ETag vs Last-Modified

Written by

in

HTTP defines two ways for a server to identify a version of a resource. They are not interchangeable, and a site can easily end up in a state where they contradict each other.

Last-Modified

A timestamp indicating when the resource last changed. The client sends it back in If-Modified-Since, and the server compares dates.

Its weakness is resolution: HTTP dates have one-second granularity. Two changes within the same second are indistinguishable. It also requires the server to genuinely know when the content changed, which is harder than it sounds for a page assembled from many sources.

ETag

An opaque identifier for a specific version of a representation. The client sends it back in If-None-Match, and the server compares strings.

ETags have no resolution limit and can be derived directly from content, so they detect any change. The cost is that computing a content-derived ETag usually means generating the response before you can decide whether to send it.

Strong and weak validators

A strong ETag ("abc123") means byte-for-byte identical. A weak one (W/"abc123") means semantically equivalent — useful when compression or trivial formatting differences should not count as a change.

This matters in practice: when a server gzips a response, it may convert a strong ETag to a weak one, so the value the client sees differs from the value the application generated.

When the two disagree

Consider editing only a page’s meta description. The modification timestamp updates, so Last-Modified changes. But if that metadata does not appear in the rendered output, the body is byte-identical and the ETag does not change.

A client using If-None-Match now gets 304. A client using If-Modified-Since gets 200. Both are behaving correctly according to the specification. The site is simply making two different claims about whether it changed.

When both validators are present, If-None-Match takes precedence — RFC 9110 is explicit about this.