静的サイトで CSS/JS に Cache-Control: public, max-age=31536000, immutable を設定していたところ、ファイル内容を更新してもブラウザが古いキャッシュを使い続ける問題が発生した。

問題

Cloudflare Pages でホストしている静的サイトの _headers:

/style.css
  Cache-Control: public, max-age=31536000, immutable

/script.js
  Cache-Control: public, max-age=31536000, immutable

immutable はブラウザに「このリソースは絶対に変わらない」と宣言するため、ファイルを更新してデプロイしても、キャッシュ期限(1年)が切れるまでブラウザは再検証すら行わない。

症状

  • デプロイ後もUIが古い英語表示のまま
  • レイアウト変更が反映されない
  • DevTools の Network タブで (disk cache) と表示される

対処法

方法1: キャッシュバスティング(クエリパラメータ)

<!-- 更新のたびに v=2, v=3 と上げる -->
<link rel="stylesheet" href="style.css?v=2">
<script src="script.js?v=2"></script>

手動管理が必要。更新時にバージョンを上げ忘れると古いキャッシュが残る。

方法2: キャッシュ戦略の見直し(推奨)

immutable をやめて、短めの max-age + must-revalidate にする:

/style.css
  Cache-Control: public, max-age=3600, must-revalidate

/script.js
  Cache-Control: public, max-age=3600, must-revalidate

1時間でキャッシュが期限切れになり、ブラウザがサーバーに再検証を行う。Cloudflare の CDN エッジキャッシュが効くため、パフォーマンスへの影響は軽微。

方法3: ビルドツールでハッシュ付きファイル名を生成

Vite 等を導入すると、ビルド時にファイル名にハッシュが付く:

style.a1b2c3d4.css
script.e5f6g7h8.js

ファイル内容が変われば名前も変わるため、immutable と両立できる。ただし静的サイトにビルドツールを導入するオーバーヘッドがある。

immutable を使うべきケース

immutable が適切なのは ファイル名にハッシュが含まれる場合のみ:

# OK: ファイル名にハッシュがある → 内容が変われば名前も変わる
/assets/style.a1b2c3.css
  Cache-Control: public, max-age=31536000, immutable

# NG: ファイル名が固定 → 内容が変わってもURLが同じ
/style.css
  Cache-Control: public, max-age=31536000, immutable

まとめ

方法 メリット デメリット
クエリパラメータ 簡単、ツール不要 手動管理、上げ忘れリスク
短い max-age 運用が楽、自動で反映 キャッシュ効率がやや低下
ハッシュ付きファイル名 最も確実、immutable と両立 ビルドツール必要

ビルドツールのない静的サイトでは 方法2(短い max-age) が最もバランスが良い。