TL;DR: Use ETag to identify a representation version and Last-Modified when you have a trustworthy modification date; send both when available. ETag can distinguish changes within one second. If a request sends both
If-None-MatchandIf-Modified-Since, the ETag condition wins.
Side By Side
| Property | ETag | Last-Modified |
|---|---|---|
| Wire value | Quoted opaque tag: "release-17" or W/"release-17" | HTTP date: Mon, 05 Oct 2026 09:00:00 GMT |
| Generation | Implementation-defined; hash or revision are options | Time the origin believes the representation last changed |
| Resolution | Can distinguish every representation change | One second on the wire |
| Strength | Strong without W/; weak with W/ | Implicitly weak unless strength can be established |
| Revalidation request | If-None-Match | If-Modified-Since |
| Main deployment trap | Different nodes generate different tags for identical content | Deployment timestamps change although content did not |
See MDN’s ETag and Last-Modified references. The timestamp resolution and conditional strength rules are in RFC 9110 §8.8.
When To Use Each
ETag: a response with an explicit version. An API that edits an article several times within a second needs more than a second-resolution date. Derive a tag from the rendered representation, or use a revision that changes whenever that representation changes. Do not call a database row revision strong if the rendered response can change independently of that row.
Last-Modified: a response with a meaningful timestamp. A static document or article with a maintained modification time can expose that date. Do not substitute the current request time: that reports a new modification on every response. MDN describes it as a fallback validator, not an exact content fingerprint.
For either strategy, test an unchanged resource, an actual edit, and a redeploy without an edit. The first and third should not generate a new validator merely because a different worker answered.
Strong And Weak Are Different Promises
ETag: "release-17" promises byte identity for matching strong tags. ETag: W/"release-17" permits representations the server considers equivalent even when bytes differ. The string is opaque; there is no required hash algorithm. MDN documents these formats and generation choices.
Weak tags work for If-None-Match revalidation. They do not provide a matching version for If-Match, and clients must not put them in If-Range. A modification date is implicitly weak, but can be strong under the rules in RFC 9110 §8.8.2.2; “Last-Modified is always weak” is too broad. See §13.1 for the conditional comparison rules.
When Both Conditions Arrive
Illustrative exchange for an unchanged representation:
GET /article.json HTTP/1.1
Host: example.com
If-None-Match: "release-17"
If-Modified-Since: Mon, 05 Oct 2026 09:00:00 GMT
HTTP/1.1 304 Not Modified
Date: Mon, 05 Oct 2026 09:05:00 GMT
ETag: "release-17"
Cache-Control: max-age=60
The 304 has no body. Now suppose the tag changes to "release-18" while the date stays the same because two edits occurred within one second. For an otherwise successful GET, the server sends 200 with the new body, not 304: it must ignore If-Modified-Since when If-None-Match is present. This is RFC 9110 §13.2.2, with the explicit ignore rule in §13.1.3.
Multiple Servers And CDNs
Make validator generation part of the deployment contract. Use the same algorithm and inputs on every node. A content digest is one way to get consistency; per-node file metadata needs inspection. Apache’s FileETag documentation lists MTime Size as the default and permits adding INode. Different inode inputs can yield different tags across nodes. Removing inode does not make separately copied modification times agree.
At a CDN, inspect what reaches the client. Cloudflare documents that compression changes can turn a strong ETag into a weak one, even with Respect Strong ETags enabled. With that setting disabled, some combinations of compression and content transformations can instead remove the tag. On a cache miss, Cloudflare needs a full response body to fill its cache; do not assume the origin sees the same conditional request you sent to the edge.
Cloudflare’s Smart Edge Revalidation can use the time an object entered its cache as a synthetic Last-Modified value when the origin supplied neither validator. An edge date therefore need not be your application’s edit time. Compare origin and edge responses before blaming the application.
The Common Mistake
Treating a validator as a freshness policy. ETag does not say how long a cached response can be reused. Set Cache-Control separately; RFC 9111 defines freshness separately from validation.
Another bug is checking the date first and returning 304 before evaluating the tag. Exercise the conflicting-condition case above in your handler: it catches code that works for unchanged files but silently serves an old representation after a same-second edit.
Check On The Wire
Fetch headers with GET, then copy the actual validator values into conditional requests:
curl -sS -D - -o /dev/null https://example.com/article.json
curl -sS -D - -o /dev/null https://example.com/article.json \
-H 'If-None-Match: "release-17"'
curl -sS -D - -o /dev/null https://example.com/article.json \
-H 'If-Modified-Since: Mon, 05 Oct 2026 09:00:00 GMT'
The values shown are illustrative. For the precedence check, send a tag you know differs from the current tag along with the current Last-Modified date:
curl -sS -D - -o /dev/null https://example.com/article.json \
-H 'If-None-Match: "known-old-release"' \
-H 'If-Modified-Since: Mon, 05 Oct 2026 09:00:00 GMT'
Expect 200 for an otherwise successful GET. If you get 304, verify that the old tag really differs and inspect which layer answered.
Repeat the initial GET against each origin with the same request headers, then through the CDN. Compare ETag, Last-Modified, Content-Encoding, and Vary. Inspect the request and response headers in DevTools as well; a browser result alone cannot tell you what the origin received.
Related
- ETag and Last-Modified
- If-None-Match and If-Modified-Since
- 304 Not Modified and Vary