Playing around with some cache headers last night, I ran into some unexpected behaviour from Chrome. Normally I’d blame my code, but there’s a number of devs on the interwebs that have run into the same thing.
I created a web service that returned two cache-related headers: ETag and Expires. ETag is used to give an “id” for the resource you just received, allowing you to use that to ask the server if that ID is still valid – if it is we avoid re-downloading the resource, instead sending a 302 Not Modified response. The Expires header lets a client know when the resource you just downloaded should be considered “expired”; you can keep using it until the specified date/time by just loading the information from cache instead of talking to the server at all.
When a response comes back with those two headers, the expected behaviour is that your client (in this case Chrome) would use them in subsequent requests. In the case where you get both of these headers at the same time, you’d assume that the Expires header would take precedence over the ETag header, load the response from cache in a matter of a few milliseconds, and not talk to the server at all. Unfortunately with Chrome its not the case.
The ETag header is meant to be validated by a server – only the server can know if that ETag is still valid. To perform that validation you would have to make a request against the server, sending the ETag value in the If-None-Match request header. Chrome does this perfectly, but it does it over the Expires or Cache-Control headers which normally would be used to avoid a request at all.