HTTP 304はエラーではない:ETagとブラウザーキャッシュの再検証を読み解く

ネットワーク・RFCカテゴリを表すパンダのイラスト ネットワーク・RFC

この記事について
この記事は、生成AIを活用した自動生成フローで作成しています。HTTPの国際標準RFC 9110・RFC 9111とMDNの一次資料をもとに、304応答とキャッシュの関係を整理しました。

検証ステータス:📘 公式仕様確認済み・実通信/ブラウザー操作は未実施

Webサイトを開発者ツールで調べると、Network一覧に「304 Not Modified」と表示されることがあります。本文が届いていないのに画像やスタイルが表示されるため、「通信エラーなのか」「キャッシュが壊れているのか」と迷うかもしれません。304は通常の障害ではなく、以前に保存した内容を引き続き利用できることを伝える応答です。無駄な本文の転送を避ける仕組みと理解すると、原因調査がしやすくなります。 、ETagとIf-None-Matchによる再検証を具体例で追い、304・200・通信なしの違いを整理します。別の記事で扱ったHTTP 428・412は更新の上書き防止が主題ですが、今回の主題はGETで取得した表示資源を再取得する必要があるかというキャッシュの判定です。

項目対応環境・条件
対象HTTP GET/HEAD、ブラウザーや中継キャッシュ
観察に使うものChrome・Edge・FirefoxなどのNetwork開発者ツール
必要な費用追加の有料サービスやAPIキーは不要
調査範囲RFCの規則とMDNの説明を確認。特定サイトでの実測なし
主な制約条件付き要求に非対応の資源では304を観測できない場合あり

最初に試す:Networkで304の前後を見る

観察対象は、一般公開されている画像やCSSなど、調査して問題のない資源にしてください。認証付きの画面やCookie、個人を識別できるヘッダーを記録して記事へ転載する必要はありません。次の3手順は読者が試すための方法で、今回の執筆環境で再現成功したという意味ではありません。

  1. ブラウザーの開発者ツールでNetworkパネルを開きます。まず「Disable cache(キャッシュを無効化)」はオフにしておきます。ページを通常の方法で読み込み、画像またはCSSのリクエストを一つ選びます。

  2. Response HeadersにETag、Last-Modified、Cache-Controlなどがあるかを見ます。Statusが200で本文を受け取った初回と、保存済み資源を使う可能性がある再読み込みを区別します。

  3. 通常の再読み込み後、Request HeadersのIf-None-MatchまたはIf-Modified-Since、応答のStatus、転送量を比較します。304が出たら何を条件として問い合わせたのかを確認してください。

必ず304になるわけではありません。ブラウザーが保存版を新鮮と判断した場合はネットワークへ問い合わせず、「from memory cache」「from disk cache」などの表示になることもあります。強制再読み込みやキャッシュ無効化は条件を変えます。反対に資源が更新されていれば200で新しい本文が返るため、304が出なかっただけで故障と結論しないことが大切です。

200と304:いつ何が届くのか

最初のGETでは、サーバーが200 OKと画像やCSSの内容を返します。その際にETagなどの識別子や保存条件が返され、キャッシュ可能な場合にはクライアントが本文を保存できます。時間が経って再検証が必要になると、クライアントは「手元の版はまだ使えるか」を条件付きGETで問い合わせられます。

例えば、初回の応答にETag: “rev-7” があれば、後の要求にIf-None-Match: “rev-7” を指定できます。サーバーが現在の表現と照合し、条件が一致して変更されていないと判断すれば304 Not Modifiedを返します。保存済み表現を選べるキャッシュは、その本文を再利用できます。変更されていれば200と新しい本文になることがあります。

場面説明用の架空通信結果
初回GET → 200、ETag: “rev-7”本文と識別子を取得
再確認GET、If-None-Match: “rev-7”手元の版を問い合わせる
同じ版304 Not Modified保存済み本文を利用
新しい版200 OK、新しいETag更新版の本文を取得

