HTTP Conditional Requests
Conditional requests are HTTP requests whose outcome depends on comparing a validator against the current state of the target resource. The server evaluates the condition before processing the request. If the condition fails, the server returns a short response (typically 304 or 412) instead of performing the full operation.
Two primary use cases drive conditional requests: cache validation, where a client checks whether a stored response is still fresh, and optimistic concurrency control, where a client ensures a resource has not changed before applying an update.
Validators
A validator is metadata the server attaches to a response so future requests have something to compare against. HTTP defines two validator types.
ETag
An ETag (entity tag) is an opaque string identifying a specific version of a resource. The server generates the tag and includes the value in the response.
ETag: "a1b2c3d4"
ETags are strong validators by default, meaning
they guarantee byte-for-byte equivalence between
representations. A weak ETag, prefixed with W/,
signals semantic equivalence without guaranteeing
identical bytes.
ETag: W/"a1b2c3d4"
Strong comparison requires both tags to be strong
(not weak-tagged) and their opaque-tag strings to
match exactly. Weak comparison ignores the W/
prefix and matches on the opaque-tag portion alone. Different
conditional headers use different comparison
functions.
| Header | Comparison |
|---|---|
| If-Match | if-Match |
| If-None-Match | if-None-Match |
| If-Range | if-Range |
Last-Modified
The Last-Modified header carries the date and time the server believes the resource was last changed.
Last-Modified: Tue, 15 Oct 2024 08:30:00 GMT
Last-Modified dates follow the IMF-fixdate format:
<day-name>, DD MMM YYYY hh:mm:ss GMT
<day-name> is one of Mon, Tue, Wed, Thu, Fri,
Sat, or Sun. DD is the zero-padded two-digit day.
MMM is the three-letter month abbreviation. YYYY
is the four-digit year. hh:mm:ss is the time in
24-hour format. All HTTP dates use GMT.
Day-name and month are case-sensitive
The day-name and month abbreviation fields are
case-sensitive. Tue is valid.
tue and TUE are not.
ETags are generally more reliable than Last-Modified dates. A file rewritten with identical content gets a new Last-Modified date but the same ETag. Last-Modified has one-second resolution, so rapid changes within the same second are invisible. Servers generating both validators give clients the best of both mechanisms.
Conditional request headers
If-None-Match
If-None-Match succeeds when none of the listed ETags match the current resource. This is the standard cache validation header for GET and HEAD requests.
If-None-Match: "a1b2c3d4"
When the ETag matches (the resource has not changed), the server returns 304 Not Modified with no body. The client reuses the cached representation. When the ETag does not match, the server returns the full response.
The wildcard value * matches any current
representation of the resource.
If-Modified-Since
If-Modified-Since succeeds when the resource has been modified after the specified date. This is the date-based equivalent of If-None-Match.
If-Modified-Since: Tue, 15 Oct 2024 08:30:00 GMT
If the resource has not changed since the given date, the server returns 304. This header applies only to GET and HEAD requests. When both If-None-Match and If-Modified-Since are present, If-Modified-Since is ignored (If-None-Match takes precedence).
If-Match
If-Match succeeds when at least one of the listed ETags matches the current resource. This header is used for safe updates and range requests.
If-Match: "a1b2c3d4"
When the condition fails (the resource has changed), the server returns 412 Precondition Failed without applying the request. This prevents overwriting changes made by another client.
If-Unmodified-Since
If-Unmodified-Since succeeds when the resource has not been modified after the specified date. This is the date-based equivalent of If-Match.
If-Unmodified-Since: Tue, 15 Oct 2024 08:30:00 GMT
When the resource has been modified, the server returns 412. When both If-Match and If-Unmodified-Since are present, If-Unmodified-Since is ignored.
If-Range
If-Range is a conditional header specific to range requests, accepting either an ETag or a Last-Modified date. If the validator matches, the server returns 206 with the requested range. If the resource changed, the server returns 200 with the full representation.
If-Range: "a1b2c3d4"
This header combines validation and range selection in a single round-trip, making this the preferred mechanism for resumable downloads.
Precondition evaluation order
The specification defines the order in which a server evaluates precondition headers. When multiple conditional headers appear in the same request, the server follows this sequence:
- If-Match: if present and fails, return 412. If absent, proceed.
- If-Unmodified-Since: if present, If-Match is absent, and the condition fails, return 412. If absent or If-Match was present, proceed.
- If-None-Match: if present and fails, return 304 for GET/HEAD or 412 for other methods. If absent, proceed.
- If-Modified-Since: if present, If-None-Match is absent, the method is GET or HEAD, and the condition fails, return 304. If absent, proceed.
- If-Range: if present with a Range header and the condition fails, ignore the Range header and return 200 with the full resource.
This ordering gives state-changing preconditions (If-Match, If-Unmodified-Since) priority over cache validation preconditions (If-None-Match, If-Modified-Since). A PUT request with both If-Match and If-None-Match evaluates If-Match first, ensuring the lost update check runs before any cache logic.
Cache validation
The most common use of conditional requests is cache revalidation. A client stores a response along with its validator (ETag, Last-Modified, or both). When the cached entry goes stale, the client sends a conditional GET to check whether the resource changed.
Initial request and response
GET /news.html HTTP/1.1
Host: www.example.re
HTTP/1.1 200 OK
ETag: "v1001"
Last-Modified: Mon, 14 Oct 2024 10:00:00 GMT
Content-Type: text/html
Content-Length: 1250
(page content)
Revalidation when the resource has not changed
GET /news.html HTTP/1.1
Host: www.example.re
If-None-Match: "v1001"
HTTP/1.1 304 Not Modified
ETag: "v1001"
The 304 response has no body. The client reuses the cached representation, saving bandwidth and server processing.
Revalidation when the resource has changed
GET /news.html HTTP/1.1
Host: www.example.re
If-None-Match: "v1001"
HTTP/1.1 200 OK
ETag: "v1002"
Content-Type: text/html
Content-Length: 1500
(updated page content)
The server returns the new representation with the updated ETag. The client replaces the cached entry.
Optimistic concurrency control
Conditional requests prevent the lost update problem when multiple clients edit the same resource. The pattern uses If-Match with PUT or PATCH to ensure the client is modifying the version last read.
Client A reads the resource
GET /config.json HTTP/1.1
Host: api.example.re
HTTP/1.1 200 OK
ETag: "cfg-42"
Content-Type: application/json
{"timeout": 30, "retries": 3}
Client A updates the resource
PUT /config.json HTTP/1.1
Host: api.example.re
If-Match: "cfg-42"
Content-Type: application/json
{"timeout": 60, "retries": 3}
If no other client modified the resource, the server applies the update and returns a new ETag:
HTTP/1.1 200 OK
ETag: "cfg-43"
If Client B modified the resource between the GET and the PUT, the ETag no longer matches and the server rejects the update:
HTTP/1.1 412 Precondition Failed
Client A then re-reads the resource to get the current state (and current ETag) before retrying the update. This pattern avoids silent data loss from concurrent edits.
ETags for concurrency
ETag-based concurrency control is more reliable than If-Unmodified-Since because Last-Modified dates have one-second resolution. Two rapid updates within the same second produce the same Last-Modified value, and the second update silently overwrites the first.
Takeaway
Conditional requests make HTTP operations state-aware by evaluating validators before processing. If-None-Match and If-Modified-Since power cache revalidation, returning 304 when nothing changed. If-Match and If-Unmodified-Since prevent lost updates by returning 412 when the resource changed unexpectedly. If-Range combines validation with range requests for resumable downloads.
See also
- RFC 9110: Conditional Requests
- Caching
- Range Requests
- ETag
- Last-Modified
- If-Match
- If-None-Match
- If-Modified-Since
- If-Unmodified-Since
- If-Range
- 304
- 412
- HTTP headers