733

The header Cache-Control: max-age=0 implies that the content is considered stale (and must be re-fetched) immediately, which is in effect the same thing as Cache-Control: no-cache.

1

9 Answers 9

677

I had this same question, and found some info in my searches (your question came up as one of the results). Here's what I determined...

There are two sides to the Cache-Control header. One side is where it can be sent by the web server (aka. "origin server"). The other side is where it can be sent by the browser (aka. "user agent").


When sent by the origin server

I believe max-age=0 simply tells caches (and user agents) the response is stale from the get-go and so they SHOULD revalidate the response (eg. with the If-Not-Modified header) before using a cached copy, whereas, no-cache tells them they MUST revalidate before using a cached copy. From 14.9.1 What is Cacheable:

no-cache

...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.

In other words, caches may sometimes choose to use a stale response (although I believe they have to then add a Warning header), but no-cache says they're not allowed to use a stale response no matter what. Maybe you'd want the SHOULD-revalidate behavior when baseball stats are generated in a page, but you'd want the MUST-revalidate behavior when you've generated the response to an e-commerce purchase.

Although you're correct in your comment when you say no-cache is not supposed to prevent storage, it might actually be another difference when using no-cache. I came across a page, Cache Control Directives Demystified, that says (I can't vouch for its correctness):

In practice, IE and Firefox have started treating the no-cache directive as if it instructs the browser not to even cache the page. We started observing this behavior about a year ago. We suspect that this change was prompted by the widespread (and incorrect) use of this directive to prevent caching.

...

Notice that of late, "cache-control: no-cache" has also started behaving like the "no-store" directive.

As an aside, it appears to me that Cache-Control: max-age=0, must-revalidate should basically mean the same thing as Cache-Control: no-cache. So maybe that's a way to get the MUST-revalidate behavior of no-cache, while avoiding the apparent migration of no-cache to doing the same thing as no-store (ie. no caching whatsoever)?


When sent by the user agent

I believe shahkalpesh's answer applies to the user agent side. You can also look at 13.2.6 Disambiguating Multiple Responses.

If a user agent sends a request with Cache-Control: max-age=0 (aka. "end-to-end revalidation"), then each cache along the way will revalidate its cache entry (eg. with the If-Not-Modified header) all the way to the origin server. If the reply is then 304 (Not Modified), the cached entity can be used.

On the other hand, sending a request with Cache-Control: no-cache (aka. "end-to-end reload") doesn't revalidate and the server MUST NOT use a cached copy when responding.

15
  • 10
    Wouldn't Cache-Control: max-age=0, must-revalidate, proxy-revalidate would be the exact equivalence of no-cache ?
    – Didier A.
    Nov 27, 2012 at 19:00
  • 1
    Great answer, I went to read the article you site but the page is no longer valid. palisade.plynt.com/issues/2008Jul/cache-control-attributes Mar 27, 2015 at 15:01
  • 9
    Thanks, @CraigLondon. I redirected it to a cached version. Mar 29, 2015 at 11:50
  • 6
    must-revalidate is NOT meant to be the same as no-cache or no-store. The latter bypass caches altogether, but the former just says that a cache must always be checked for freshness, but if it is still current, it can be used, so saving bandwidth. The latter forces full end-to-end downloads all the time, taking up unnecessary bandwidth and delaying responses.
    – Patanjali
    Feb 23, 2017 at 23:15
  • 8
    @Patanjali no-cache doesn't "bypass caches altogether" or "force full end-to-end downloads all the time", at least not in all browsers. The specification only says that browser must validate the cache. Oct 15, 2018 at 14:14
57

max-age=0

This is equivalent to clicking Refresh, which means, give me the latest copy unless I already have the latest copy.

no-cache

This is holding Shift while clicking Refresh, which means, just redo everything no matter what.

3
  • 70
    This is incorrect. shift-refresh is a hard refresh which is more similar to no-store
    – Michael
    Feb 23, 2016 at 18:58
  • 4
    Verified in Firefox 45.0 which, like Chrome 49.0.2623.87 m, also sends a "Pragma: no-cache" when Shift+Refreshing. Mar 14, 2016 at 17:05
  • 2
    Your description is inaccurate. This is misinforming people.
    – jKlaus
    Nov 12, 2021 at 15:14
