Cloudflare PagesでリロードするとNuxtページが404になる問題の調査と解決
問題の症状
Cloudflare Pagesにデプロイした動的ルートのNuxtページで以下の問題が発生:
- 初回アクセス: ページが正常に表示される
- F5でリロード:
ERR_HTTP_RESPONSE_CODE_FAILURE 404 (Not Found)エラーが発生
例: https://log.eurekapu.com/2025-11-27/domain-transfer-onamae-to-cloudflare
ブラウザのコンソールには以下のエラーが表示:
GET https://log.eurekapu.com/2025-11-27/domain-transfer-onamae-to-cloudflare net::ERR_HTTP_RESPONSE_CODE_FAILURE 404 (Not Found)
原因の調査プロセス
当初の仮説(誤り)
Cloudflare PagesでNuxt 3のSSR(Server Side Rendering)アプリケーションを動作させる際のルーティング設定不足が原因と、最初は考えました。
なぜ初回は成功してリロードで失敗するのか?
- 初回アクセス(クライアントサイドナビゲーション):
- Nuxtアプリがクライアントサイドで動作
- Vue Routerがクライアント側でルーティングを処理
- サーバーへのリクエストなしでページが表示される
- リロード時(サーバーサイドリクエスト):
- ブラウザがサーバーに直接HTTPリクエストを送信
- Cloudflare Pagesが適切にNitro関数にルーティングできていない
_routes.jsonの設定不足により404が返される
この仮説に基づき、nuxt.config.tsに以下の設定を追加しましたが、問題は解決しませんでした:
nitro: {
preset: "cloudflare-pages",
prerender: {
autoSubfolderIndex: false,
crawlLinks: true,
failOnError: false
}
},
routeRules: {
'/**': { ssr: true }
}
なぜ当初の仮説が誤りだったのか
Nuxt 3のcloudflare-pages presetはデフォルトでSSRモードで動作します。つまり:
nuxt.config.tsの設定変更は不要だったrouteRulesを明示的に設定しなくても、すべてのルートは既にSSRとして扱われている- 問題の本質は、Nuxt/Nitroの設定ではなく、ビルド後の
_routes.jsonファイルの内容だった
実際の原因(_routes.jsonの誤った除外ルール)
ビルド出力ディレクトリ(dist/)の_routes.jsonを確認したところ、以下の設定が見つかりました:
{
"version": 1,
"include": ["/*"],
"exclude": [
"/_nuxt/*",
"/dump.pages.sql",
"/favicon.ico",
"/robots.txt",
"/data/*",
"/excel/*",
"/images/*",
"/__nuxt_content/*",
"/20*/*" // ← これが問題!
]
}
/20*/*パターンがコンテンツページを除外していました。
この除外ルールはscripts/fix-routes.mjsで定義されており、画像ファイル(/2025-11-27/image.pngなど)を静的ファイルとして扱う意図でしたが、コンテンツページ(/2025-11-27/domain-transfer-onamae-to-cloudflare)も除外してしまっていました。
結果:
- コンテンツページへのリクエストが
_worker.js(SSR関数)にルーティングされない - 対応する静的HTMLファイルも存在しない
- 404エラーが発生
解決方法(検討中)
scripts/fix-routes.mjsから/20*/*の除外ルールを削除:
修正前:
const routes = {
version: 1,
include: ["/*"],
exclude: [
"/_nuxt/*",
"/dump.pages.sql",
"/favicon.ico",
"/robots.txt",
"/data/*",
"/excel/*",
"/images/*",
"/__nuxt_content/*",
"/20*/*" // ← 削除
]
};
修正後:
const routes = {
version: 1,
include: ["/*"],
exclude: [
"/_nuxt/*",
"/dump.pages.sql",
"/favicon.ico",
"/robots.txt",
"/data/*",
"/excel/*",
"/images/*",
"/__nuxt_content/*"
// /20*/* を削除
]
};
なぜこの修正で解決するのか
- 画像ファイルは既に静的ファイルとして処理されている
nuxt.config.tsのhooks['nitro:build:public-assets']で、コンテンツディレクトリの画像をpublic/ディレクトリにコピー済み- 画像ファイル(例:
/2025-11-27/image.png)は既に静的ファイルとしてアクセス可能 /20*/*の除外ルールは不要
- コンテンツページはSSR関数で処理する必要がある
- コンテンツページ(例:
/2025-11-27/domain-transfer-onamae-to-cloudflare)は動的ルート _worker.js(Nitro関数)でサーバーサイドレンダリングする必要がある_routes.jsonのexcludeリストから除外すれば、_worker.jsにルーティングされる
- コンテンツページ(例:
デプロイ手順
修正後、以下のコマンドで再ビルド・デプロイ:
cd apps/web
# ビルド(postbuildフックでfix-routes.mjsが自動実行される)
pnpm build
# Cloudflare Pagesにデプロイ
pnpm exec wrangler pages deploy
ビルド時の確認
ビルド完了後、dist/_routes.jsonを確認し、/20*/*が除外リストから削除されていることを確認:
# 確認コマンド(Windows)
cat apps/web/dist/_routes.json
# 確認コマンド(Linux/Mac)
cat apps/web/dist/_routes.json
期待される出力:
{
"version": 1,
"include": ["/*"],
"exclude": [
"/_nuxt/*",
"/dump.pages.sql",
"/favicon.ico",
"/robots.txt",
"/data/*",
"/excel/*",
"/images/*",
"/__nuxt_content/*"
]
}
確認方法
デプロイ完了後:
- ブラウザで動的ルートにアクセス(例:
https://log.eurekapu.com/2025-11-27/domain-transfer-onamae-to-cloudflare) - F5キーでページをリロード
- 404エラーが発生せず、ページが正常に表示されることを確認
検証結果
✅ 問題は解決しました(2025-11-27)
scripts/fix-routes.mjsから/20*/*の除外ルールを削除し、再ビルド・デプロイした結果:
- 初回アクセス: ✅ 正常に表示
- F5でリロード: ✅ 正常に表示(404エラーなし)
結論
問題の原因は_routes.jsonの/20*/*除外ルールでした。このルールにより:
- コンテンツページ(
/2025-11-27/domain-transfer-onamae-to-cloudflare)が_worker.jsにルーティングされず - 対応する静的HTMLファイルも存在しないため404エラーが発生
除外ルールを削除することで、すべてのコンテンツページが正しくSSR関数(_worker.js)でレンダリングされるようになり、リロード時も正常に動作するようになりました。
関連情報
トラブルシューティング
この修正で問題が解決しない場合
_routes.jsonの内容を確認cat apps/web/dist/_routes.json/20*/*が本当に削除されているか確認postbuildスクリプトが正しく実行されているか確認
- Cloudflare Pagesのデプロイログを確認
- デプロイ時にエラーが発生していないか
_worker.jsが正しくデプロイされているか
- Cloudflare Pagesのキャッシュをクリア
- Cloudflare Pagesダッシュボードからキャッシュをパージ
- ローカルでプレビュー
pnpm preview- ローカル環境で本番ビルドをテストし、問題を再現できるか確認
デバッグのヒント
問題が発生している場合、以下を確認:
- ブラウザの開発者ツール(Network タブ): 実際にどのリクエストが送信され、どのレスポンスが返されているか
- Cloudflare Pagesのファンクションログ:
_worker.jsが実行されているか、エラーが発生していないか wrangler pages devでローカルテスト: 本番環境と同じ挙動を再現できるか
重要な学び
_routes.jsonがCloudflare Pagesのルーティングを制御しているinclude: SSR関数でレンダリングするパスexclude: 静的ファイルとして配信するパス
- ビルド後のファイルを確認する重要性: 設定ファイル(
nuxt.config.ts)だけでなく、実際のビルド出力(dist/_routes.json)を確認することで、問題の根本原因を特定できる