Dev Study
AWSサービスの内部原理 コース

103. エッジのコンテンツ更新には3つの伝え方がある — 期限で待つ・無効化で消す・名前を変える

CloudFrontのエッジに一度置いたコンテンツを「どうやって新しくするか」——期限で待つ・無効化で消す・名前を変える、という3つの手段がそれぞれ何を犠牲に何を買っているのかを、図で追っていきます。

① 期限切れは「削除」ではなく「再検証の合図」

エッジに載るTTL有効のあいだ
オリジンに一切問い合わせずエッジがそのまま配る
次のビューワー要求が来た「その時」に初めて
stale状態「古い」扱いだがまだエッジに載っている
消えたのではない
オリジンの答えで分岐
オリジンへ再検証最新版かどうかを確認しに行く
エッジの振る舞い
304 Not Modified変わってない・本体は送り直さない
200 OK + 新本体変わった・最新版を返送
そのまま配るキャッシュ済みの本体を最新とみなす
新本体を配る新しい本体をキャッシュし直す
  • "After it expires, the next time the edge location gets a request for the file, CloudFront forwards the request to the origin to verify that the cache contains the latest version of the file."(公式)
  • "If the CloudFront cache already has the latest version, the origin returns a status code 304 Not Modified." / "If the CloudFront cache does not have the latest version, the origin returns a status code 200 OK and the latest version of the file."(公式)
  • 補足: 人気のないオブジェクトは期限前でもエビクション(追い出し)され得る——"If a file in an edge location isn't frequently requested, CloudFront might evict the file—remove the file before its expiration date"(公式)。前レッスンで見たエビクションの復習。

期限切れ=削除ではなく、次の要求を引き金にした「これ最新ですか?」という再検証の合図。オリジンが304を返せば本体は流れない。304という条件付きリクエストの応答は、可観測性編・API Gateway編で見たHTTPの条件付きリクエストがそのまま実戦投入されたもの。

② TTLはどう決まるか — キャッシュポリシーとオリジンヘッダの合成

オリジンの応答キャッシュ指示ヘッダはあるか?
併存したときの優先順位
なし→Default TTL既定は24時間(ポリシー未使用時)
max-ageブラウザとCloudFront共通の寿命
s-maxage共有キャッシュ(CloudFront)専用の寿命
Expires絶対時刻で指定
どのヘッダでも最後に
max-age優先max-ageとExpiresが両方あればmax-age
s-maxage優先CloudFrontはs-maxage・ブラウザはmax-ageを使う
1応答で「ブラウザには短く・エッジには長く」を書き分けられる
Max TTLでキャップヘッダ値とMax TTLの小さい方を採用
  • "If you opt out of using a cache policy, the default TTL (Time to Live) is 24 hours"(公式・執筆時に現行ドキュメントで確認: 24時間)。
  • "If you specify values both for Cache-Control max-age and for Expires, CloudFront uses only the value of Cache-Control max-age."(公式)
  • max-ageあり(Min TTL=0)のとき: "CloudFront caches the object for the lesser of the value of the Cache-Control: max-age directive or the value of the CloudFront maximum TTL."(公式)=ヘッダ値とMax TTLの小さい方。
  • s-maxageあり(Min TTL=0)のとき: "CloudFront caches the object for the lesser of the value of the Cache-Control: s-maxage directive or the value of the CloudFront maximum TTL."(公式)。ブラウザはmax-ageの値でキャッシュする。
  • Expiresあり(Min TTL=0)のとき: "CloudFront caches the object until the date in the Expires header or for the value of the CloudFront maximum TTL, whichever is sooner."(公式)

TTLは「オリジンのヘッダ ∧ CloudFrontのMax TTLでキャップ」の合成で決まる。s-maxageの存在意義は、1つの応答でブラウザ(max-age)と共有キャッシュ(s-maxage)の寿命を別々に書けること。ヘッダが皆無なら既定24時間に落ちる。

③ ビューワーには強制再取得の権限がない

ビューワーGET + Cache-Control / Pragma
「最新を取り直して!」
TTLがまだ有効なら
CloudFrontエッジビューワー要求のそのヘッダを無視
キャッシュ済みを返すオリジンへは取りに行かない
  • "You cannot use the HTTP Cache-Control or Pragma header fields in a GET request from a viewer to force CloudFront to go back to the origin server for the object. CloudFront ignores those header fields in viewer requests."(公式)

強制再取得の権限は利用者側にない。キャッシュの寿命を決めるのはオリジンと配信者であって、閲覧者ではない。だからこそ「エッジに残った古いコピー」を配信者が能動的に消すための手段(=次の④⑤)が必要になる。

④ 古さを許して速度と可用性を買う — stale-while-revalidate / stale-if-error

