Cache-Control: max-age=3, must-revalidate という設定について調べたので、忘れないうちに整理しておく。
この設定は「ごく短い間(3秒)だけキャッシュを利用し、期限切れ後は厳密にサーバーへ確認を行う」という挙動になる。一般的にマイクロキャッシングと呼ばれる手法で、リアルタイム性を維持しつつ、突発的なアクセス集中からサーバーを守るためによく使われるらしい。
各ディレクティブの意味
まず、それぞれのディレクティブが何を意味しているか確認しておく。
max-age=3
ブラウザやCDNなどのキャッシュサーバーに対し、**「このコンテンツは3秒間だけ新鮮である」**と伝える。この3秒間は、オリジンサーバーへのリクエストは発生せず、キャッシュから即座に返される。
must-revalidate
キャッシュの期限(3秒)が切れた後、**「オリジンサーバーへ確認(再検証)を行い、許可が出るまでは絶対に古いキャッシュを返してはいけない」**と強制する。
これがない場合、一部のキャッシュサーバーや設定によっては、オリジンサーバーがダウンしている時などに「とりあえず古いキャッシュを返す(stale content)」という挙動をとることがある。must-revalidateはそれを明確に禁止するためのものだ。
時系列での動作フロー
ユーザーがページにアクセスした時の動きをシミュレーションしてみる。頭の中で追いかけると理解しやすい。
| タイミング | ユーザーの行動 | 挙動 | ステータスコード |
|---|---|---|---|
| 初回 | アクセス | オリジンサーバーからデータを取得して表示 | 200 OK |
| 直後〜3秒以内 | 再読み込み / 遷移 | オリジンサーバーへの通信は発生しない。ブラウザ(またはCDN)のキャッシュを表示 | 200 (from cache) |
| 3秒経過後 | 再読み込み / 遷移 | キャッシュが「期限切れ」と判断される。ブラウザはオリジンサーバーへ条件付きリクエストを送る | 通信発生 |
| → 変更なし | — | サーバーは「変更なし」と回答。古いキャッシュを再び3秒間有効化。コンテンツのダウンロードは発生しないため軽量 | 304 Not Modified |
| → 変更あり | — | サーバーは新しいコンテンツを返す | 200 OK |
ポイントは、3秒経過後に304 Not Modifiedが返るケースだ。コンテンツ本体のダウンロードが発生しないので、通信量もレイテンシも最小限で済む。ここが効いてくる。
この設定のメリット
主に**「リアルタイム性が重要だが、アクセス数が非常に多いサイト」**で絶大な効果を発揮する。覚えておきたい3点。
サーバー負荷の劇的な低減
例えば、秒間1,000リクエストが来るサイトの場合、max-age=3があれば、理論上オリジンサーバーへのリクエストは「3秒に1回」だけで済む。残りの2,999回はキャッシュで捌ける(CDN併用時)。
たった3秒のキャッシュでも、高トラフィック環境では圧倒的な差になる。
ほぼリアルタイムな更新
最長でも3秒待てば新しい情報が反映される。「完全なリアルタイム」ではないが、多くのユースケースで十分許容できる範囲だ。
古い情報の表示防止
must-revalidateにより、期限切れ後にサーバー確認なしで古い情報が表示されるリスクを排除できる。情報の正確性が重要な場面では必須の設定だ。
具体的なユースケース
「どんな場面で使えるか」を自分なりに考えてみた。
向いているケース
- ニュースサイトのトップページ・一覧ページ: 新着記事が即座に反映される必要があるが、秒単位の遅延は許容できる。アクセスが集中しやすい面でもある
- ECサイトの在庫状況表示: 「残りわずか」の表示が数秒遅れても致命的ではないが、売り切れた商品が購入可能に見えるのは困る。
must-revalidateで古い在庫情報の表示を防げる - イベントやキャンペーンのLP: 公開直後にSNS等から大量のアクセスが来る。マイクロキャッシングで最初の波を凌ぎつつ、内容更新があればすぐ反映できる
- ダッシュボードのサマリー表示: リアルタイムグラフほどの即時性は不要だが、最新のKPIを反映したい画面。ポーリング間隔が3秒以上ならこの設定と相性が良い
- APIレスポンス(リスト系): 一覧取得APIのレスポンスにこのヘッダを付与することで、CDNレイヤーでキャッシュし、オリジンサーバーの負荷を軽減できる
向いていないケース
- 静的アセット(CSS/JS/画像): 頻繁に変更されない。ファイルハッシュ付きの命名 +
max-age=31536000,immutableのほうが適切 - ブログの記事詳細ページ: 公開後に頻繁に更新されることは少ない。もっと長い
max-ageを設定して良い - リアルタイムチャットやWebSocket通信: そもそもHTTPキャッシュの対象外。3秒の遅延すら許容できない場面には不向き
- ユーザー固有のコンテンツ: マイページなど、ユーザーごとに異なるレスポンスはCDNでの共有キャッシュに適さない。
privateディレクティブとの組み合わせを検討すべき
注意点
忘れがちな落とし穴も書いておく。
3秒ごとの通信発生
3秒経過後は必ず条件付きリクエストが発生する。max-ageが長い設定と比べると、ネットワークレイテンシの影響を受けやすい。低帯域環境でのUXには注意が必要だ。
オフライン時の挙動
must-revalidateがあるため、ネットワークが切断されている場合やサーバーがダウンしている場合、キャッシュが残っていても表示されずエラーになる可能性が高い。「古いものを出すくらいならエラーにする」という挙動だ。
これを許容できない場合は、stale-while-revalidateやstale-if-errorとの組み合わせを検討する。
まとめ
Cache-Control: max-age=3, must-revalidateは、**「鮮度は命だが、サーバーを落としたくない」**というバランスを攻めた設定だ。
マイクロキャッシングという名前はやや大げさに聞こえるが、やっていることはシンプルで「ほんの数秒だけキャッシュする」それだけだ。その数秒が高トラフィック環境では劇的な効果をもたらす。
設定する前に、自分のサービスが「向いているケース」に該当するか確認すること。静的アセットや更新頻度の低いページにまでこの設定を適用してしまうと、無駄な再検証リクエストが増えるだけなので注意。
hsb.horse