Cloudflare PagesでNext.jsの静的サイトを無料公開してみる
Next.jsで作った静的サイト「戦略の系譜」をCloudflare Pagesへデプロイし、ローカル確認、ビルド、wrangler設定、Productionデプロイ、独自ドメイン公開までを確認した実験ログ。
はじめに
個人開発を始めると、最初に地味に詰まるのが公開環境です。
アプリ本体を作る前に、LPを置きたい。技術ブログを公開したい。小さなメディアサイトを試したい。そう思っても、いざ公開しようとすると、ホスティング、独自ドメイン、SSL、ビルド設定、環境変数、デプロイ方法など、細かい判断が一気に出てきます。
VercelやNetlifyを使えばすぐに公開できます。ただ、Cloudflare Pagesもかなり強い選択肢です。静的サイトであれば、無料枠で公開しやすく、独自ドメインも扱いやすく、CloudflareのCDN上で配信できます。
今回は、実際に自分で公開したサイトを題材にして、Cloudflare Pagesで静的サイトを公開する流れを確認します。
対象にするのは、次のサイトです。
https://bukei-wisdom.com/ 戦略の系譜 歴史的英雄たちの戦略決断を古典の教えで分析し、現代への応用を探る知恵の宝庫 https://bukei-wisdom.com/
「戦略の系譜」という、東洋古典や兵法書を扱う静的サイトです。実装はNext.jsで、Cloudflare Pagesへデプロイしています。
今回公開したもの
今回公開したのは、Next.jsで作った静的サイトです。
Browser
↓
Cloudflare Pages
↓
Next.js static export
対象プロジェクトは、手元では「戦略の系譜」プロジェクトの app 配下にあります。
bukei_shichisyo/
アプリ本体は app 配下です。
bukei_shichisyo/
app/
package.json
wrangler.toml
out/
今回確認したことは、次の6つです。
- ローカルでNext.jsサイトを表示できるか
- Next.jsの静的ビルドが通るか
- sitemapが生成されるか
- Cloudflare Pages向けの出力先が
outになっているか - Cloudflare PagesのProductionデプロイが成功しているか
- 独自ドメイン
bukei-wisdom.comで表示できるか
最初から結論を書くと、Cloudflare PagesでNext.jsの静的サイトを公開する流れは成立しました。
ただし、Cloudflare Dashboard上でBuild commandやRoot directoryが常に分かりやすく見えるわけではありませんでした。今回はWrangler CLIと wrangler.toml の設定を根拠に、デプロイ構成を確認しています。
なぜCloudflare Pagesを使うのか
静的サイトの公開先としては、Vercel、Netlify、GitHub Pagesなど、いくつか選択肢があります。
その中でCloudflare Pagesを使う理由は、Cloudflareの他サービスへ広げやすいからです。
最初は静的サイトだけで十分です。しかし、個人開発を続けていると、問い合わせフォーム、画像配信、API、認証、Bot対策、AI機能などを後から足したくなります。
Cloudflare Pagesを入口にしておくと、Workers、R2、KV、D1、Turnstile、AI Gatewayなどに広げやすいです。最初から全部使う必要はありませんが、あとから拡張できる余地があるのは安心です。
今回の「戦略の系譜」も、まずは静的サイトとして公開するところに絞りました。DBや認証は扱わず、公開環境としてCloudflare Pagesが使えるかを確認します。
ローカルで確認する
まず、手元でNext.jsサイトを表示します。
cd bukei_shichisyo/app
npm install
npm run dev
ローカルでは localhost:3000 で表示しました。

ここで確認したのは、サイトのデザインそのものではありません。Cloudflare Pagesへ出す前に、少なくともローカルでトップページが表示でき、主要なナビゲーションやカードが崩れていないことを確認しました。
公開環境の問題を調べる前に、ローカルで成立している状態を残しておくと、後で切り分けがしやすくなります。
ビルドする
次に、本番向けにビルドします。
npm run build
このプロジェクトでは、build 後に next-sitemap が走るようになっています。
package.json では、次のようなscriptになっています。
{
"scripts": {
"build": "next build",
"postbuild": "next-sitemap"
}
}
実際にビルドすると、next-sitemap の生成完了まで確認できました。

スクリーンショットでは、https://bukei-wisdom.com/sitemap.xml が生成対象として表示されています。
ここは地味ですが重要です。静的サイトを公開するだけなら、HTMLが表示されれば最低限は成立します。しかし、検索流入を考えるなら、sitemapまで生成されているかは確認しておきたいところです。
Cloudflare Pages向けの設定
Cloudflare Pages向けの設定は、app/wrangler.toml にあります。
name = "bukei-wisdom"
compatibility_date = "2024-01-15"
pages_build_output_dir = "out"
compatibility_flags = ["nodejs_compat"]
[env.production]
name = "bukei-wisdom"
compatibility_flags = ["nodejs_compat"]
[env.preview]
name = "bukei-wisdom-preview"
compatibility_flags = ["nodejs_compat"]
実際の設定画面は次の通りです。

