HTTP headers · Response
ETag
ETag identifies a specific version of a representation so caches and clients can revalidate it cheaply.
Key facts
| Type | Response |
|---|---|
| Syntax | <code>ETag: "abc123"</code> |
| Weak | <code>W/"abc123"</code> |
| Revalidation | <code>If-None-Match</code> |
| Success | 304 with no body |
| Generation | Content hash or build id |
| Standard | RFC 9110 §8.8.3 |
Validators and the 304
The client stores an ETag with the body. On the next request it sends If-None-Match, and the server compares. If the representation has not changed, the server answers 304 Not Modified with no body, saving bandwidth while refreshing the stored copy's lifetime.
A strong ETag promises byte equality, which matters for range requests and integrity. A weak ETag, prefixed with W/, promises only semantic equivalence: the content means the same thing but may differ in insignificant ways.
Generating a stable value
Any value works as long as it changes when the bytes change and stays stable when they do not. A content hash is reliable but costs CPU; a build identifier plus mtime is cheap but can collide across deploys. ETags must be quoted, and they must differ between representations that vary by encoding or language.
ETag does not replace Cache-Control. It enables revalidation; Cache-Control decides when revalidation is needed. Without a cache lifetime, a client may revalidate on every request.
Frequently asked questions
Can I use ETag instead of Last-Modified?
Yes, and it is more precise. Sending both is allowed; If-None-Match takes precedence when both validators are present.
Is ETag unique across servers?
No. It is only compared with the same resource on the same server, so a hash does not need a global namespace.