HTTP has several features related to caching and they apply to both user agent (browser) cache and proxy caches (whether transparent, or not; e.g. proxy in the client’s network or a reverse proxy sitting just next to the server). These features come in two groups: expiration (may prevent request entirely) and validation (may prevent transfer of data).
Entity tag (ETag
) is just one of these features and belongs to the validation group. The other one in this group is last modification time (Last-Modified
). Entity tag allows for cache invalidation due to contents change instead of newer last modification time. Read more on how entity tag works on Wikipedia. In short, the typical usage is:
The server adds ETag
header to a response containing a resource being served.
The client caches the resource and remembers its entity tag (the value of ETag
).
Next time the client needs the resource, it requests it from the server conditionally. In the request, it includes If-None-Match
header containing the entity tag.
If the resource changed (the entity tag in If-None-Match
is considered to be stale by the server), the server sends a response containing the current version of the resource (and the new entity tag), otherwise it just responds with 304 Not Modified
and does not bother to send the resource again.
For static files (not created dynamically by a CGI script or so, on each request), Apache may be configured to generate ETag
via FileETag
directive. By default, without you making any changes to the configuration, Apache will generate ETag
and its value will be based on the file’s last modification time (mtime) and size in Apache 2.4. In Apache 2.3.14, the default used to include the file’s inode number, too.
If the file is served dynamically, Apache cannot generate the ETag
, because it does not know the details of how the resource to be cached is generated. It is up to the script to set ETag
appropriately and to handle the If-None-Match
. E.g. in mod_perl, the If-None-Match
part can be handled using Apache2::Request::meets_conditions, which implements handling of HTTP/1.1 conditional requests in general.
If you want to rely solely on ETag, you have to disable other validation features and the expiration mechanism. Set Cache-Control: max-age=0, must-revalidate
and Expires: 0
to force the revalidation of cache entries (i.e. always make a request). You may also remove the Last-Modified
header from the responses, but HTTP/1.1 advises against that, in general.
For comparison of Last-Modified
and ETag
, see these:
Note that Last-Modified
is seen as a HTTP/1.0 compatibility feature. ETag
may contain the same value and work exactly the same (except using If-None-Match
instead of If-Modified-Since
).
As a side note, I’d like to add that proposed standard RFC 7232 exists and it is related to details of entity tags and conditional requests. See its appendix A for changes it introduces from HTTP/1.1.