ここで特に見るべきなのは、次の2つです。
name = "bukei-wisdom"
pages_build_output_dir = "out"
name はCloudflare Pagesのプロジェクト名です。pages_build_output_dir は、Cloudflare Pagesへデプロイする静的ファイルの出力先です。
今回のプロジェクトでは out を使っています。Astroであれば dist がよく出ますが、Next.jsの静的出力では構成によって out を使います。ここを間違えると、ビルドは通っているのにCloudflare Pages側で配信対象が見つからない、という状態になります。
デプロイしてみる
Cloudflare Pages側には、bukei-wisdom というプロジェクトとして作成されています。

今回の運用では、Cloudflare Dashboard上のBuild commandやRoot directoryの詳細値は確認できませんでした。Git連携のビルド設定画面というより、Wrangler CLI中心のPagesデプロイ運用になっているためです。
そのため、記事ではDashboardのBuild settingsではなく、次の2つを根拠にします。
wrangler.tomlのpages_build_output_dir = "out"package.jsonのCloudflare Pages向けscript
package.json には、Cloudflare Pages向けに次のscriptがあります。
{
"scripts": {
"pages:build": "npx @cloudflare/next-on-pages",
"pages:deploy": "npm run pages:build && wrangler pages deploy",
"deploy:production": "npm run build && npm run pages:build && wrangler pages deploy"
}
}
デプロイは、基本的には次の流れです。
npm run build
npm run pages:build
wrangler pages deploy
または、scriptにまとめたコマンドを使います。
npm run deploy:production
Cloudflare PagesのProduction deploymentは成功しており、bukei-wisdom.com と bukei-wisdom.pages.dev が紐づいています。

この画面では、Production環境、mainブランチ、デプロイ成功status、公開ドメインを確認できます。
独自ドメインで表示する
最後に、独自ドメインで表示できるかを確認します。
今回の本番URLは次の通りです。
https://bukei-wisdom.com/
実際にアクセスすると、Cloudflare Pages経由で「戦略の系譜」のトップページが表示されました。

PagesのProduction deployment画面でも、次の2つのドメインが確認できます。
bukei-wisdom.com
bukei-wisdom.pages.dev
Cloudflare Pagesでは、pages.dev のURLだけでも公開できます。しかし、検索やSNS共有、読者への見え方を考えると、独自ドメインを設定した方がサイトとして扱いやすくなります。
今回も、最終的には bukei-wisdom.com を主URLとして使う前提にしています。
実際に詰まりやすいところ
実際に確認して、Cloudflare Pagesで静的サイトを出すときに注意すべき点は3つありました。
1つ目は、出力ディレクトリです。
Astroなら dist、Next.jsの静的出力なら out のように、フレームワークや構成によって配信対象のディレクトリが変わります。今回の設定では、wrangler.toml の pages_build_output_dir = "out" が重要でした。
2つ目は、Cloudflare Dashboardで見える情報と、実際のデプロイ運用が一致しないことです。
Git連携のBuild settingsを使っている場合は、Build commandやRoot directoryがDashboardで見やすいです。一方で、Wrangler CLI中心でデプロイしている場合は、Dashboardだけを見てもすべての設定が分かるとは限りません。今回は、wrangler.toml と package.json のscriptを見て、デプロイ構成を確認しました。
3つ目は、sitemapです。
サイトが表示されるだけなら、sitemapは必須ではありません。しかし、検索流入を期待するなら、sitemap.xml が生成されているかを確認した方がよいです。今回のビルドログでは、next-sitemap によって https://bukei-wisdom.com/sitemap.xml が生成対象になっていることを確認できました。
この記事で分かったこと
Cloudflare Pagesは、Next.jsの静的サイトを公開する先として十分使えます。
今回の実例では、ローカル表示、Next.jsビルド、sitemap生成、Wrangler設定、Productionデプロイ、独自ドメイン表示まで確認できました。
特に、wrangler.toml に pages_build_output_dir = "out" を明示しておくと、Cloudflare Pagesへ何を配信するのかが分かりやすくなります。Dashboardだけで設定を追おうとすると見えない部分もあるため、リポジトリ内に設定を残しておくことは重要です。
個人開発では、サイトを作ることよりも、公開して継続的に直せる状態を作ることが大事です。
Cloudflare Pagesで一度公開まで通しておくと、その後にWorkers、R2、D1、Turnstile、AI Gatewayなどへ広げる足場になります。今回の「戦略の系譜」は静的サイトとして公開しましたが、将来的にフォーム、分析、AI機能を足す場合にも、Cloudflare上に置いてあることは拡張しやすさにつながります。
ぼやきとしては、公開記事に「あとでスクショを撮る」と書いたまま出してしまうのはよくないです。検証記事は、やはり動かした証跡まで揃えてから出した方が強いです。