304は「そのURLが永遠に変わらない」という意味ではありません。その条件付き要求で、保存している表現を再利用できるという判断です。サーバー側の内容が別の時点で変わることも、リクエスト条件に応じて異なる表現が選ばれることもあります。そのため、URLだけを見て304と200を単純比較するのは不十分です。

RFC 9110では、304は条件付きGETまたはHEADに対する応答とされています。304自体に表示用の本文はありませんが、Date、ETag、Vary、Cache-Controlなど必要なヘッダーが含まれる場合があります。キャッシュはRFC 9111の規則に従い、保存済み応答を特定して関連ヘッダーを更新できます。本文を新規受信しなくてもページが表示できるのは、こうして以前の内容を利用するためです。

ETagはハッシュ値と同じもの?

ETagはサーバーが表現へ付ける識別子です。ファイル内容のハッシュに由来する実装もありますが、必ずSHAなどのハッシュになっているわけではありません。サーバーが自由に生成方式を選びます。「値が文字列なのでファイルのハッシュだ」と決めつけると、更新判定や再現実験の説明を誤ります。

先頭にW/が付く弱いETagも存在します。弱い比較は、表現のバイトが厳密に同一であるとまでは保証しません。キャッシュ再検証では弱い比較が関係する場面もありますが、ファイル完全一致を証明する用途には慎重な判断が必要です。今回の例で使った”rev-7″は仕組みを説明するための架空値であり、実在サイトの記録ではありません。

Last-Modifiedは、サーバーが示す最終変更日時です。クライアントはIf-Modified-Sinceに時刻を入れて条件付きGETを送ることがあります。しかし日時は精度や更新管理の影響を受けるため、ETagと完全に同じ情報ではありません。

両方の条件が要求に含まれる場合、RFC 9110はIf-None-Matchを優先し、If-Modified-Sinceを無視するよう定めています。二つの条件を足し合わせるという意味ではありません。Request Headersにどちらが付いているか確認し、何が判定材料になっているのかを区別しましょう。

仕組みクライアントの条件調べる対象
ETagIf-None-Match保存済み表現と識別子の比較
更新日時If-Modified-Since指定時刻以降の更新
304条件付きGET/HEADへの応答保存済み本文を再利用できるか

304なのにファイルが取得できない場合

一般的なブラウザーには、以前の応答を保存して再利用する仕組みがあります。しかし独自HTTPクライアントがIf-None-Matchを手動で付けた場合、サーバーから304を受け取っても、クライアント自身に保存済みの画像が存在しないことがあります。304には画像本体が含まれないため、その応答だけをファイルに保存しても本来の画像は復元できません。

これはサーバーが空ファイルを送ったという意味ではなく、手元に再利用する本文があるという前提が満たされていない問題です。キャッシュを持たないツールで条件付きGETだけ送っているなら、目的に応じて無条件GETで実体を取得する必要があります。再試行の方法はクライアントの仕様に合わせ、同じ条件付き要求を無制限に繰り返さないでください。

ブラウザーに表示される304の転送量が小さくても、ユーザーが受け取る画面は以前に保存したHTMLやCSSで構成されます。したがって「転送バイトが少ない」ことと「利用者にデータがない」ことは別です。操作に問題が起きている場合は、HTTPの番号だけでなく、キャッシュに保存された表現が選ばれているかを確認してください。

CDNやリバースプロキシがある構成では、ブラウザーと中継層、中継層とオリジンで別のキャッシュ判定が働く場合もあります。ブラウザーに304が見えても、それが必ずオリジンまで届いた要求の結果とは限りません。どこのNetworkやログを見ているのか、観測点を明確にすることが重要です。

no-cacheは保存禁止ではない

Cache-Controlのno-cacheは名前が紛らわしい設定です。no-cacheは保存を絶対に禁止する意味ではなく、保存済み応答を再利用する前に検証が必要であることを示します。 再検証の結果304になれば、保存済みの本文を利用する場合があります。no-cacheがあるから304は不可能、という理解は誤りです。

