Introduction
The correct minimum set of headers that works across all mentioned clients (and proxies):
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
The Cache-Control
is per the HTTP 1.1 spec for clients and proxies (and implicitly required by some clients next to Expires
). The Pragma
is per the HTTP 1.0 spec for prehistoric clients. The Expires
is per the HTTP 1.0 and 1.1 specs for clients and proxies. In HTTP 1.1, the Cache-Control
takes precedence over Expires
, so it's after all for HTTP 1.0 proxies only.
If you don't care about IE6 and its broken caching when serving pages over HTTPS with only no-store
, then you could omit Cache-Control: no-cache
.
Cache-Control: no-store, must-revalidate
Pragma: no-cache
Expires: 0
If you don't care about IE6 nor HTTP 1.0 clients (HTTP 1.1 was introduced in 1997), then you could omit Pragma
.
Cache-Control: no-store, must-revalidate
Expires: 0
If you don't care about HTTP 1.0 proxies either, then you could omit Expires
.
Cache-Control: no-store, must-revalidate
On the other hand, if the server auto-includes a valid Date
header, then you could theoretically omit Cache-Control
too and rely on Expires
only.
Date: Wed, 24 Aug 2016 18:32:02 GMT
Expires: 0
But that may fail if e.g. the end-user manipulates the operating system date and the client software is relying on it.
Other Cache-Control
parameters such as max-age
are irrelevant if the abovementioned Cache-Control
parameters are specified. The Last-Modified
header as included in most other answers here is only interesting if you actually want to cache the request, so you don't need to specify it at all.
How to set it?
Using PHP:
header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP 1.1.
header("Pragma: no-cache"); // HTTP 1.0.
header("Expires: 0"); // Proxies.
Using Java Servlet, or Node.js:
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setHeader("Expires", "0"); // Proxies.
Using ASP.NET-MVC
Response.Cache.SetCacheability(HttpCacheability.NoCache); // HTTP 1.1.
Response.Cache.AppendCacheExtension("no-store, must-revalidate");
Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
Response.AppendHeader("Expires", "0"); // Proxies.
Using ASP.NET Web API:
// `response` is an instance of System.Net.Http.HttpResponseMessage
response.Headers.CacheControl = new CacheControlHeaderValue
{
NoCache = true,
NoStore = true,
MustRevalidate = true
};
response.Headers.Pragma.ParseAdd("no-cache");
// We can't use `response.Content.Headers.Expires` directly
// since it allows only `DateTimeOffset?` values.
response.Content?.Headers.TryAddWithoutValidation("Expires", 0.ToString());
Using ASP.NET:
Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
Response.AppendHeader("Expires", "0"); // Proxies.
Using ASP.NET Core v3
// using Microsoft.Net.Http.Headers
Response.Headers[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate";
Response.Headers[HeaderNames.Expires] = "0";
Response.Headers[HeaderNames.Pragma] = "no-cache";
Using ASP:
Response.addHeader "Cache-Control", "no-cache, no-store, must-revalidate" ' HTTP 1.1.
Response.addHeader "Pragma", "no-cache" ' HTTP 1.0.
Response.addHeader "Expires", "0" ' Proxies.
Using Ruby on Rails:
headers["Cache-Control"] = "no-cache, no-store, must-revalidate" # HTTP 1.1.
headers["Pragma"] = "no-cache" # HTTP 1.0.
headers["Expires"] = "0" # Proxies.
Using Python/Flask:
response = make_response(render_template(...))
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate" # HTTP 1.1.
response.headers["Pragma"] = "no-cache" # HTTP 1.0.
response.headers["Expires"] = "0" # Proxies.
Using Python/Django:
response["Cache-Control"] = "no-cache, no-store, must-revalidate" # HTTP 1.1.
response["Pragma"] = "no-cache" # HTTP 1.0.
response["Expires"] = "0" # Proxies.
Using Python/Pyramid:
request.response.headerlist.extend(
(
('Cache-Control', 'no-cache, no-store, must-revalidate'),
('Pragma', 'no-cache'),
('Expires', '0')
)
)
Using Go:
responseWriter.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") // HTTP 1.1.
responseWriter.Header().Set("Pragma", "no-cache") // HTTP 1.0.
responseWriter.Header().Set("Expires", "0") // Proxies.
Using Clojure (require Ring utils):
(require '[ring.util.response :as r])
(-> response
(r/header "Cache-Control" "no-cache, no-store, must-revalidate")
(r/header "Pragma" "no-cache")
(r/header "Expires" 0))
Using Apache .htaccess
file:
<IfModule mod_headers.c>
Header set Cache-Control "no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires 0
</IfModule>
Using HTML:
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
HTML meta tags vs HTTP response headers
Important to know is that when an HTML page is served over an HTTP connection, and a header is present in both the HTTP response headers and the HTML <meta http-equiv>
tags, then the one specified in the HTTP response header will get precedence over the HTML meta tag. The HTML meta tag will only be used when the page is viewed from a local disk file system via a file://
URL. See also W3 HTML spec chapter 5.2.2. Take care with this when you don't specify them programmatically because the webserver can namely include some default values.
Generally, you'd better just not specify the HTML meta tags to avoid confusion by starters and rely on hard HTTP response headers. Moreover, specifically those <meta http-equiv>
tags are invalid in HTML5. Only the http-equiv
values listed in HTML5 specification are allowed.
Verifying the actual HTTP response headers
To verify the one and the other, you can see/debug them in the HTTP traffic monitor of the web browser's developer toolset. You can get there by pressing F12 in Chrome/Firefox23+/IE9+, and then opening the "Network" or "Net" tab panel, and then clicking the HTTP request of interest to uncover all detail about the HTTP request and response. The below screenshot is from Chrome:
I want to set those headers on file downloads too
First of all, this question and answer are targeted on "web pages" (HTML pages), not "file downloads" (PDF, zip, Excel, etc). You'd better have them cached and make use of some file version identifier somewhere in the URI path or query string to force a redownload on a changed file. When applying those no-cache headers on file downloads anyway, then beware of the IE7/8 bug when serving a file download over HTTPS instead of HTTP. For detail, see IE cannot download foo.jsf. IE was not able to open this internet site. The requested site is either unavailable or cannot be found.
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.
Best Answer
We're doing API and Output caching on a large scale (3 milion visits a day) web site (news portal). The site is primarily used by anonymous users, but we do have authenticated users and we cache a complete site just for them, due to some personalized parts of the site, and I must admit that we had absolutely no problems with memory pressure.
So, my advice would be cache everything you can in API cache so your Output cache rebuilding is even faster.
Of course, pay close attention to your cache ratio values in the performance counters. You should see numbers >95% of cached hits.
Another thing to pay attention is cache invalidation, this is a big issue if you have a lot of related content. For example, you cache music stuff and information about one album or song might be displayed and cached on few hundred pages. If anything changes in that song, you have to invalidate all of these pages which can be problematic.
Bottom line, caching is one of the best features of ASP.NET, it's done superbly and you can rely on it.