Cloud Infrastructure

【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つが組み合わさった結果、以下のループが発生していました:

  1. ユーザーが `/play` にアクセスする。
  2. `_redirects` に従い、Cloudflareが内部的に `/play.html` を表示しようとする。
  3. Cloudflareの「Clean URLs」が働き、「`.html` がついているから `/play` に308リダイレクトせよ」と返答する。
  4. 結果として `/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レスポンスヘッダーとステータスコードを事前チェックすることが肝要です。