一方、no-storeは保存を禁じる指示です。これがある資源に対して保存済み本文による再検証を期待しても、思ったように304にならない場合があります。max-ageなどの条件で保存版がまだ新鮮なら、再検証通信を行わずそのまま再利用されることもあります。

条件代表的な動き誤解しやすい点
新鮮なキャッシュ通信せず再利用することがある304がなくても正常
no-cache再利用前に検証する保存禁止ではない
no-store応答を保存しないno-cacheとは別
検証が必要な保存版304または200になりうる必ず304とは限らない

ブラウザーの開発者ツールにあるDisable cacheは、こうしたサーバーのCache-Controlと同じ意味のHTTPヘッダーではありません。開発中の観察方法を変えるためのブラウザー側の機能です。「チェックを入れたら304が消えた」としても、サーバーのキャッシュ設定が故障したと断定できません。

CSSが更新されないときの切り分け方

更新したはずのCSSが古く見える場合、最初から全キャッシュ削除をすると原因の証拠が消えてしまうことがあります。まず対象ファイルのURL、Status、Request Headersの条件付きヘッダー、Response HeadersのETagとCache-Controlを記録します。バージョン付きURLなどを採用しているサイトでは、要求先が新しいURLに切り替わっているかも確認します。

次に、通信そのものが起きていないのか、304で保存版が再利用されているのか、200で更新版が返っているのかを分けます。通信がない場合はキャッシュが新鮮である可能性があります。304ならクライアントが提示した識別子とサーバーの現在の表現の対応を確認します。200であれば新しい本文を受け取っているか、CSSの適用順序や別ファイルの読み込みを調べます。

「304が返るのに画面が更新されない」という相談でも、まず304がどのファイルに対応するかを確かめます。HTMLは200でCSSだけ304なのか、画像やスクリプトが別のキャッシュ条件になっているのかで対処が変わります。全てを一括してキャッシュ障害と呼ぶより、資源ごとに判定したほうが問題の場所を特定できます。

一方、200が毎回返ることも正常です。そもそもキャッシュ保存をしない方針、条件付き要求がない、資源が更新されたなどの理由が考えられます。「304の割合を増やす」ことだけを改善目標にせず、正しい内容が適切なタイミングで表示されるか、不要な転送が少ないかを組み合わせて見ましょう。

運用監視では304をエラー扱いしない

HTTPステータスの分類で3xxはリダイレクト系として扱われますが、304は一般的なLocationによる別URLへの誘導とは用途が異なります。条件付きGET/HEADを受け取ったサーバーが、既存の表現を使えると伝えるための応答です。単純に「200以外は失敗」とする監視は正常な再検証を誤報する可能性があります。

ただし、APIクライアントがキャッシュを扱わず、常に完全なJSON本文を期待している場合は304が想定外となることもあります。そのときはサーバーを即時に責める前に、クライアントが条件付きヘッダーを送っていないか、取得時に保存済み本文を保持しているかを確認します。HTTPとして正常でも、アプリの実装条件が一致していなければ利用者には問題が生じます。

調査結果を共有するときは、ブラウザー・バージョン、資源の種類、キャッシュ無効化のオン・オフ、対象URLの機密性、観測した要求・応答ヘッダーを分けて記録すると再現しやすくなります。Cookie、認証トークン、内部ホスト名、個人情報を含む生ログはそのまま公開しないでください。

図解:304の応答に本文がなくても画面が表示される理由

HTTP 304の意味を理解するには、通信で新しく届いたものと以前に保存したものを分ける必要があります。たとえば画像を最初にGETしたときに200 OKと本文を受け取り、ETagとともに保存したとします。次回の表示でその保存版を使えるか確認するため、If-None-Matchを付けた条件付きGETを送ることがあります。

