ETag と条件付きリクエスト(304 Not Modified)

初級 | 15分 で読める | 2026.05.02

Webページを再表示するたびに、画像やCSSの中身をすべて受信し直す必要はありません。

ブラウザに保存済みの内容がまだ使えるなら、サーバーは「変更なし」とだけ伝えられます。その確認に使う代表的な仕組みが ETag条件付きリクエスト です。

この記事では、最初の 200 OK から次回の 304 Not Modified まで、1つのCSSファイルの往復を追います。

初回の200 responseとETagをcacheし、次回If-None-Matchでrevalidateして変更がなければ304を受け、保存済みbodyを再利用する条件付きrequest図

先に結論

ETagは、サーバーがレスポンスの版を識別するために付ける値です。

流れは次の4段階です。

  1. サーバーが本文と ETag を返す
  2. ブラウザが本文とETagを保存する
  3. 次回、ブラウザが If-None-Match で保存済みETagを送る
  4. 変更がなければサーバーは本文なしの 304 を返す

変更されていれば、サーバーは新しい本文とETagを 200 で返します。

つまり、ETagは転送を省くための「版の照合」に使われます。

ETagは「版を見分ける札」

ETagはHTTPレスポンスヘッダーです。

ETag: "style-v1"

引用符を含む文字列全体が値です。この値は opaque validator(中身を解釈しない検証子) として扱います。

見た目がハッシュや更新番号に似ていても、クライアントが意味を推測してはいけません。ETagをどう作るかはサーバー側の責任です。

初回は200と本文を受け取る

ブラウザが初めて /styles.css を取得します。

GET /styles.css HTTP/1.1
Host: example.com

サーバーは本文とETagを返します。

HTTP/1.1 200 OK
Content-Type: text/css
Cache-Control: no-cache
ETag: "style-v1"

body { color: navy; }

ブラウザは次の2つを対応付けて保存できます。

  • 本文: body { color: navy; }
  • 検証子: "style-v1"

ここでの Cache-Control: no-cache は「保存禁止」ではありません。「再利用する前にサーバーへ確認する」という意味です。

次回はIf-None-Matchで確認する

同じURLが再び必要になったとき、ブラウザは保存済みETagを If-None-Match に入れます。

GET /styles.css HTTP/1.1
Host: example.com
If-None-Match: "style-v1"

このリクエストは、サーバーに次のように尋ねています。

手元に "style-v1" の内容があります。現在も同じですか。

ETagそのものを送り返すのではなく、リクエスト側では If-None-Match という別のヘッダー名を使う点が重要です。

変更がなければ304

現在の版も "style-v1" なら、サーバーは 304 Not Modified を返します。

HTTP/1.1 304 Not Modified
ETag: "style-v1"
Cache-Control: no-cache

304 の後ろにCSS本文はありません。ブラウザは初回に保存した本文を再利用します。

そのため、表示結果は同じでも転送量を減らせます。大きな画像、JavaScript、CSSでは特に効果があります。

304は単独では表示内容を持たない

304 は「前に保存した内容を使ってよい」という合図です。本文を新しく届ける成功レスポンスではありません。

保存済みの表現がないクライアントに、304 だけを返しても内容を復元できません。条件付きリクエストへの応答として使われます。

変更があれば新しい200

CSSが変更され、現在のETagが "style-v2" になったとします。

ブラウザから届く値は古いままです。

GET /styles.css HTTP/1.1
Host: example.com
If-None-Match: "style-v1"

値が一致しないため、サーバーは新しい本文を返します。

HTTP/1.1 200 OK
Content-Type: text/css
Cache-Control: no-cache
ETag: "style-v2"

body { color: teal; }

ブラウザは本文とETagを新しい組み合わせへ更新します。

200と304の見分け方

状態ステータス本文ブラウザの動作
初回取得200あり本文とETagを保存
ETagが一致304なし保存済み本文を再利用
ETagが不一致200あり本文とETagを更新

判断の中心は、If-None-Match と現在のETagが一致するかどうかです。

強いETagと弱いETag

ETagにはstrong(強い)とweak(弱い)があります。

強いETag

ETag: "style-v2"

強いETagは、表現がバイト単位で同一だと判断できる場合に使います。

弱いETag

ETag: W/"article-v5"

先頭の W/ はweakを表します。意味上は同じでも、空白や圧縮方法などの違いによってバイト列が完全一致しない可能性があるときに使えます。

