#はじめに
「キャッシュの有効期限が切れたけど、リソースは変更されていない」——この場合、同じデータを再度ダウンロードするのは無駄です。
条件付きリクエストは、「変更されている場合のみ新しいデータを送って」とサーバーに依頼する仕組みです。変更がなければ、ボディなしの軽量レスポンスで済みます。
この記事を読むと、以下のことができるようになります:
- 条件付きリクエストの仕組みを理解できる
- 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の比較
| 観点 | ETag | Last-Modified |
|---|---|---|
| 精度 | 高い(内容ベース) | 低い(秒単位の時刻) |
| 生成コスト | 高い(ハッシュ計算) | 低い(ファイル日時) |
| 分散環境 | 一貫性に注意が必要 | 時刻同期が必要 |
#使い分け
- ETag推奨: 動的コンテンツ、厳密な検証が必要な場合
- Last-Modified推奨: 静的ファイル、シンプルな実装
両方を使用することも可能です。その場合、ETagが優先されます。
#304 Not Modified
#意味
304 Not Modifiedは、「リソースは変更されていないので、キャッシュを使用してください」という意味のステータスコードです。
#304レスポンスに含まれるヘッダー
ボディは含まれませんが、以下のヘッダーが含まれる可能性があります:
Cache-Control(更新される場合がある)ETagExpiresVary
HTTP/1.1 304 Not Modified
ETag: "abc123"
Cache-Control: max-age=3600
#キャッシュの更新
304レスポンスを受け取ると、ブラウザは:
- キャッシュされたレスポンスを使用
- ヘッダー情報を304のものに更新
- 有効期限をリセット
#DevToolsでの確認
#304レスポンスの確認
- DevToolsのNetworkタブを開く
- ページをリロード(ハードリロードではなく通常のリロード)
- 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はエッジサーバーでコンテンツをキャッシュし、ユーザーに近い場所から配信することで、さらなる高速化を実現します。