Web APIの「キャッシュ戦略」を制する者は、インフラの寿命を制する
エンジニア諸君、日々APIの設計に頭を悩ませていることだろう。REST APIの美学といえば「リソース指向」だが、その美しさを支える裏方こそがHTTPヘッダー、特に Cache-Control だ。
多くのジュニアエンジニアが「なんとなく」で設定し、後々「キャッシュが消えない」「個人情報がCDNに漏れた」といった惨事に直面する。今日は、RFC 9111(HTTP Caching)の深淵を覗きつつ、現場で泣きを見ないためのキャッシュ設計を叩き込む。
なぜ Cache-Control が「インフラの守護神」なのか
APIレスポンスのキャッシュは、単なる高速化の手段ではない。オリジンサーバーへの負荷を劇的に下げ、ネットワーク帯域を節約し、結果としてサービス全体の可用性を左右する「防波堤」だ。
しかし、この防波堤を誤った設定で運用するとどうなるか。例えば、認証が必要なユーザー固有の情報を public と誤認してキャッシュさせれば、ユーザーAのマイページがユーザーBに見えてしまうという致命的なインシデントに直結する。
4つの主要ディレクティブ:その「境界線」を見極める
まずは、現場で最もよく使う4つのディレクティブを正確に整理しておこう。
1. public
「どこでもキャッシュして良い」という許可証だ。共有キャッシュ(CDNやプロキシ)を含め、誰でもレスポンスを保存できる。検索結果の一覧や静的なマスターデータなど、誰が見ても同じ内容のリソースに適用する。
2. private
「ブラウザ(クライアント)のみキャッシュせよ」という命令だ。CDNのような共有キャッシュに保存させるのは厳禁。ユーザーごとのダッシュボードや、パーソナライズされたAPIレスポンスには、必ずこれを指定する。
3. no-cache
ここが勘違いされやすい。「キャッシュするな」という意味ではない。「キャッシュしてもいいが、必ずオリジンサーバーに『変更がないか』を確認せよ(再検証せよ)」という意味だ。ETag と組み合わせて、リソースの鮮度を保証したい場合に重宝する。
4. no-store
最強の拒否権。「一切キャッシュを保存するな」という命令。機密性の高い個人情報や、決済情報など、ディスクやメモリに残してはならないデータに使う。
—
実践:現場で役立つ実装パターン
ケースA:CDNで配信する公開API(最大1時間キャッシュ)
Cache-Control: public, max-age=3600
これだけで、世界中のCDNエッジサーバーが1時間のリクエストを肩代わりしてくれる。インフラの負荷は激減するはずだ。
ケースB:個人情報を扱うAPI(キャッシュ厳禁)
Cache-Control: no-store, private
サーバーサイドでヘッダーを付与する例(Python/Flask)を見てみよう。
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/api/user/profile')
def get_profile():
# ユーザー固有のデータはキャッシュさせないのが鉄則
response = make_response({"name": "Taro Yamada", "status": "active"})
response.headers['Cache-Control'] = 'no-store, private'
return response
ケースC:ETagで賢く再検証(no-cacheの真骨頂)
ETag を使えば、データに変更がない場合に 304 Not Modified を返し、通信量を大幅に削減できる。
# curlで挙動を確認する例
# -Iでヘッダーのみ取得し、キャッシュの挙動を追跡する
curl -I -H "If-None-Match: \"etag-value-here\"" https://api.example.com/data
運用時の「泥臭い」チェックリスト
現場でデバッグを行う際、以下のコマンドを叩いてレスポンスを確認するのがエンジニアの流儀だ。
1. curl で応答を見る
curl -svo /dev/null https://api.example.com/v1/resource を実行し、Cache-Control や Age ヘッダーを確認する。Age ヘッダーがインクリメントされていれば、どこかのキャッシュサーバーでヒットしている証拠だ。
2. CDNのパージ(無効化)を待つ勇気
キャッシュの設計を誤った時、即座に全キャッシュをクリアするのは最終手段だ。可能な限り、max-age を短くし、徐々に伸ばす「漸進的な運用」を心がけてほしい。
3. Vary ヘッダーを忘れない
もしAPIが Authorization や Accept-Encoding でレスポンスを出し分けるなら、Vary: Authorization を必ず付与せよ。これを忘れると、キャッシュサーバーが「認証なしのレスポンス」を「認証済みのユーザー」に返すという地獄を見る。
最後に:ネットワークを愛する諸君へ
キャッシュの設計は、パケットがどこで留まり、どこで計算されるかをイメージする「ネットワークの地図」を描く作業に等しい。
「なんとなく no-cache にしておけばいいや」という安直な選択は、スケーラビリティを殺す。一つ一つのリソースの特性を理解し、適切な Cache-Control を付与することで、あなたのAPIはより速く、より堅牢になる。
プロトコルは嘘をつかない。挙動が怪しいと思ったら、まずはパケットとヘッダーを見ろ。それが、一流のエンジニアへの近道だ。
コメント