帯域を無駄にするな:ETagと条件付きリクエストで実現する「賢い」HTTP通信
ネットワークエンジニアとして現場に立っていると、APIのレスポンス速度を改善してくれという無茶振りに遭遇することは一度や二度ではないはずだ。大抵の場合、真っ先に疑うのはバックエンドのクエリ速度やDBのインデックスだが、実は最も即効性があり、かつ見落とされがちなのが「HTTPの条件付きリクエスト(Conditional Requests)」の活用だ。
今日は、HTTP/1.1の洗練された仕組みである`ETag`と`If-None-Match`に焦点を当て、なぜこれが「無駄なパケットを減らす最強の武器」なのかを深掘りしていこう。
なぜETagが必要なのか?
HTTP/1.0の頃は、リソースの鮮度管理といえば`Last-Modified`(更新日時)が主流だった。しかし、これには致命的な弱点がある。
1. 粒度の粗さ: 多くのファイルシステムは更新日時を秒単位でしか管理していない。1秒間に複数回更新されるような動的なAPIでは役に立たない。
2. 実質的な変化の検知不可: 処理が終わった後のタイムスタンプが更新されても、中身が全く同じであれば再送は無駄だ。
そこで登場したのが`ETag`(Entity Tag)だ。これはリソースの状態を識別するための「指紋」のようなものだ。コンテンツのハッシュ値やバージョン文字列をサーバーが発行し、クライアントがそれを保持しておくことで、サーバーに「これ、まだ有効?」と効率的に確認できるようになった。
通信フロー:無駄なペイロードを排除する仕組み
ETagを活用したキャッシュ戦略の肝は、HTTPステータスコード 304 Not Modified にある。
1. 初回リクエスト(キャッシュなし)
クライアントがリクエストを投げ、サーバーはリソースと一緒に `ETag` を返す。
サーバーのレスポンス
HTTP/1.1 200 OK
Content-Type: application/json
ETag: “v1.0.4-abc12345” # これをクライアントは保存する
{“status”: “success”, “data”: “…”}
2. 二回目以降のリクエスト(条件付き)
クライアントは保存しておいたETagを `If-None-Match` ヘッダーに乗せて送信する。
クライアントのリクエスト
GET /api/resource HTTP/1.1
Host: example.com
If-None-Match: “v1.0.4-abc12345” # 「これと同じなら再送不要だよ」と伝える
もしサーバー側のリソースに変更がなければ、サーバーは 304 Not Modified を返し、ボディ(データ)の送信を省略する。これで帯域を大幅に節約できるわけだ。
強ETag(Strong)と弱ETag(Weak)の使い分け
実務でハマりやすいのが、`W/` から始まる「弱ETag」の存在だ。
- 強ETag (`”…”`): リソースの内容がバイト単位で完全に一致することを保証する。キャッシュの厳密な制御が必要な場合に使う。
- 弱ETag (`W/”…”`): 内容が「意味的に」等価であればOKとする(例:改行コードの違いや、わずかなメタデータの差は無視する)。`If-None-Match` で弱ETagを比較した場合、セマンティックな一致が優先される。
API設計時には、「このレスポンスはどの程度厳密に一致させるべきか」を考え、強/弱を使い分けるのがプロの流儀だ。
実装サンプル:現場で使えるFetch API
ブラウザからAPIを叩く際、モダンなブラウザはキャッシュ周りを自動でハンドリングしてくれることが多いが、手動で制御したい場合のコード例を挙げておく。
const etag = localStorage.getItem(‘api-etag’);
const response = await fetch(‘/api/data’, {
headers: {
// サーバーにキャッシュのETagを通知
‘If-None-Match’: etag ? etag : ”
}
});
if (response.status === 304) {
console.log(‘キャッシュが有効です。ローカルのデータを利用します。’);
} else if (response.ok) {
// 新しいデータを取得し、ETagを更新
const newEtag = response.headers.get(‘ETag’);
localStorage.setItem(‘api-etag’, newEtag);
const data = await response.json();
}
運用上の注意点:デバッグの心得
最後に、インフラエンジニアとして現場で遭遇する「あるある」を一つ。
`ETag` は便利だが、負荷分散環境(ロードバランサーやリバースプロキシ)では要注意だ。複数のバックエンドサーバーがある場合、各サーバーが異なるハッシュ計算アルゴリズムを持っていたり、タイムスタンプを混ぜてETagを生成していたりすると、サーバーを跨ぐたびにキャッシュミスが多発する。
Tips:
- ETagは、DBのバージョンIDや内容のハッシュ値から決定論的に生成するように設計すること。
- トラブル時は `curl -I -H ‘If-None-Match: “…”‘ [URL]` で304が正しく返ってくるか、レスポンスヘッダーの `Vary` 属性が適切かを確認せよ。
ETagは地味な仕組みだが、ネットワークの混雑を緩和し、ユーザー体験を劇的に向上させるエンジニアリングの粋だ。ぜひ今日の設計から、この「指紋」を意識して取り入れてみてほしい。それが、プロのインフラ屋の仕事というものだ。
コメント