#はじめに

「キャッシュの有効期限が切れたけど、リソースは変更されていない」——この場合、同じデータを再度ダウンロードするのは無駄です。

条件付きリクエストは、「変更されている場合のみ新しいデータを送って」とサーバーに依頼する仕組みです。変更がなければ、ボディなしの軽量レスポンスで済みます。

この記事を読むと、以下のことができるようになります:

  • 条件付きリクエストの仕組みを理解できる
  • ETagとLast-Modifiedの違いと使い分けがわかる
  • 304 Not Modifiedの意味がわかる

#条件付きリクエストとは

条件付きリクエストとは、「指定した条件を満たす場合のみ」レスポンスを返すよう依頼するリクエストです。

キャッシュの再検証では、「前回取得したものと変わっていなければ、ボディは不要」という条件を使います。

[ブラウザ]                           [サーバー]
    |                                    |
    |------ GET /image.png ------------->|
    |       If-None-Match: "abc123"      |
    |       "このETagと同じなら不要"        |
    |                                    |
    |                    変更なし → 304 返す
    |                    変更あり → 200 + 新データ
    |                                    |
    |<----- 304 Not Modified ------------|
    |       (ボディなし)                   |

#ETagによる検証

#ETagとは

ETag(Entity Tag)は、リソースの特定バージョンを識別するための文字列です。

ETag: "abc123def456"

ファイルの内容から生成されるハッシュ値や、バージョン番号などが使われます。

#検証フロー

#1. 初回リクエスト

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

HTTP/1.1 200 OK
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Cache-Control: max-age=3600
Content-Type: text/css

.header { ... }

ブラウザはレスポンスとETagをキャッシュに保存します。

#2. 再検証リクエスト(キャッシュ期限切れ後)

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

If-None-Matchヘッダーで、キャッシュしているETagを送信します。

#3. サーバーの応答

変更なしの場合:

HTTP/1.1 304 Not Modified
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Cache-Control: max-age=3600

ボディは含まれません。ブラウザはキャッシュを使用します。

変更ありの場合:

HTTP/1.1 200 OK
ETag: "新しいハッシュ値"
Cache-Control: max-age=3600
Content-Type: text/css

.header { /* 新しい内容 */ }

新しいデータが返され、キャッシュが更新されます。

#強いETagと弱いETag

# 強いETag(デフォルト)
ETag: "abc123"

# 弱いETag
ETag: W/"abc123"
種類意味比較
強いETagバイト単位で同一厳密
弱いETag意味的に同等緩い

弱いETagは、内容が微妙に異なっていても「同等」とみなす場合に使用します(例: 空白の違いのみ)。

#Last-Modifiedによる検証

#Last-Modifiedとは

Last-Modifiedは、リソースが最後に変更された日時を示すヘッダーです。

Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT

#検証フロー

#1. 初回リクエスト

HTTP/1.1 200 OK
Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT
Cache-Control: max-age=3600

#2. 再検証リクエスト

GET /style.css HTTP/1.1
If-Modified-Since: Wed, 21 Oct 2023 07:28:00 GMT

If-Modified-Sinceヘッダーで、キャッシュの更新日時を送信します。

#3. サーバーの応答

指定日時以降に変更がなければ304 Not Modified、あれば200 OKと新データを返します。

#ETagとLast-Modifiedの比較

観点ETagLast-Modified
精度高い(内容ベース)低い(秒単位の時刻)
生成コスト高い(ハッシュ計算)低い(ファイル日時)
分散環境一貫性に注意が必要時刻同期が必要

#使い分け

  • ETag推奨: 動的コンテンツ、厳密な検証が必要な場合
  • Last-Modified推奨: 静的ファイル、シンプルな実装

両方を使用することも可能です。その場合、ETagが優先されます。

#304 Not Modified

#意味

304 Not Modifiedは、「リソースは変更されていないので、キャッシュを使用してください」という意味のステータスコードです。

#304レスポンスに含まれるヘッダー

ボディは含まれませんが、以下のヘッダーが含まれる可能性があります:

  • Cache-Control(更新される場合がある)
  • ETag
  • Expires
  • Vary
HTTP/1.1 304 Not Modified
ETag: "abc123"
Cache-Control: max-age=3600

#キャッシュの更新

304レスポンスを受け取ると、ブラウザは:

  1. キャッシュされたレスポンスを使用
  2. ヘッダー情報を304のものに更新
  3. 有効期限をリセット

#DevToolsでの確認

#304レスポンスの確認

  1. DevToolsのNetworkタブを開く
  2. ページをリロード(ハードリロードではなく通常のリロード)
  3. Statusが「304」のリクエストを探す

#リクエストヘッダーの確認

304レスポンスになったリクエストのヘッダーを見ると、条件付きヘッダーが含まれています:

If-None-Match: "abc123"
If-Modified-Since: Wed, 21 Oct 2023 07:28:00 GMT

#サイズの比較

304レスポンスはボディがないため、サイズが非常に小さくなります。

# 初回リクエスト
/style.css    200    50 KB

# 再検証リクエスト
/style.css    304    200 B  ← 大幅に削減

#条件付きリクエストが使われる場面

#1. キャッシュの再検証

max-ageが切れた後、no-cache指定時など。

#2. ブラウザのリロード

通常のリロード(F5)では、条件付きリクエストが送信されます。

#3. ハードリロード

ハードリロード(Ctrl+Shift+R)では、条件付きヘッダーは送信されず、強制的に新しいデータを取得します。

#サーバー実装のポイント

#ETagの生成

import hashlib

def generate_etag(content):
    return hashlib.md5(content.encode()).hexdigest()

#検証ロジック

def handle_request(request):
    current_etag = generate_etag(get_content())

    if_none_match = request.headers.get('If-None-Match')

    if if_none_match == current_etag:
        return Response(status=304, headers={'ETag': current_etag})

    return Response(
        body=get_content(),
        headers={'ETag': current_etag}
    )

#まとめ

  • 条件付きリクエストは「変更がなければデータ不要」と伝える仕組み
  • ETag: リソースの識別子(内容ベース、精度高い)
  • Last-Modified: 最終更新日時(シンプル、精度低い)
  • If-None-Match / If-Modified-Since: 条件付きリクエストヘッダー
  • 304 Not Modified: 変更なし、キャッシュを使用してよい
  • 帯域節約と高速化に効果的

#次のステップ

条件付きリクエストを理解したところで、次はCDNキャッシュについて学びましょう。CDNはエッジサーバーでコンテンツをキャッシュし、ユーザーに近い場所から配信することで、さらなる高速化を実現します。

#参考リンク