Cache-Control ヘッダーの設定ガイド — max-age・no-cache・immutableの使い分け
HTTPキャッシュはWebパフォーマンスの根幹です。Cache-Controlヘッダーを適切に設定することで、ブラウザ・CDN・プロキシがレスポンスをキャッシュし、サーバー負荷の削減とユーザー体験の向上を実現できます。HTTP Cache Header Builderで各ディレクティブを組み合わせて最適な設定を見つけましょう。
max-age と s-maxage の違い
max-ageはブラウザとCDN両方に適用されるキャッシュ有効期間(秒)です。s-maxageはCDN(共有キャッシュ)にのみ適用され、max-ageを上書きします。「ブラウザは1時間、CDNは1日キャッシュしてほしい」場合はCache-Control: max-age=3600, s-maxage=86400と設定します。CDNを使わない構成ではs-maxageは無視されるため、CDNがある場合のみ有効に機能します。CloudFrontやFastlyなどのCDNはs-maxageを優先し、それ以降のリクエストはオリジンサーバーへのアクセスなしにキャッシュから応答します。APIレスポンスのCDNキャッシュ時間とブラウザのキャッシュ時間を個別に調整したい場面で有効です。
no-cache と no-store の違い
no-cacheはキャッシュを保存するが毎回オリジンに再検証する設定です(ETagやLast-Modifiedで304が返ればキャッシュを利用)。no-storeはキャッシュ自体を一切保存せずディスクにも書き込まれません。セキュリティ要件が高い認証済みページや個人情報を含むレスポンスにはno-storeを使い、動的だが帯域節約したいページにはno-cacheを使います。no-cacheとmust-revalidateの組み合わせは再検証が失敗した場合に古いキャッシュを返さないことを保証します。プライベートキャッシュのみに保存させたい場合はprivateディレクティブを追加することでCDNなど共有キャッシュに保存されないようにできます。
stale-while-revalidate でUXを向上
stale-while-revalidateを設定すると、max-age経過後でも指定秒数内はバックグラウンドで再検証しながら古いキャッシュをすぐに返します。ユーザーには常にキャッシュから即座にレスポンスが届き、バックグラウンドで更新が走るため体感速度が大幅に改善します。例:Cache-Control: max-age=60, stale-while-revalidate=300とすると、60秒後〜360秒後の間は古いキャッシュを返しつつバックグラウンドで最新データを取得します。Next.jsのrevalidateオプションやVercelのEdge Cacheもこの仕組みを活用しており、静的生成ページを定期更新するISR(Incremental Static Regeneration)と同様の考え方です。APIのリアルタイム性と応答速度のバランスを取る場面で非常に有効です。
immutable で静的アセットを最適化
CSSやJSファイルにコンテンツハッシュを付けたファイル名(app.a1b2c3.js)を使う場合、immutableを設定するとキャッシュ期限内に再検証リクエストすら送りません。Cache-Control: max-age=31536000, immutableが静的アセットの定番設定です。ファイル名が変われば新しいURLになるため古いキャッシュは使われません。