37

Old question now, but if anyone else comes across this through a search as I did, it appears that IE9 will be making use of this to configure the behaviour of resources when using the back and forward buttons. When max-age=0 is used, the browser will use the last version when viewing a resource on a back/forward press. If no-cache is used, the resource will be refetched.

Further details about IE9 caching can be seen on this msdn caching blog post.

1
  • 4
    Similarly, IE 8 runs into all sorts of "could not download" issues when no-cache is used over https. suggested resolutions sometimes include changing headers to max-age=0 Jan 13, 2014 at 23:54
31

In my recent tests with IE8 and Firefox 3.5, it seems that both are RFC-compliant. However, they differ in their "friendliness" to the origin server. IE8 treats no-cache responses with the same semantics as max-age=0,must-revalidate. Firefox 3.5, however, seems to treat no-cache as equivalent to no-store, which sucks for performance and bandwidth usage.

Squid Cache, by default, seems to never store anything with a no-cache header, just like Firefox.

My advice would be to set public,max-age=0 for non-sensitive resources you want to have checked for freshness on every request, but still allow the performance and bandwidth benefits of caching. For per-user items with the same consideration, use private,max-age=0.

I would avoid the use of no-cache entirely, as it seems it has been bastardized by some browsers and popular caches to the functional equivalent of no-store.

Additionally, do not emulate Akamai and Limelight. While they essentially run massive caching arrays as their primary business, and should be experts, they actually have a vested interest in causing more data to be downloaded from their networks. Google might not be a good choice for emulation, either. They seem to use max-age=0 or no-cache randomly depending on the resource.

1
  • 3
    Best answer for password protected content. private,max-age=0.
    – dana
    Apr 17, 2014 at 2:44
23
max-age
    When an intermediate cache is forced, by means of a max-age=0 directive, to revalidate 
its own cache entry, and the client has supplied its own validator in the request, the 
supplied validator might differ from the validator currently stored with the cache entry. 
In this case, the cache MAY use either validator in making its own request without 
affecting semantic transparency. 

    However, the choice of validator might affect performance. The best approach is for the 
intermediate cache to use its own validator when making its request. If the server replies 
with 304 (Not Modified), then the cache can return its now validated copy to the client 
with a 200 (OK) response. If the server replies with a new entity and cache validator, 
however, the intermediate cache can compare the returned validator with the one provided in 
the client's request, using the strong comparison function. If the client's validator is 
equal to the origin server's, then the intermediate cache simply returns 304 (Not 
Modified). Otherwise, it returns the new entity with a 200 (OK) response. 

    If a request includes the no-cache directive, it SHOULD NOT include min-fresh, 
max-stale, or max-age. 

courtesy: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4

Don't accept this as answer - I will have to read it to understand the true usage of it :)

1
  • I actually tried to read that very page and didn't understand it. I came to stackoverflow in hope for a simple, clear answer.
    – Florian F
    Feb 3, 2021 at 17:37
16

I'm hardly a caching expert, but Mark Nottingham is. Here are his caching docs. He also has excellent links in the References section.

Based on my reading of those docs, it looks like max-age=0 could allow the cache to send a cached response to requests that came in at the "same time" where "same time" means close enough together they look simultaneous to the cache, but no-cache would not.

2
  • Good point, but in practice does any browsers actually do that?
    – Pacerier
    Jul 12, 2012 at 4:31
  • 4
    @Pacerier I think this is more for caching proxy servers like Varnish, Squid, Traffic, etc.
    – Hank Gay
    Jul 12, 2012 at 12:56
15

By the way, it's worth noting that some mobile devices, particularly Apple products like iPhone/iPad completely ignore headers like no-cache, no-store, Expires: 0, or whatever else you may try to force them to not re-use expired form pages.

