静的サイトのキャッシュヘッダー:ハッシュ付きは永久、HTML は毎回確認

キャッシュ方針を決めるのはファイル名が内容で変わるかどうかで、サイズではありません。HTML を長くキャッシュすると、削除済みチャンクを指す古い入口を配ることになります。

静的サイトを公開して最初に出る問題は速度ではありません。デプロイ直後の真っ白なページです。原因はほぼ同じで、HTML がキャッシュされ、そこが参照するアセットのファイル名は変わっている、というものです。

判断は 1 つの問いだけ

そのファイル名は内容が変われば変わるか。変わるなら永久にキャッシュしてよく、変わらないなら毎回確認する必要があります。サイズやアクセス頻度は関係ありません。

ビルド成果物は 3 種類に分かれます。

パス ファイル名 方針
/_astro/* 内容ハッシュ付き max-age=31536000, immutable
*.html 固定 no-cache
/pagefind/* 混在 max-age=3600

HTML を長くキャッシュできない理由

ビルドは原子的です。npm run build は新しいハッシュ名を書き出し、古いものを消します。index.html は唯一の入口で、これを 1 年キャッシュされると、利用者が開くのは前回デプロイの HTML です。そこに書かれた /_astro/index.abc123.js は新しいデプロイには存在しません。構造と CSS は出ますが、スクリプトは 404 で、操作はすべて死にます。

no-cache は「キャッシュしない」ではなく「毎回 ETag 付きで聞き直す」です。ヒットすれば本文なしの 304 が返り、コストは往復 1 回、対価は削除済みハッシュを参照しないことです。

location ~* ^/_astro/ {
  add_header Cache-Control "public, max-age=31536000, immutable";
}

location ~* \.html$ {
  add_header Cache-Control "no-cache";
}

見落としやすい 2 点

  • Pagefind の索引ディレクトリ:pagefind.js にハッシュはなく、分割ファイルにはあります。まとめて 1 時間にしておくと、再ビルドに追随しつつ、検索のたびに数 MB を取り直すこともありません。
  • sw.js や manifest のような慣例的な入口:このサイトは PWA を作らないので存在しません。もし作るなら HTML と同じ扱いです。ファイル名が固定なら長くキャッシュしてはいけません。

ファイルが大きいかではなく、内容を変えたらファイル名が変わるかを問う。

← 記事一覧に戻る

コメント

…