Cache Control
大規模サービスを支えているのはキャッシュだ。
もしあなたのWebサービスが暴力的なアクセスの洗礼を受けたら、最初にやるべきことはキャッシュヒット率をメトリクスで取ることだ。これが見えていないサービスは、自分がどれだけキャッシュに依存しているか把握できていない。もちろんアプリケーションによるが、ヒット率80%なら上出来だ。80%から60%に落ちたらオリジンへのリクエストは倍になる。大規模サービスではこの数%の変動が障害の引き金になる。
Cache-Controlヘッダでキャッシュの寿命を制御する。max-ageで秒数を指定し、no-cacheで毎回検証を要求し、no-storeでキャッシュ自体を禁止する。s-maxageを使えばCDNとブラウザで異なるTTLを設定できる。何をどれだけキャッシュするかは設計判断だ。静的アセットは長く、APIレスポンスは短く、ユーザー固有のデータはキャッシュしない。
問題はキャッシュを消したいときだ。CDNのキャッシュは世界中のエッジに散らばっている。パージを叩いても、全エッジに反映されるまでタイムラグがある。古い画像が見えている、新しいCSSが当たらない。クレームが来る。
Phil Karltonの有名な言葉がある。「コンピュータサイエンスで難しいことは二つだけ。キャッシュの無効化と命名だ」。
対策としてファイル名にハッシュを入れるのが定番になった。app.css?v=1ではなく、app.a3b2c1.cssにする。中身が変われば名前が変わるから、キャッシュの無効化を考える必要がない。ただ個人的にはこの手法に懐疑的だ。デプロイのたびにファイル名が変わるのはデバッグしにくいし、古いファイルの掃除も面倒だ。消せないなら消さなくていい設計、という割り切りは理解できるが、別の複雑さを持ち込んでいるだけのような気もする。
つい最近、CloudflareのキャッシュをClaude Codeに吹き飛ばされた。調査でAPIを叩かせていたら、パージコマンドを実行されていた。気づいたときには呆然だった。幸い個人サイトだったので問題はなかったが、AIにキャッシュを消される時代が来るとは思わなかった。
結局のところ、キャッシュの問題に銀の弾丸はない。速くしたければキャッシュする。キャッシュすれば消せなくなる。ヒット率が落ちた瞬間、オリジンのDBにDDoSが起きる。即死する。