This has caused us no end of headaches as we try to get the issue of a user's iPad say, being left asleep on a page they have reached through a form process, say step 2 of 3, and then the device totally ignores the store/cache directives, and as far as I can tell, simply takes what is a virtual snapshot of the page from its last state, that is, ignoring what it was told explicitly, and, not only that, taking a page that should not be stored, and storing it without actually checking it again, which leads to all kinds of strange Session issues, among other things.

I'm just adding this in case someone comes along and can't figure out why they are getting session errors with particularly iphones and ipads, which seem by far to be the worst offenders in this area.

I've done fairly extensive debugger testing with this issue, and this is my conclusion, the devices ignore these directives completely.

Even in regular use, I've found that some mobiles also totally fail to check for new versions via say, Expires: 0 then checking last modified dates to determine if it should get a new one.

It simply doesn't happen, so what I was forced to do was add query strings to the css/js files I needed to force updates on, which tricks the stupid mobile devices into thinking it's a file it does not have, like: my.css?v=1, then v=2 for a css/js update. This largely works.

User browsers also, by the way, if left to their defaults, as of 2016, as I continuously discover (we do a LOT of changes and updates to our site) also fail to check for last modified dates on such files, but the query string method fixes that issue. This is something I've noticed with clients and office people who tend to use basic normal user defaults on their browsers, and have no awareness of caching issues with css/js etc, almost invariably fail to get the new css/js on change, which means the defaults for their browsers, mostly MSIE / Firefox, are not doing what they are told to do, they ignore changes and ignore last modified dates and do not validate, even with Expires: 0 set explicitly.

This was a good thread with a lot of good technical information, but it's also important to note how bad the support for this stuff is in particularly mobile devices. Every few months I have to add more layers of protection against their failure to follow the header commands they receive, or to properly interpet those commands.

1
  • css and js are suitable candidates for caching, as in production systems, they shouldn't really change often. However, having caching for them while developing is a pain, as that activity can require frequent forced cache-flushes. But, if one cannot use different settings for the different environments, production requirements should take precedent, as it has the most effect because the far larger numbers of accesses will save bandwidth, compared to the few Ctrl-F5 refreshes that a few developers will have to do. However, querying real-time data requires that cache-control work properly.
    – Patanjali
    Oct 17, 2018 at 7:01
7

This is answered directly in the MDN docs about cache control:

Most HTTP/1.0 caches don't support no-cache directives, so historically max-age=0 was used as a workaround. But only max-age=0 could cause a stale response to be reused when caches disconnected from the origin server. must-revalidate addresses that. That's why the example below is equivalent to no-cache.

Cache-Control: max-age=0, must-revalidate

But for now, you can simply use no-cache instead.

And also in the MDN docs about cache validation:

It is often stated that the combination of max-age=0 and must-revalidate has the same meaning as no-cache.

Cache-Control: max-age=0, must-revalidate 

max-age=0 means that the response is immediately stale, and must-revalidate means that it must not be reused without revalidation once it is stale — so in combination, the semantics seem to be the same as no-cache.

However, that usage of max-age=0 is a remnant of the fact that many implementations prior to HTTP/1.1 were unable to handle the no-cache directive — and so to deal with that limitation, max-age=0 was used as a workaround.

But now that HTTP/1.1-conformant servers are widely deployed, there's no reason to ever use that max-age=0-and-must-revalidate combination — you should instead just use no-cache.

1
  • 1
    I hope you don't mind the added docs. There's a dupe copy of this question (it seems) that I found first and I literally just wrote a virtually identical answer (great minds...). But then I found this and saw your answer, so I figured I'd just expand on yours instead of posting yet another answer. I think it's a useful addition, but nbd if you want to roll back.
    – Jason C
    Jun 10, 2022 at 14:01
2

One thing that (surprisingly) hasn't been mentioned is that a request can explicitly indicate that it will accept stale data, using the max-stale directive. In that case, if the server responded with max-age=0, the cache would merely consider the response stale, and would be free to use it to satisfy the client's request [which asked for potentially-stale data]. By contrast, if the server sends no-cache that really does trump any request by the client (with max-stale) for stale data, as the cache MUST revalidate.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.