skip to content

What is the difference between a strong HTTP ETag and a weak one written as W/"abc", and where does that distinction actually change server behaviour?

level: middleimportance: must knowfreq 45%

answer

  1. W/ = semantic equivalence, no W/ = byte-identical
  2. conditional GET = weak comparison, still 304s
  3. If-Match and If-Range = strong comparison
  4. weak tag matches nothing under strong comparison
  5. proxies weaken tags when they compress

basics

~20 s

A strong ETag promises byte-for-byte identity of the representation; W/ marks a weak one promising only semantic equivalence. Conditional GET uses weak comparison, so both produce 304. Range requests and write preconditions like If-Match use strong comparison, where a weak tag never matches.

solid answer

~60 s

A **strong** validator changes whenever a single byte of the representation changes. A **weak** one, prefixed `W/`, changes only when the meaning changes - so a page whose embedded timestamp or ad slot differs can keep the same weak tag even though the bytes differ. The distinction is not decoration; it selects a comparison function. - **Weak comparison** treats `W/"v7"` and `"v7"` as matching. It is used for `If-None-Match` on a conditional GET, because "good enough to reuse" is exactly the semantic-equivalence question. - **Strong comparison** requires both tags to be strong and byte-identical. It is used where the client is doing something that depends on the exact bytes: `If-Match` on a write, and `If-Range` on a byte-range request. So a weak tag still gets you 304s, but it cannot guard an update, and it cannot underpin a resumed download - if you send only weak tags, `If-Match` always fails and range requests fall back to full transfers. Use weak tags when producing an exact byte-stable tag is impractical, strong ones when the resource is written concurrently or served in ranges.

code

http · 10 lines
http
GET /report HTTP/1.1
If-None-Match: W/"r19"

HTTP/1.1 304 Not Modified
ETag: W/"r19"

PUT /report HTTP/1.1
If-Match: W/"r19"

HTTP/1.1 412 Precondition Failed

go deeper

for a junior

Know that W/ means weak, that weak promises equivalent-not-identical, and that conditional GET still works with it.

for a middle

Name the two comparison functions and map them onto If-None-Match, If-Match and If-Range.

for a senior

Choose tag strength from the resource's needs - concurrent writes, range serving - and watch for proxies weakening tags on compression.

for a principal

Treat validator strength as an API guarantee: what the platform promises about byte stability, and which capabilities (resumable downloads, optimistic concurrency) that promise unlocks.

## What the two forms promise An entity tag is an opaque identifier for a representation. The `W/` prefix qualifies the strength of that identification. ``` ETag: "33a64df5" strong: identifies these exact bytes ETag: W/"33a64df5" weak: identifies this content up to semantic equivalence ``` A **strong** validator guarantees that two representations sharing the tag are octet-for-octet identical. A **weak** validator guarantees only that they are *equivalent for the purpose of reuse* - a client may substitute one for the other without the user noticing anything meaningful. Why would you ever want the weaker promise? Because exact byte stability is often expensive or impossible: - A dynamically rendered page whose footer prints "generated at 09:14:22" produces different bytes each render, though nothing meaningful changed. - A response assembled from fragments where whitespace or ordering is not deterministic. - A large object whose full-content hash is too costly to compute per request, so the server derives a tag from a version counter that tracks meaning rather than bytes. Without weak tags, all of these would emit a new tag on every request, and revalidation would never produce a 304. ## Two comparison functions The strength matters because HTTP defines two ways of comparing entity tags. **Weak comparison**: the tags match if their opaque values are equal, regardless of whether either is weak. `W/"1"` matches `"1"`; `W/"1"` matches `W/"1"`; `"1"` does not match `"2"`. **Strong comparison**: the tags match only if the opaque values are equal **and neither is weak**. `"1"` matches `"1"`; `W/"1"` matches nothing at all, not even itself. HTTP then assigns each conditional header a comparison function: | Header | Comparison | Consequence for a weak tag | | --- | --- | --- | | `If-None-Match` (conditional GET) | weak | works; 304 is produced normally | | `If-Match` (write precondition) | strong | never matches; the write is refused with 412 | | `If-Range` (conditional range) | strong | never matches; server returns the full 200 instead of a 206 partial | ## Why writes and ranges demand strength These two cases are not arbitrary. For a **range request**, the client already holds bytes 0-999 and asks for 1000-1999 to be stitched onto them. If the underlying representation changed by even one byte, the concatenation is corrupt. Semantic equivalence is worthless here: the client needs the *same bytes*, so `If-Range` uses strong comparison, and a weak tag makes the server safely return the whole entity rather than a splice. For a **write precondition**, `If-Match` asserts "the resource is still exactly the version I read and edited". A weak tag admits the possibility that the content differs in ways the server considers unimportant but the client's edit was computed against - so it cannot support optimistic concurrency, and strong comparison rejects it. A conditional GET is the opposite situation: the only question is whether a stored copy remains good enough to serve, which is precisely the semantic-equivalence question, so weak comparison is correct. ## Practical guidance Decide by what the resource must support. If the resource is **written concurrently** and you rely on `If-Match`/412 for lost-update protection, you need strong tags - typically a database row version or a content hash. Emitting weak tags there silently breaks every conditional write, which surfaces as "every update gets 412" or, worse, as a team disabling preconditions. If the resource is **large and range-served** - video, big downloads, PDFs opened progressively - you need strong tags or you lose resumable and partial fetches. If the resource is **read-mostly dynamic HTML or an aggregate JSON view** whose bytes wobble for uninteresting reasons, weak tags are the honest choice and still deliver the full bandwidth saving. One implementation trap: some servers and proxies automatically weaken tags when they transform a response - most commonly when compressing it, because the tag was computed on the uncompressed body and no longer identifies the bytes on the wire. If your writes start failing after enabling gzip at a proxy, this is the first thing to check, alongside whether the encoding variants are being distinguished at all.

  • Why does If-Range require strong comparison?
    Because the client is going to splice the returned byte range onto bytes it already holds. If the representation changed at all, even in a way the server considers semantically irrelevant, the concatenated result is corrupt. Strong comparison forces a mismatch on any weak tag, and the server then returns the complete representation with 200 rather than a 206 partial.
  • When is a weak ETag the right choice rather than a compromise?
    When the representation's bytes vary for reasons that carry no meaning - a rendered timestamp, non-deterministic ordering, a rotating banner - so a strong tag would change on every request and no revalidation would ever produce a 304. A weak tag tells the truth about what the server can promise, and conditional GET, which uses weak comparison, still delivers the full bandwidth saving.

saying these in an interview costs you the question

  • Believing weak ETags cannot produce a 304
  • Thinking W/ is a hint the client may ignore rather than a change of comparison function
  • Expecting If-Match with a weak tag to succeed when the content is unchanged
  • Assuming the strength affects only performance, not correctness of ranges
  • Emitting weak tags on a resource that relies on conditional writes and then blaming the client for 412s

context