"Cache-Control: max-age=0, no-cache" but browser bypasses server query (and hits cache)?

George Hawkins picture George Hawkins · Mar 16, 2015 · Viewed 33.7k times · Source

I'm using Chrome 40 (so something nice and modern).

Cache-Control: max-age=0, no-cache is set on all pages - so I expect the browser to only use something from its cache if it has first checked with the server and gotten a 304 Not Modified response.

However on pressing the back button the browser merrily hits its own cache without checking with the server.

If I open the same page, as I reached with the back button, in a new tab then it does check with the server (and gets a 303 See Other response as things have changed).

See the screen captures below showing the output for the two different cases from the Network tab of the Chrome Developer Tools.

I thought I could use max-age=0, no-cache as a lighter weight alternative to no-store where I don't want users seeing stale data via the back button (but where the data is non-valuable and so can be cached).

My understanding of no-cache (see here and here on SO) is that the browser must always revalidate all responses. So why doesn't Chrome do this when using the back button?

Is no-store the only option?


200 response (from cache) on pressing back button:

enter image description here

303 response on requesting the same page in a new tab:

enter image description here

Answer

Ext3h picture Ext3h · Mar 24, 2015

From http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.1

no-cache

If the no-cache directive does not specify a field-name, then a cache MUST NOT use the response to satisfy a subsequent request without successful revalidation with the origin server. This allows an origin server to prevent caching even by caches that have been configured to return stale responses to client requests.

If the no-cache directive does specify one or more field-names, then a cache MAY use the response to satisfy a subsequent request, subject to any other restrictions on caching. However, the specified field-name(s) MUST NOT be sent in the response to a subsequent request without successful revalidation with the origin server. This allows an origin server to prevent the re-use of certain header fields in a response, while still allowing caching of the rest of the response.

Other than the name implies, no-cache does not require that the response must not be stored in cache. It only specifies that the cached response must not be reused to serve a subsequent request without re-validating, so it's a shorthand for must-revalidate, max-age=0.

It is up to the browser what to qualify as a subsequent request, and to my understanding using the back-button does not. This behavior varies between different browser engines.

no-store forbids the use of the cached response for all requests, not only for subsequent ones.

Note that even with no-store, the RFC actually permits the client to store the response for use in history buffers. That means client may still use a cached response even when no-store has been specified.

Latter behavior covers cases where the page has been recorded with its original page title in the browser history. Another use case is the behavior of various mobile browsers which will not discard the previous page until the following page has fully loaded as the user might want to abort.

For clarification on the the behavior of the back button: It is not subject to any cache header, according to http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.13

User agents often have history mechanisms, such as "Back" buttons and history lists, which can be used to redisplay an entity retrieved earlier in a session.

History mechanisms and caches are different. In particular history mechanisms SHOULD NOT try to show a semantically transparent view of the current state of a resource. Rather, a history mechanism is meant to show exactly what the user saw at the time when the resource was retrieved.

By default, an expiration time does not apply to history mechanisms. If the entity is still in storage, a history mechanism SHOULD display it even if the entity has expired, unless the user has specifically configured the agent to refresh expired history documents.

That means that disrespecting any cache control headers when using the back button is the recommended behavior. If your browser happens to honor a backdated expiration date or applies the no-store directive not only to the browser cache but also to the history, it's actually already departing from that recommendation.

For how to solve it:
You can't, and you are not supposed to. If the user is returning to a previously visited page, most browsers will even try to restore the viewport. You may use deferred mechanism like AJAX to refresh content if this was the original behavior before the user left the page, but otherwise you should not even modify the content.