📑

Laravel のリダイレクトが意図せず発火し続けた原因と、Docker+ブラウザキャッシュの落とし穴

に公開

📌 はじめに

Laravel で「過去の西暦なら来年ページにリダイレクトする」というシンプルな処理を実装していたところ、
想定していない /2025 → /2026 リダイレクトが永久に発生するという問題に遭遇しました。

調査の結果、

  • ルートでも
  • コントローラの中でも
  • dd() の値はすべて /2026

にもかかわらず、

実際の原因はブラウザの 301 リダイレクトキャッシュという落とし穴

でした。

この記事では、

  • 実装内容・方針
  • バグ発生内容
  • 調査内容・方針
  • 解決手順・原因
  • 再発防止策は何か

を整理します。


🧪 発生した現象

以下のような実装をしていました。

$year = (int) $lastSegment;
$currentYear = now()->year;      // 2025
$nextYear = now()->addYear()->year; // 2026

if ($year < $currentYear) {
    return redirect('/' . $nextYear, 301);
}

意図としては、

  • /2023/2026 へリダイレクト
  • /2024/2026 へリダイレクト
  • /2025リダイレクトさせない(通常表示)

ところが実際には……

/2025 にアクセスしても必ず /2026 に飛んでしまう

という状態になりました。


🔍 調査ログ(重要ポイント)

異常が起きているのを確認してから、次のように切り分けを実施しました。

✔ ① ルート定義で dd()

dd() の値はすでに /2026

✔ ② コントローラで dd()

やはり /2026 が入っている

つまり、

Laravel に届いた時点で既に /2026 になっている

という状態でした。

この時点で、

  • PHPのロジックではない
  • Laravelのルートでもない
  • 控えめに言って挙動がおかしい

ということが確定。


🧠 問題の本質:

ブラウザの 301 永久リダイレクトキャッシュが Laravel より先に動いていた

結論としてはこれです。

調査の過程で一度だけ、

return redirect('/2026', 301);

つまり 301 Permanent Redirect(永久リダイレクト) を返したタイミングがありました。

Chrome などのブラウザは、301 を受け取るとこう判断します:

「/2025 は永久に /2026 に移動した」と記憶する

すると、

  • コードを直しても
  • Laravel が正しい判定をしていても
  • Docker を再起動しても
  • ルートキャッシュを消しても

ブラウザ自身が /2025 を /2026 に書き換えてしまい、
Laravel にリクエストが届かなくなる
のです。

これが、

✔ ルートで dd() → /2026
✔ コントローラで dd() → /2026

の原因でした。


🩺 今回の現象はこう流れていた

[ブラウザ]
    ↓ (/2025 のリクエストを送信)
[ブラウザ内部の301キャッシュ] 
    「/2025 は永久に /2026!覚えてる!」
    ↓ (勝手に書き換え)
[サーバへ送信されるのは /2026]
    ↓
[Laravel(正しいロジック含む)]
    dd() → 当然 /2026

つまり今回、

/2025 のリクエストは一度も Laravel に届いていなかった。

だから、どれだけロジックが正しくても、
dd() が見ていたのは「すでに書き換えられた後のURL」でした。


🛠 解決方法

最終的に次の手順で解決しました。

✔ Docker コンテナ内で Laravel キャッシュを削除

docker exec -it <コンテナID> php artisan cache:clear
docker exec -it <コンテナID> php artisan config:clear
docker exec -it <コンテナID> php artisan route:clear
docker exec -it <コンテナID> php artisan view:clear

※ Docker 環境では、ホスト側で artisan を叩いても意味がない場合が多い点に注意。

✔ ブラウザを完全終了 → 再起動

(またはシークレットモードでアクセス)

これにより、

  • Laravel 側キャッシュ → 消える
  • ブラウザ側の 301 キャッシュ → リセットされる

本来の挙動に戻った


🔥 再発防止策(超重要)

1. デバッグ時に 301 を使わない

302(Temporary Redirect)を使うこと。

return redirect('/'.$nextYear, 302); // デバッグ中はこちら

2. 挙動がおかしい時は Network タブを見る

  • 301 か?
  • 302 か?
  • Location は?

これを見るだけで原因が速攻でわかる。

3. Docker のキャッシュはコンテナ内で消すこと

ホスト側の artisan は無効なことも多い。


🎯 まとめ

今回のケースは、

ロジックは100%正しいのに挙動がおかしい
→ 実は Laravel の外側が原因だった

という、実務ではよくある“キャッシュの罠”でした。

ポイントは:

  • 301 は強烈にブラウザにキャッシュされる
  • 一度誤った 301 を返すと、修正しても Laravel に届かなくなる
  • Laravel のキャッシュも Docker 内でクリアしないと意味がない
  • dd() が正しく見えても、それは「ブラウザに書き換えられた後の世界」だった

この経験は、皆様の業務でも役立ちますと幸いです💪✨

Discussion