静的サイトのキャッシュ戦略 — immutable キャッシュの罠と対処法
静的サイトで 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) が最もバランスが良い。