sequenceDiagram
    participant B as ブラウザー
    participant C as 保存済みキャッシュ
    participant S as Webサーバー
    B->>S: 初回GET
    S-->>B: 200と本文とETag
    B->>C: 本文と条件を保存
    B->>C: 保存版を利用できるか照合
    C-->>B: 再検証が必要
    B->>S: If-None-Matchを付けたGET
    S-->>B: 304 本文なし
    B->>C: 保存済み本文を再利用
    C-->>B: 画像やCSSを表示

図は仕組みを説明する架空の通信です。実際のブラウザー、キャッシュ、CDNでは経路が異なります。304を返すサーバーが新しい本文を送らないのは、利用できる保存版があることを前提とした応答だからです。ただし、保存版がない、選択した表現が違う、キャッシュが破損している場合まで正常表示を保証するものではありません。

通信なし・304・200の三つを見分ける

キャッシュに保存した資源がまだ新鮮なら、そもそもネットワークで問い合わせない場合があります。これは304の通信が発生した状態とは違います。304を観察するためだけに「キャッシュ無効化」や強制再読み込みを選ぶと、通常利用時とは条件が変わることにも注意します。

flowchart TD
    A["保存済み資源を表示したい"] --> B{"保存版を利用できるか"}
    B -->|"新鮮"| C["再検証通信なしで利用する場合"]
    B -->|"再検証が必要"| D["条件付きGETを送信"]
    B -->|"保存版なし"| E["通常GETで本文を取得"]
    D --> F{"サーバーの判定"}
    F -->|"変更なし"| G["304 保存済み本文を利用"]
    F -->|"変更あり"| H["200 新しい本文を取得"]

この図は判断の単純化で、実際にはCache-Control、Varyやブラウザーの実装条件にも左右されます。200が出たから問題があるのでも、304が出なかったからキャッシュが無効というわけでもありません。要求と応答のヘッダー、保存条件、再読み込み方法を一緒に見て判断します。

CSSだけ古いときの調査を具体化する

架空のサイトでCSSを更新したのに、一部の利用者だけ旧デザインが表示されるとします。最初に該当CSSのURLと、Networkの最終応答が通信なし、304、200のどれに当たるかを確認します。304なら要求のIf-None-MatchやIf-Modified-Sinceと、返されたETagなどの条件を比べます。200なら期待した新しい本文が返っているかを調べます。

ここで無条件にキャッシュを全削除すると、症状を再現する条件が失われる場合があります。まず発生時刻、URL、条件付きヘッダー、利用したブラウザーと再読み込み方法を記録します。プロキシやCDNがある場合は、どこで保存された資源が返ったのかも切り分けます。認証Cookieや署名付きURLなどの秘密情報を報告書へ貼り付けず、読取から原因を絞ってから設定変更の必要性を判断します。

まとめ:304は保存版の利用確認

304 Not Modifiedは、条件付きGET/HEADの結果、保存している表現を再利用できると判断されたことを示します。304自体に本文がないのは仕様どおりです。ETag、If-None-Match、Last-Modified、Cache-Control、保存済み本文の有無を併せて見れば、通信量を節約している状態と実際の不具合を区別しやすくなります。

公式資料

本記事は公式仕様と観察手順の解説であり、実通信ログを採取した再現レポートではありません。特定ブラウザーでの動作、CDNやWordPressでの実表示は未確認です。

文書情報

記事タイトル
HTTP 304はエラーではない:ETagとブラウザーキャッシュの再検証を読み解く
作成日
更新日
Source URL
https://papanda925.com/?p=18470

ライセンス: 本記事のうち、当サイトが権利を有する本文・自作図表は、特記なき限り CC BY 4.0 で利用できます。生成AIを活用して作成・編集した内容を含みます。コードについて、別途ライセンス表示またはリンク先GitHubリポジトリのライセンスがある場合は、その条件を優先します。引用・第三者資料・画像・商標等は本ライセンスの対象外です。 利用ポリシー

タイトルとURLをコピーしました