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:

  1. If-Match: if present and fails, return 412. If absent, proceed.
  2. If-Unmodified-Since: if present, If-Match is absent, and the condition fails, return 412. If absent or If-Match was present, proceed.
  3. If-None-Match: if present and fails, return 304 for GET/HEAD or 412 for other methods. If absent, proceed.
  4. 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.
  5. 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

Last updated: March 11, 2026