帯域を無駄にするな:If-Modified-Sinceで実現する「賢い」キャッシュ戦略
Webインフラの現場にいると、時折「なぜかレスポンスが重い」「帯域が逼迫している」という相談を受ける。原因を探ると、クライアントが毎回律儀に巨大なデータを全件取得し、サーバーが律儀にそれを全送信している……なんていう非効率な光景に出くわすことが多い。
HTTP/1.1の時代から続く「条件付きGET(Conditional GET)」という概念は、一見地味だが、Webアプリケーションのパフォーマンスとコスト効率を左右する極めて強力な武器だ。今回は、その主役である `If-Modified-Since` ヘッダーについて、現場の視点から深掘りしてみよう。
なぜ「全件取得」が罪なのか
HTTP/0.9からHTTP/1.1への進化の歴史は、いかに効率よくデータを運ぶかという戦いだった。サーバー上のリソースが変わっていないのに、クライアントが毎回「そのファイルをくれ」と言い、サーバーが「はい、どうぞ」とバイト列をすべて送り直す。これは、昨今のクラウドの転送量課金を考慮すれば、ただの「金ドブ」に等しい。
ここで登場するのが If-Modified-Since だ。
クライアントは「このファイル、〇〇年〇月〇日より後に更新されてる?」とサーバーに尋ねる。サーバーは最終更新日(Last-Modified)を確認し、変更がなければ「304 Not Modified」という、ボディを含まない軽量な応答を返すだけでいい。これが、帯域節約の要諦だ。
通信フロー:304応答の舞台裏
この仕組みは、初回アクセスと2回目以降のアクセスで役割分担が明確に分かれている。
1. 初回アクセス:サーバーは `Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT` といった日付を付与してリソースを送る。クライアントはこの日時をブラウザキャッシュやローカルストレージに保存する。
2. 2回目以降:クライアントはリクエストヘッダーに `If-Modified-Since: Wed, 21 Oct 2023 07:28:00 GMT` を付与して送る。
3. 判定:サーバーはリソースの現在の更新日時と、ヘッダーの日時を比較する。
- 更新なし:`304 Not Modified` を返す。ボディは空。
- 更新あり:`200 OK` を返し、新しいボディと新しい `Last-Modified` を送る。
実践:curlとブラウザでの確認
理屈はわかっても、手を動かさなければエンジニアとは言えない。まずは `curl` で挙動をトレースしてみよう。
初回取得(Last-Modifiedを確認)
curl -I https://example.com/api/data.json
2回目:If-Modified-Sinceを付与して確認
curl -I -H “If-Modified-Since: Wed, 21 Oct 2023 07:28:00 GMT” https://example.com/api/data.json
もしサーバーが正しく実装されていれば、2回目のレスポンスステータスは `304` になるはずだ。
また、フロントエンド開発でFetch APIを使う場合も、ブラウザは自動的にこのヘッダーを管理してくれるが、キャッシュをバイパスしたい場合は `cache: ‘no-cache’` を指定するなどの制御も重要になる。
// Fetch APIでのリクエスト例
fetch(‘https://example.com/api/data.json’, {
method: ‘GET’,
headers: {
// ブラウザのキャッシュ制御を明示する場合のヒント
‘Cache-Control’: ‘no-cache’
}
})
.then(response => {
if (response.status === 304) {
console.log(‘キャッシュが有効です。帯域を節約しました!’);
} else {
return response.json();
}
});
現場でハマりやすい「落とし穴」
この仕組み、一見完璧だが、運用でいくつか注意すべき点がある。
- 時刻の精度:`Last-Modified` はHTTP日時のフォーマット(RFC 7231)に厳密に従う必要がある。サーバー側の時刻がずれていると、意図せずキャッシュがパージされたり、逆に古いデータが残り続けたりする。
- 動的生成コンテンツ:PHPやNode.js等で動的に生成するAPIの場合、サーバー側で明示的に `Last-Modified` ヘッダーを出力し、かつ `If-Modified-Since` を判定するロジックを書かなければ、304は返らない。「何もしなくてもブラウザがキャッシュしてくれる」というのは間違いだ。
- ETagとの併用:実は `If-Modified-Since` より `ETag` (ハッシュ値による比較) の方が、時刻のズレに左右されないため、現代的なWeb APIでは併用が推奨される。`If-None-Match` ヘッダーと `ETag` の組み合わせも、必ずセットで押さえておくこと。
締めくくり:インフラエンジニアの矜持
「動けばいい」というコードと、「どう通信が行われ、どうリソースを節約するか」を理解したコードの間には、雲泥の差がある。
HTTPヘッダーの小さな一行は、世界中のユーザーにとっての待ち時間の短縮であり、貴社のインフラコストの削減に直結する。今度、ブラウザのデベロッパーツールを開いたときは、ぜひNetworkタブを覗いてみてほしい。304の山を築けているなら、君の設計は正しい。
もし、すべてのレスポンスが200で返されているなら……今日から改善の余地があるということだ。ネットワークは、細部にこそ神が宿る。
コメント