キャッシュの「曖昧さ」を撲滅せよ:ETagとIf-None-Matchで実現する堅牢なHTTP検証
ネットワークの現場で「なぜか古いキャッシュが表示される」「サーバーの負荷が下がらない」というトラブルに遭遇したことはないだろうか。
HTTPのキャッシュ制御において、`Last-Modified`(最終更新日時)による検証は直感的だが、実は危うい。ミリ秒単位の更新や、同一秒内に複数回変更が加わった場合、クライアントは「まだ有効だ」と勘違いしてしまう。この「曖昧さ」を排除し、コンテンツの同一性をビット単位で保証するために生まれたのが、ETagとIf-None-Matchという強力なタッグだ。
今回は、Web API設計やインフラ運用において必須の教養である、条件付きリクエストの深淵を紐解いていこう。
—
ETagとは何か? ― 「指紋」による検証の真価
ETag(Entity Tag)は、サーバーがリソースに対して割り当てる識別子だ。これは単なるタイムスタンプではなく、リソースの内容から生成された「指紋」のようなものだと考えてほしい。
- 強い検証子(Strong Validator): 内容が1バイトでも変われば、ETagも必ず変わる。デフォルトの動作であり、厳密なキャッシュ管理に向いている。
- 弱い検証子(Weak Validator): 先頭に `W/` をつける(例: `W/”12345″`)。リソースの内容が「意味的に」同じであれば許容する形式だ。
サーバー側でETagを生成する際は、ファイルの内容をMD5やSHA-256でハッシュ化するのが一般的だが、計算コストを考慮して「inode番号 + ファイルサイズ + 最終更新時刻」を組み合わせる手法もよく使われる。
—
条件付きリクエストのシーケンス:無駄な通信を断つ
ETagを活用したキャッシュ検証のフローは、ネットワークスペシャリストとして見ていて気持ちがいいほど理にかなっている。
1. 初回アクセス: クライアントがGETリクエストを投げる。サーバーはリソースとともに `ETag: “abc-123″` を返す。
2. キャッシュ保持: クライアントはレスポンスボディとETagを保存する。
3. 再検証(If-None-Match): クライアントが再度同じURLにアクセスする際、保持していたETagを `If-None-Match: “abc-123″` ヘッダーとして送信する。
4. サーバーの比較: サーバーは現在のリソースのETagを計算し、クライアントからの値と照合する。
- 一致した場合: ボディを送る必要はない。`304 Not Modified` を返すだけで通信は完了する。
- 不一致の場合: 新しいリソース本体を `200 OK` とともに返す。
この「304」というステータスコードこそが、ネットワーク帯域とサーバーCPUを救う魔法の鍵だ。
—
実践:クライアントとサーバーの実装Tips
1. サーバー側のロジック(擬似コード:Node.js/Express)
インフラを設計する際、バックエンドエンジニアにこう要求してほしい。「ETagの生成ロジックはリソースの不変性を担保しているか?」と。
app.get(‘/api/data’, (req, res) => {
const data = getDataFromDB();
const etag = generateHash(data); // データのハッシュ値を計算
// クライアントからのIf-None-Matchと比較
if (req.headers[‘if-none-match’] === etag) {
// 304を返せば、ボディの送信は不要(ネットワーク帯域の節約!)
return res.status(304).end();
}
// 変更があれば200でデータを返す
res.setHeader(‘ETag’, etag);
res.json(data);
});
2. クライアント側の確認(curlでデバッグする)
トラブルシューティングの際、ブラウザのデベロッパーツールだけで満足してはいけない。`curl` を使って生パケットレベルの挙動を確認するのが、現場の流儀だ。
1回目:通常のリクエスト。ETagを取得する
curl -I https://api.example.com/data
2回目:前回のETagをヘッダーに含めてリクエスト
curl -I -H ‘If-None-Match: “abc-123″‘ https://api.example.com/data
期待される結果:
HTTP/1.1 304 Not Modified
Date: …
ETag: “abc-123”
—
運用における「落とし穴」と教訓
最後に、シニアエンジニアとして一つ警告しておく。「ETagの生成コスト」だ。
数GBもの巨大なファイルを配信する際、毎回ハッシュ計算を行っていては、かえってCPU負荷が跳ね上がる。この場合、ETagによる厳密な検証よりも、`Cache-Control: max-age=…` による期限付きキャッシュを優先し、検証は `Last-Modified` に任せるという設計判断も必要になる。
- APIや小さなJSON: ETagを積極的に使い、厳密な整合性を保つ。
- 巨大なメディアファイル: 期限付きキャッシュ(Expires/Max-Age)を賢く使い、検証コストを抑える。
ネットワークアーキテクチャに「唯一の正解」はない。しかし、ETagとIf-None-Matchというプロトコルの作法を知っているだけで、システムを設計する際の「武器」の数は格段に増えるはずだ。
さて、次は君の番だ。自身の環境でパケットをキャプチャし、この `304 Not Modified` の美しいダンスを確認してみてほしい。これこそが、Webを支えるプロトコルの醍醐味なのだから。
コメント