Conditional GET Requests with ETag and If-Modified-Since
Learn how to use conditional request headers like ETag, If-None-Match, and If-Modified-Since to optimize bandwidth and caching.
Detailed Explanation
What Are Conditional Requests?
Conditional requests allow clients to ask the server "has this resource changed since I last fetched it?" If it has not changed, the server returns 304 Not Modified with no body, saving bandwidth.
ETag-Based Conditionals
Step 1: Client fetches the resource and receives an ETag:
GET /api/users/42 HTTP/1.1
Host: api.example.com
HTTP/1.1 200 OK
ETag: "v3-abc123"
Content-Type: application/json
{ "id": 42, "name": "Alice", "version": 3 }
Step 2: Client sends a conditional request with If-None-Match:
GET /api/users/42 HTTP/1.1
If-None-Match: "v3-abc123"
Step 3: Server checks if the ETag matches:
HTTP/1.1 304 Not Modified
ETag: "v3-abc123"
No body is transferred — the client uses its cached copy.
Date-Based Conditionals
GET /api/reports/daily HTTP/1.1
If-Modified-Since: Thu, 15 Jan 2026 10:00:00 GMT
If the resource has not changed since the specified date:
HTTP/1.1 304 Not Modified
Last-Modified: Thu, 15 Jan 2026 10:00:00 GMT
Conditional PUT for Conflict Prevention
ETags are also used with unsafe methods to prevent lost updates:
PUT /api/users/42 HTTP/1.1
If-Match: "v3-abc123"
Content-Type: application/json
{ "name": "Alice Updated", "version": 4 }
If another client has modified the resource (ETag no longer matches):
HTTP/1.1 412 Precondition Failed
This is the HTTP way of implementing optimistic concurrency control.
Conditional Header Summary
| Header | Used With | Purpose |
|---|---|---|
If-None-Match |
GET, HEAD | Skip download if ETag matches |
If-Modified-Since |
GET, HEAD | Skip download if not changed since date |
If-Match |
PUT, PATCH, DELETE | Prevent update if resource changed |
If-Unmodified-Since |
PUT, PATCH, DELETE | Prevent update if modified since date |
Use Case
A news reader app checks for article updates every 5 minutes using conditional GET with If-None-Match. Most requests return 304 (no change), consuming minimal bandwidth. Only when an article is actually updated does the full response body transfer.