通常のGETに対する If-None-Match では、弱い比較によって「再利用可能か」を判断できます。一方、バイト範囲のように完全一致が重要な処理では、強い検証子が必要になります。

初学者は、まず次の区別で十分です。

  • "abc": バイト単位の同一性を示せる
  • W/"abc": 意味上は同じと示せる

freshnessとvalidationは別

HTTPキャッシュには2つの問いがあります。

  1. freshness: 確認せず、そのまま使える期間内か
  2. validation: 保存済み内容が現在も有効か

たとえば次のレスポンスは、60秒間はfreshです。

Cache-Control: max-age=60
ETag: "style-v2"

freshな間は、通常サーバーへリクエストせず保存済み内容を使えます。期限が切れた後に、ETagで再検証できます。

対して次の指定は、保存はできても再利用前の確認を求めます。

Cache-Control: no-cache

名前に反して、no-cache は保存禁止ではありません。

保存自体を避けさせたい場合は、目的が異なる no-store を使います。

Cache-Control: no-store

ETagは「いつまで新鮮か」を決める値ではなく、「手元の版が現在の版と同じか」を確かめる値です。

Last-Modifiedとの関係

別の条件付きリクエストとして、更新日時を使う組み合わせもあります。

Last-Modified: Sat, 25 Jul 2026 03:00:00 GMT

次回は次のように確認します。

If-Modified-Since: Sat, 25 Jul 2026 03:00:00 GMT

ETagはサーバーが版を表す値を選べるため、時刻だけでは区別しにくい変更にも対応できます。両方が送られた場合の評価規則はHTTP仕様に従いますが、学習段階ではまず ETagIf-None-Match の1組を追いましょう。

curlで往復を確認する

まずヘッダー込みで取得します。

curl -i https://example.com/styles.css

レスポンスに次のような行があるか確認します。

ETag: "style-v1"

次に、その値を条件として送ります。

curl -i \
  -H 'If-None-Match: "style-v1"' \
  https://example.com/styles.css

変更がなければ 304 Not Modified になり、ヘッダーの後に本文がないことを確認できます。

実際のETagをコピーするときは、引用符も含めます。シェル全体を単一引用符で囲むと扱いやすくなります。

DevToolsで確認する

ブラウザの開発者ツールでは、次の順番で確認します。

  1. Networkタブを開く
  2. ページを再読み込みする
  3. 対象の画像、CSS、JavaScriptを選ぶ
  4. Request Headersの If-None-Match を見る
  5. StatusとResponse Headersの ETag を見る

Networkタブの「Disable cache」を有効にすると、通常のキャッシュ挙動を再現できない場合があります。ETagの再検証を観察するときは無効にして試してください。

ブラウザ表示上の from memory cachefrom disk cache は、サーバーへ確認せず再利用した場合にも現れます。必ずリクエストヘッダーとステータスを一緒に見ます。

よくある勘違い

304にも本文が入っている

304 に表現の本文はありません。表示されている内容は、クライアントが保存済み本文を再利用したものです。

ETagは必ずファイルのハッシュ

生成方法はサーバー次第です。クライアントは値を解析せず、受け取った値を検証時に返します。

no-cacheならキャッシュされない

no-cache は再利用前の検証を要求します。保存禁止は no-store です。

304はエラー

3xx に分類されますが、条件付きGETでは正常な「変更なし」の応答です。

URLが違ってもETagだけで再利用できる

キャッシュは対象URLやリクエスト条件と対応付けて管理されます。ETag文字列だけを全URL共通の識別子として扱うものではありません。

セキュリティ上の注意

ETagは認証やアクセス制御の代わりではありません。認証が必要な内容は、毎回適切に権限を確認する必要があります。

ユーザーごとに内容が変わるレスポンスでは、共有キャッシュに誤って保存されないよう Cache-ControlVary を適切に設計します。

また、ETagに個人情報や内部パスをそのまま埋め込まないでください。クライアントから見えるHTTPヘッダーです。

まとめ

ETagを使う往復は、次の1本の流れです。

初回: 200 + ETag + 本文
次回: If-None-Match
一致: 304 + 本文なし → 保存済み本文を使う
不一致: 200 + 新ETag + 新本文

覚える要点は3つです。

  • ETagは中身を推測せず、そのまま扱う検証子
  • 304 Not Modified には本文がない
  • freshnessの期間と、ETagによるvalidationは別の仕組み

この流れが分かると、Networkタブで「なぜ本文を受信していないのに表示できたのか」を説明できるようになります。

次に読む

参考リソース

← 一覧に戻る
PR
PR
PR
PR