#はじめに
「キャッシュを長く設定したいけど、更新したときに古い内容が表示されるのは困る」——これはWeb開発者の共通の悩みです。
キャッシュバスティングは、この問題を解決するための戦略です。ファイルの内容が変わったときに、確実に新しいバージョンを取得させる仕組みを作ります。
この記事を読むと、以下のことができるようになります:
- キャッシュバスティングの必要性を理解できる
- ファイル名ハッシュの仕組みがわかる
- ビルドツールとの連携方法がわかる
#キャッシュのジレンマ
#短いキャッシュ期間
Cache-Control: max-age=60
- ✅ 更新がすぐ反映される
- ❌ 毎分サーバーに確認が必要
- ❌ パフォーマンスが悪い
#長いキャッシュ期間
Cache-Control: max-age=31536000
- ✅ 高速(1年間キャッシュ)
- ❌ 更新しても古い内容が表示される
- ❌ ユーザーに手動でキャッシュクリアを依頼?
#解決策: キャッシュバスティング
内容が変わったらURLも変えるという発想です。
# v1
/assets/app.a1b2c3.js
# v2(内容が変わった)
/assets/app.d4e5f6.js ← URLが変わるので、新しくダウンロード
#ファイル名ハッシュ
ファイル名にコンテンツのハッシュ値を含める方法です。
#仕組み
ファイル内容: console.log("Hello World");
↓ ハッシュ計算
ハッシュ値: a1b2c3d4
↓ ファイル名に付与
app.a1b2c3d4.js
内容が1文字でも変わると、ハッシュ値が完全に変わります。
#メリット
- 内容が同じならURLも同じ(キャッシュが効く)
- 内容が違えばURLも違う(新しいファイルを取得)
- 長いキャッシュ期間を設定できる
#Cache-Controlの設定
Cache-Control: max-age=31536000, immutable
1年間キャッシュ。immutableにより、リロード時も再検証をスキップ。
#HTMLファイルの扱い
重要: HTMLファイル自体はハッシュを含めません。
# これはダメ
index.a1b2c3.html ← URLが変わってしまう
# これが正解
index.html ← 固定URL
└→ app.a1b2c3.js を参照
HTMLファイルは常に最新を取得し、その中で参照するアセットのURLにハッシュを含めます。
Cache-Control: no-cache
#全体の構成
index.html (no-cache, 常に最新を確認)
├── /assets/app.a1b2c3.js (max-age=1年)
├── /assets/style.d4e5f6.css (max-age=1年)
└── /assets/logo.g7h8i9.png (max-age=1年)
#ビルドツールとの連携
#webpack
// webpack.config.js
module.exports = {
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].js',
},
};
生成されるファイル:
main.a1b2c3d4e5f6g7h8.js
vendor.i9j0k1l2m3n4o5p6.js
#Vite
// vite.config.js
export default {
build: {
rollupOptions: {
output: {
entryFileNames: 'assets/[name].[hash].js',
chunkFileNames: 'assets/[name].[hash].js',
assetFileNames: 'assets/[name].[hash].[ext]',
},
},
},
};
#Next.js
デフォルトでファイル名ハッシュが有効です。
/_next/static/chunks/pages/index-a1b2c3.js
#クエリパラメータ方式
URLは変えず、クエリパラメータでバージョンを指定する方法です。
<script src="/app.js?v=1.2.3"></script>
<script src="/app.js?v=1.2.4"></script> <!-- バージョンアップ -->
#メリット
- 既存のファイル名を変えなくて済む
- シンプルに実装できる
#デメリット
- 一部のCDNはクエリパラメータを無視する設定になっている場合がある
- キャッシュキーの扱いがCDNによって異なる
#使用が適切な場面
- ファイル名ハッシュを導入できない古いシステム
- 手動でバージョン管理する小規模サイト
#マニフェストファイル
ビルドツールは「どのファイルがどのハッシュ付きファイル名になったか」を記録したマニフェストを生成します。
// manifest.json
{
"main.js": "main.a1b2c3.js",
"style.css": "style.d4e5f6.css"
}
サーバーサイドレンダリングやテンプレートエンジンで、このマニフェストを参照してHTMLを生成します。
<script src="<?= $manifest['main.js'] ?>"></script>
#実践的な設定例
#静的サイト(nginx)
# HTMLファイル
location ~* \.html$ {
add_header Cache-Control "no-cache";
}
# ハッシュ付きアセット
location ~* \.[a-f0-9]{8,}\.(js|css|png|jpg|svg|woff2)$ {
add_header Cache-Control "max-age=31536000, immutable";
}
# ハッシュなしアセット(フォールバック)
location ~* \.(js|css|png|jpg|svg|woff2)$ {
add_header Cache-Control "max-age=86400";
}
#CDN設定(Cloudflare)
Page Rulesまたは Transform Rulesで:
*.html→Cache-Control: no-cache/assets/*→Cache-Control: max-age=31536000, immutable
#チャンク分割との組み合わせ
大きなJavaScriptファイルを複数のチャンクに分割することで、変更のない部分はキャッシュを活用できます。
# 変更前
main.a1b2c3.js (全体)
# チャンク分割後
vendor.x1y2z3.js (ライブラリ、ほぼ変更なし) → キャッシュ継続
main.a1b2c3.js (アプリコード) → 変更があれば新しいハッシュ
ライブラリを更新しない限り、vendor.jsのキャッシュは有効なまま。
// webpack
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
},
},
},
}
#デバッグとトラブルシューティング
#「更新したのに反映されない」場合
- ブラウザキャッシュを確認: ハードリロード(Ctrl+Shift+R)
- CDNキャッシュを確認: パージを実行
- HTMLファイルのキャッシュを確認:
no-cacheが設定されているか - ビルドが成功しているか確認: 新しいハッシュが生成されているか
#DevToolsでの確認
Networkタブで:
- ファイル名にハッシュが含まれているか
- Cache-Controlヘッダーが正しく設定されているか
- Status(200 vs 304 vs from cache)
#まとめ
- キャッシュバスティングは「内容が変わったらURLも変える」戦略
- ファイル名ハッシュが最も確実な方法
- HTMLはno-cache、アセットはmax-age=1年+immutable
- ビルドツール(webpack、Vite等)が自動でハッシュを生成
- マニフェストファイルで元のファイル名との対応を管理
- チャンク分割でさらに効率的なキャッシュ活用
#次のステップ
キャッシュバスティングを理解したところで、次はService Workerとキャッシュについて学びましょう。Service Workerを使うと、オフライン対応や、より柔軟なキャッシュ戦略を実装できます。