Revalidate without re-downloading
Conditional requests let a client ask “has this changed?” and get back a tiny
304 Not Modified instead of the whole body. They also power optimistic
concurrency, so two clients don’t silently overwrite each other. This reference
covers the validator headers, the strong-versus-weak comparison rules, and the
304 flow — plus a comparator for two ETags.
The full set of conditional request headers
| Header | Direction | Paired validator | Semantics |
|---|---|---|---|
ETag | Response | — | Opaque identifier for this version of the representation |
Last-Modified | Response | — | Date-time the resource was last changed |
If-None-Match | Request | ETag | ”Give me the resource only if its ETag doesn’t match any of these” |
If-Modified-Since | Request | Last-Modified | ”Give me the resource only if it changed after this date” |
If-Match | Request | ETag | ”Only apply this write if the ETag matches” |
If-Unmodified-Since | Request | Last-Modified | ”Only apply this write if the resource hasn’t changed since this date” |
If-Range | Request | ETag or date | ”Resume a range request only if the resource hasn’t changed” |
How it works in the cache revalidation flow
A response advertises validators:
ETag: "33a64df5"
Last-Modified: Tue, 10 Jun 2026 12:00:00 GMT
The client echoes them on the next request:
If-None-Match: "33a64df5"
If-Modified-Since: Tue, 10 Jun 2026 12:00:00 GMT
If the resource is unchanged, the server replies 304 Not Modified with no body — the client reuses its cached copy. If the resource changed, the server returns 200 OK with the fresh content and new validators.
Strong vs weak ETag comparison
The ETag comparison mode determines which operations succeed:
Strong comparison requires both ETags to be present, neither to carry the W/ prefix, and the values to be identical. It is used by:
- Range requests (
If-Range) - Assembling a complete response from parts
Weak comparison permits W/"x" and "x" to match. It is used by:
If-None-Matchfor cache revalidation- General conditional reads
A response served with compression may produce a different byte sequence than the uncompressed version, which is why servers sometimes use weak ETags for content-negotiated responses where the underlying content is the same but the encoding varies.
Optimistic concurrency for writes
The classic lost-update problem: two clients read a resource, both make edits, and the second write silently overwrites the first. Conditional writes with ETags solve this:
- Client A reads the resource and receives
ETag: "v1". - Client B also reads and receives
ETag: "v1". - Client A writes with
If-Match: "v1"— succeeds, server now hasETag: "v2". - Client B tries to write with
If-Match: "v1"— fails with412 Precondition Failedbecause the current ETag is"v2". - Client B must re-read, merge the changes, and try again with the new ETag.
This makes writes safe without requiring server-side locking.
Generating good ETags
- Content hash (MD5, SHA-1 of body): perfectly precise; changes if and only if content changes. Requires computing a hash on every response, which has CPU cost.
- Version counter or UUID: simple to implement; requires incrementing reliably on every change.
- Last-modified timestamp in milliseconds: simple, but loses precision if two changes happen within one millisecond and the clock resolution is 1 ms.
Prefer content hashing for static assets; version counters or UUIDs for database-backed resources.
Tips
- Prefer strong ETags unless your representation legitimately changes byte-for-byte between equivalent versions (for example due to compression or serialisation variance).
- ETag beats
Last-Modifiedfor precision: files can be modified without changing their content (atouch), and file system timestamps are often limited to 1-second resolution. - Use
If-None-Match: *to ensure a resource does not already exist before creating it (returns412if it does). - A
304response should still carryETag,Cache-Control,Vary, andExpiresso the client knows when to revalidate again.