Strong vs Weak ETags Explained

A response carrying ETag: "abc123" and one carrying ETag: W/"abc123" look nearly identical, but they authorize different operations. Sending a Range request against a resource whose validator turns out to be weak — perhaps because a CDN silently downgraded it — produces a full 200 OK response instead of the 206 Partial Content a client expected, breaking resumable downloads and video seeking without any obvious error message.

Prerequisite Concepts

Step-by-Step Resolution

Why this distinction matters in practice

A video player resuming a paused download, a download manager retrying a partial transfer, and a CDN edge validating a cached object all rely on the same header but ask two structurally different questions of it: “has anything at all changed since I last saw this?” (weak comparison is fine) versus “is this exact byte range, at this exact offset, still valid to splice into the copy I already have on disk?” (only strong comparison is safe). Conflating the two is how resumable downloads silently restart from zero, or how a video seek bar jumps to a corrupted frame after an edge node quietly rewrote the validator during compression.

Step 1 — Inspect the raw ETag value and identify its strength

curl -sI https://example.com/video.mp4 | grep -i etag

Two possible shapes:

ETag: "f9e2c7ab114d"
ETag: W/"f9e2c7ab114d"

The literal two-character prefix W/ immediately preceding the opening quote is the only signal. There is no separate header or flag — strength is encoded entirely in this syntax. A response can also validly omit ETag and rely solely on Last-Modified, but that’s a separate validator entirely.

Step 2 — Understand what each form promises

  • Strong validator ("f9e2c7ab114d") — asserts byte-for-byte identity. Any two responses sharing this value are guaranteed to be octet-for-octet the same representation, including headers that affect the body’s interpretation.
  • Weak validator (W/"f9e2c7ab114d") — asserts only that the two responses are semantically equivalent for the purpose the client cares about (e.g., “renders the same page,” not “identical whitespace”). RFC 9110 §8.8.3.1 leaves the exact semantic-equivalence boundary to the origin server’s judgment.

Step 3 — Test weak comparison: ordinary conditional GET

Weak comparison considers two validators equal if their opaque strings match after stripping any W/ prefix from either side. This is the comparison function used for If-None-Match on a plain GET:

curl -sI -H 'If-None-Match: W/"f9e2c7ab114d"' https://example.com/video.mp4

Expected response:

HTTP/1.1 304 Not Modified

This succeeds whether the stored validator was itself strong or weak — weak comparison is lenient in both directions for this operation.

Step 4 — Test strong comparison: Range and If-Range requests

Strong comparison requires both validators to be strong (no W/ prefix on either) and identical. RFC 9110 §13.1.5 mandates strong comparison for If-Range, and by extension for any byte-range validation:

curl -sI -H 'Range: bytes=0-1023' -H 'If-Range: W/"f9e2c7ab114d"' https://example.com/video.mp4

Because the supplied validator is weak, a spec-compliant server must not honor the range and instead returns the full body:

HTTP/1.1 200 OK
Content-Length: 8493021

Re-run with the strong form of the same identifier:

curl -sI -H 'Range: bytes=0-1023' -H 'If-Range: "f9e2c7ab114d"' https://example.com/video.mp4
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/8493021
Content-Length: 1024

Step 5 — Detect a proxy or CDN downgrading strong to weak

Compare the validator seen directly at the origin against what a client sees through the CDN:

# Directly at origin (bypass CDN, e.g., via /etc/hosts or --resolve)
curl -sI --resolve example.com:443:203.0.113.10 https://example.com/style.css | grep -i etag

# Through the CDN
curl -sI https://example.com/style.css | grep -i etag

If the origin returns a strong ETag: "d41d8cd98f00" but the CDN-fronted response returns ETag: W/"d41d8cd98f00", the CDN is downgrading the validator — almost always because it applied gzip or Brotli compression to the response body without re-hashing it from the compressed bytes. This is standard, correct behavior: the compressed bytes are not identical to what the origin’s hash described, so continuing to advertise a strong guarantee would be incorrect.

Expected Output / Verification

  • A weak validator (W/"...") satisfies If-None-Match conditional GET requests and returns 304 Not Modified on match.
  • The same weak validator, when placed in If-Range, must be rejected by a compliant server, which falls back to a full 200 OK response rather than 206 Partial Content.
  • A strong validator in If-Range on an unchanged resource returns 206 Partial Content with a Content-Range header matching the requested byte range.
  • Comparing origin and CDN responses for the same resource reveals whether an intermediary is silently converting strong validators to weak ones — expected whenever dynamic compression is active.

Edge Cases

  • Chunked or streamed responses — servers that stream a body without knowing its final length up front frequently can only emit a weak validator, since a true strong hash requires the complete byte sequence in hand before responding.
  • HTTP/1.1 vs HTTP/2 — the strong/weak distinction and both comparison functions are defined at the semantic layer and behave identically regardless of the underlying transport version; HTTP/2 multiplexing does not change validator strength.
  • Missing Accept-Ranges alongside a strong ETag — a strong validator alone does not guarantee range support; the server must also advertise Accept-Ranges: bytes for Range/If-Range requests to be meaningful at all.
  • Re-quoting by intermediaries — some proxies rewrite header whitespace or quoting style when relaying ETag, which can accidentally strip or duplicate the W/ prefix. Always verify the exact byte sequence of the header as delivered to the client, not just as emitted by the origin.
  • Application frameworks that weaken by default — several web frameworks emit weak validators automatically for any dynamically rendered response, on the theory that byte-for-byte identity across renders is rarely achievable or meaningful. If you need strong-validator behavior for range support on generated content (e.g., a dynamically assembled archive download), you must opt in explicitly and guarantee a stable, complete-body hash.

Frequently Asked Questions

Why does a weak ETag start with W/?

The W/ prefix is a literal syntax marker defined by RFC 9110 that tells any recipient this validator only asserts semantic equivalence, not byte-for-byte identity, and must therefore only be used with weak comparison.

Can a weak ETag ever satisfy a Range request?

No. RFC 9110 requires strong comparison for If-Range and byte-range validation. A server receiving a Range request with a weak validator in If-Range must ignore the conditional and return the full representation instead of a 206 Partial Content response.

Why did my strong ETag become weak after passing through a CDN?

Most CDNs and reverse proxies that apply on-the-fly gzip or Brotli compression alter the response bytes without re-hashing them from scratch. To remain correct, they either strip the strong validator or prefix it with W/, since the compressed bytes are no longer identical to what the original hash described.

Does a weak ETag still prevent unnecessary bandwidth use?

Yes. A weak ETag works perfectly for ordinary conditional GET revalidation using If-None-Match, which only requires weak comparison. It saves the same response body transfer as a strong ETag would for a normal cache-hit check.


Back to ETag Generation and Validation Strategies