【SPA×Cloudflare Pages】Clean URLs と 308リダイレクト無限ループの罠と解決策
Cloudflare Pagesにフロントエンドをデプロイした際、`Clean URLs`機能と`_redirects`のルール定義が競合して発生した308 Permanent Redirect不具合の再現と修正手順を解説します。
1. 遭遇した現象:`/play` へのアクセスが308エラーでループする
Doodle Fighterのゲーム画面は `frontend/play.html` で提供されていますが、綺麗なURL(`https://doodle-fighter.com/play`)でアクセスできるよう、静的ホスティングの設定を行っていました。
しかし、Cloudflare Pages上にデプロイした直後、`/play` にアクセスするとブラウザのリダイレクト上限エラー(`TOO_MANY_REDIRECTS`)が発生したり、HTTPステータス `308 Permanent Redirect` が繰り返される事象に直面しました。
2. 原因の特定:Clean URLs と `_redirects` の二重書き換え
Cloudflare Pagesには、`.html` 拡張子を自動的に除去して `/page.html` を `/page` にリダイレクトする **「Clean URLs」** というデフォルト機能が備わっています。
一方、プロジェクト直下に以下のような `_redirects` ファイルを配置していました:
# 修正前の _redirects
/play /play.html 200
この2つが組み合わさった結果、以下のループが発生していました:
- ユーザーが `/play` にアクセスする。
- `_redirects` に従い、Cloudflareが内部的に `/play.html` を表示しようとする。
- Cloudflareの「Clean URLs」が働き、「`.html` がついているから `/play` に308リダイレクトせよ」と返答する。
- 結果として `/play` ↔ `/play.html` のリダイレクト合戦が勃発。
3. 解決策:`_redirects` のターゲット指定を修正する
Cloudflare Pagesの仕様として、Clean URLsが有効な場合、rewriteの宛先も `.html` 抜きのパスとして指定するのが正しい挙動です。
実際の修正コミット(_redirects)
# frontend/_redirects の修正
# 旧設定(308ループを引き起こす)
# /play /play.html 200
# 新設定(Clean URLsと共存可能)
/play /play 200
/characters/* /play 200
4. クローラーおよびAdSense審査への影響
この308リダイレクトループは、一般のユーザーがプレイできないだけでなく、**Google AdSenseクローラーがサイトを巡回する際にも「リダイレクトエラー」としてブロックされる原因**になっていました。
`_redirects` の宛先を `/play` に書き換えることで、HTTPステータス 200 OK で正しくコンテンツが応答されるようになり、クローラーが正常に画面を読み込めるようになりました。
5. まとめ
Cloudflare PagesやVercel、NetlifyなどのモダンなJAMstackホスティングでは、プラットフォーム固有の自動URL整形ルール(Clean URLs / Trailing Slash handling)と、リポジトリ内の書き換えルールの干渉に配慮する必要があります。開発・デプロイ時には必ず `curl -I` などでHTTPレスポンスヘッダーとステータスコードを事前チェックすることが肝要です。