freshを配るmax-age=3600の間(1時間)
例: Cache-Control: max-age=3600, stale-while-revalidate=600, stale-if-error=86400
どちらも「古さ」と引き換えに
速度を買うstale-while-revalidate=600
古いのを即返しつつ、裏で非同期にオリジンから取り直す(例では最大10分)
可用性を買うstale-if-error=86400
オリジンが5xx(500〜600)や不達なら、古いのを配り続ける(例では最大24時間)
待たずに応答ビューワーはすぐ受け取る
障害中も配信オリジンが落ちても止まらない
  • stale-while-revalidate: "CloudFront to serve stale content from the cache while CloudFront asynchronously fetches a fresh version from the origin."(公式)例ではmax-age=3600, stale-while-revalidate=600 → 最大10分のstale配信。
  • stale-if-error: "allows CloudFront to serve stale content from the cache if the origin is unreachable or returns an error code that is between 500 and 600."(公式)例ではstale-if-error=86400 → 最大24時間のstale配信。
  • 両者とも"the value of the … directive or the value of the CloudFront maximum TTL, whichever is less"でMax TTLに頭打ちされる。Max TTLを超えると、値に関わらずstaleは配れなくなる。

stale-while-revalidateは「古さ↔速度」、stale-if-errorは「古さ↔可用性」の交換。stale-if-errorは、DynamoDB編以来くり返してきた「一貫性を落として可用性を買う」トレードオフを、応答ヘッダ1行で明示的にONにするスイッチそのもの。

⑤ 無効化で消す — ただしCloudFrontのキャッシュにしか効かない

invalidation送信パス指定 例: /styles.css や /*
だが、この壁の外には届かない
エッジから除去CloudFrontのエッジキャッシュから対象を取り除く
次のビューワー要求は必ずオリジンへ取りに行く
その版がローカルの期限切れになるまで
ブラウザキャッシュ残った古いコピーには効かない
企業プロキシ残った古いコピーには効かない
古いまま見え得る利用者には旧版が表示され続けることがある
  • "Invalidate the file from edge caches. The next time a viewer requests the file, CloudFront returns to the origin to fetch the latest version of the file."(公式)
  • 効く範囲の限界(公式): "If you invalidate the file, the user might continue to see the old version until it expires from those caches."——locally(ブラウザ)や corporate caching proxy に残った版のこと。
  • 課金: "The first 1,000 invalidation paths that you submit per month are free; you pay for each invalidation path over 1,000 in a month."(公式)ワイルドカード(例 /*)を含むパスは、何千ファイルを消しても1パスと数える。
  • この無料枠1,000パス/月は、ディストリビューション毎ではなくAWSアカウント単位(全ディストリビューション合算)で数える。

無効化はCloudFrontのエッジキャッシュにしか効かない。ブラウザや企業プロキシに残ったコピーには届かず、そこが古いままになり得る——これが「消す」手段の構造的な弱点。

⑥ 名前を変える — 「更新」を「別オブジェクトの新規作成」に変える

別名で新規作成styles-v1.cssの中身を直さず、styles-v2.cssを新規に置く
「更新」を「別名の新規作成」への変換とみなす
⑤で届かなかった層にも「必ず」届く
未知の名前全キャッシュ層が「見たことのない名前」として必ず取り直す
CloudFrontエッジ新規なので当然フェッチ
ブラウザ別URLなので古いv1は関係ない
企業プロキシ同上・必ず新版を取りに行く
  • 公式の推奨: "If you want to update your files frequently, we recommend that you primarily use file versioning"。
  • 全キャッシュ層に効く: "Versioning enables you to control which file a request returns even when the user has a version cached either locally or behind a corporate caching proxy."(公式)
  • ログ分析: "CloudFront access logs include the names of your files, so versioning makes it easier to analyze the results of file changes."(公式)——無効化ではログでの版の追跡がしにくい。
  • ロールバック: "Versioning simplifies rolling forward and back between file revisions."(公式)——無効化で戻すには、再度無効化とオリジン側の差し替えが要る。
  • 費用: "Versioning is less expensive. You still have to pay for CloudFront to transfer new versions of your files to edge locations, but you don't have to pay for invalidating files."(公式)——新しい名前の転送費用のみで、無効化の追加費用なし(⑤の1,000パス/月超の課金と対比)。

名前を変えることで、⑤で届かなかったブラウザ・企業プロキシにも「必ず」新版が届く。これはコンテナ編のイミュータブルなイメージ、S3編の「上書きせず別オブジェクト」と同じ思想の合流点——中身を書き換えず、新しい名前の不変オブジェクトを作る。

⑦ 締め — 3つの手段を対価で並べる

期限で待つタダだが遅い
無効化で消す速いがCloudFrontにしか効かず有料になり得る
名前を変える全層に必ず届き安価だが、参照の差し替えが要る
無効化しない中身を不変にして、名前を変える
難問の1つ(無効化)を、もう1つ(命名)で回避する
  • 「キャッシュ無効化(cache invalidation)と命名(naming things)は計算機科学の2大難問」という有名な格言の両方が、この回に登場した。

3手段を対価で並べると——期限で待つ(タダだが遅い)、無効化で消す(速いがCloudFrontにしか効かず有料)、名前を変える(全層に必ず届き安価だが参照の差し替えが要る)。一番堅い答えは「無効化しない」——難問の1つ(無効化)を、もう1つ(命名)を使って回避する、というのがこの回の芯。

公式ドキュメントで詳しく ↗