HTTP キャッシュ:Cache-Control と ETag は半分ずつ担う
Cache-Control は「リクエストを送るか」を、ETag は「送った後で本文を省けるか」を決めます。混同すると片方を直しても直りません。
キャッシュの問題はほぼ二つの独立した問いに分解できます。このリクエストを送るべきか、そして送った後で本文を省けるか。前者は Cache-Control、後者は ETag と Last-Modified が担います。
前半:リクエストを送るか
Cache-Control: public, max-age=31536000, immutable
ハッシュ付きファイル名のビルド成果物はこうあるべきです。一年、immutable、リクエスト自体を送りません。
Cache-Control: no-cache
no-cache は最も誤読される指令です。キャッシュしないという意味ではありません。「キャッシュしてよいが、使うたびに検証が必要」です。保存自体を禁じるのは no-store です。
| 指令 | 保存するか | リクエストを送るか |
|---|---|---|
max-age=0 |
する | 送る(検証あり) |
no-cache |
する | 送る(検証あり) |
no-store |
しない | 送る(書き込みなし) |
immutable |
する | 有効期間内は送らない |
後半:本文を省けるか
サーバが ETag: "abc123" を返し、次回ブラウザが付けます。
If-None-Match: "abc123"
変化が無ければ 304 Not Modified で本文はありません。節約できるのは転送量で、往復は変わりません。リクエストは依然として発生します。
etag on;
組み合わせ
| 目的 | Cache-Control | 検証子 |
|---|---|---|
| ハッシュ付き静的資産 | max-age=31536000, immutable |
不要 |
| HTML ページ | no-cache |
ETag |
| キャッシュ可能な API | max-age=60, must-revalidate |
ETag |
| 機微なデータ | no-store |
無意味 |
HTML は通常 no-cache と ETag の組み合わせです。変更は即座に反映され、変化が無ければ 304 一つで文書全体を省けます。
つまずきやすい点
ETag の生成方法を変えると全キャッシュが一度無効になります。 mtime 由来なら、内容が同じでも再デプロイで全クライアントが再取得します。内容ハッシュの方が安定します。
CDN はヘッダを書き換えたり飲み込んだりします。 オリジンが max-age=60 でも、CDN が独自規則でより長く保持することがあります。「更新されない」を調べるときは、オリジンより先に CDN の状態ヘッダ(eo-cache-status、cf-cache-status)を見てください。
Cache-Control は問い合わせるかを、ETag は答えるかを決めます。一緒に語ると永遠に直りません。

コメント
…