1680

Our investigations have shown us that not all browsers respect the HTTP cache directives in a uniform manner.

For security reasons we do not want certain pages in our application to be cached, ever, by the web browser. This must work for at least the following browsers:

  • Internet Explorer 6+
  • Firefox 1.5+
  • Safari 3+
  • Opera 9+
  • Chrome

Our requirement came from a security test. After logging out from our website you could press the back button and view cached pages.

HDJEMAI
  • 7,766
  • 41
  • 60
  • 81
Edward Wilde
  • 23,831
  • 8
  • 52
  • 64
  • Just for ipad Safari, Does [this][1] help? [1]: http://stackoverflow.com/questions/24524248/forcing-mobile-safari-to-re-evaluate-the-cached-page-when-user-presses-back-butt/24524249#24524249 – Bakhshi Jul 03 '14 at 06:15
  • The simplest is using: max-age=10 . This is not perfect because the page will be cached for 10 seconds. But it's the least "header spaghetti" solution out there. Also, this sometimes provides a big performance boost on dynamic websites who use reverse proxies. (Your slow php script will be called once every 10 seconds and will then be cached by the reverse proxy. once per 10 seconds is way better than once per visitor) – Hello World Aug 29 '14 at 12:27
  • 4
    Also see https://securityevaluators.com/knowledge/case_studies/caching/ – Pacerier Mar 15 '15 at 18:08
  • 3
    Thank you for that great question . For curiosity what might be the situation that makes you send some data while don't want the receiver to save it for *"security reasons"* . you sent them already! – Accountant م Apr 22 '17 at 09:45
  • see https://stackoverflow.com/a/49925190/3748498 – pravdomil Apr 19 '18 at 15:47
  • @Accountantم, they may be concerned about input fields. Back button would normally preserve those. – pcalkins Nov 06 '19 at 23:25
  • 2
    @Accountant: in his scenario, the user had logged out. Who can guarantee that the next human user on that User-Agent will be the person who just logged out? – Fabien Haddadi Jan 30 '20 at 14:06

29 Answers29

2797

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, or Python/Flask:

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/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:

Chrome developer toolset HTTP traffic monitor showing HTTP response headers on stackoverflow.com

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.

BalusC
  • 992,635
  • 352
  • 3,478
  • 3,452
  • 18
    This does not appear to be complete. I tried this solution on IE 8 and found that the browser will load a cached version when you hit the back button. – Mike Ottum Jan 15 '10 at 02:26
  • 18
    Likely your testing methodology was wrong. Maybe the page was already in the cache? Maybe the headers were incorrect/overriden? Maybe you were looking at the wrong request? Etc.. – BalusC Jan 15 '10 at 03:38
  • @BalusC: Some people told me setting expires to **0** won't work in some cases, so better set it to past date. also i got some interesting [comment](http://www.php.net/manual/en/function.header.php#108138) in php.net – vkGunasekaran Apr 20 '12 at 06:45
  • I would like to add that I just did this and it did not appear to be working while I was testing. Turned out iis on the server was caching the pages – Curtis May 30 '12 at 20:33
  • 10
    Actually, I confirm that this approach is incomplete and causes issues with IE8, or at least in some circumstances. Specifically, when using IE8 to fetch a resource over SSL, IE8 will refuse to fetch the resource a second time (either at all, or after a first try, depending on headers used). See [EricLaw's blog](http://blogs.msdn.com/b/ieinternals/archive/2009/10/02/internet-explorer-cannot-download-over-https-when-no-cache.aspx), for instance. – haylem Oct 02 '12 at 22:46
  • @haylem IE6, 7 and 8 must have `no-store` before `no-cache` and can't have a `Pragma` header on HTTPS connections. – Keith Oct 29 '12 at 11:51
  • 24
    I'd like to add that this is essentially what Bank of America does. If you look at their response headers and translate that into aspx, they're doing: Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); Response.AppendHeader("Expires", "Thu, 01 Dec 1994 16:00:00 GMT"); I figure, if it's good enough for them, it's good enough for me. – John Feb 12 '13 at 16:14
  • 10
    @John: That expires header is exactly the example value in [HTTP 1.0 specification](http://www.ietf.org/rfc/rfc1945.txt). It works, but it's somewhat ridiculous to take exactly that timestamp. – BalusC Feb 12 '13 at 16:18
  • Yeah, it's very arbitrary but Bank of America uses that value! lol I suppose it really doesn't matter what you use, right? But I posted the comment not to suggest any changes to your example, but to provide a real world example that supports your example from people we trust to know about this stuff (our banks). – John Feb 12 '13 at 16:27
  • 1
    The "plain HTML way" you mentioned doesn't work actually, even without proxies; the headers should be in the actual HTTP headers (E.g. "Expires: 0"), not in HTML tag. And when proxies get in the way, it's guaranteed not to work: proxies rely on HTTP headers, not HTML contents. I recommend reading this document to get a good understanding: http://www.mnot.net/cache_docs/ – Luka Ramishvili Feb 16 '13 at 10:37
  • 1
    @Luka: as stated in the answer: *"The HTML meta tag will only be used when the page is viewed from local disk file system."* – BalusC Feb 16 '13 at 11:33
  • Agreed, but the way the answer is formulated is confusing. "then the one specified in the response header will get precedence over the HTML meta tag." - when served over HTTP, META tags won't have any precedence at all. Moreover, the proxy may not read the and tags at all. Finally, there are people who won't read further after they find the syntax. And then, a HTML file from local file system wouldn't change (unless on a developer machine) and thus won't need cache-control, so using the META cache-control tags are extremely uncommon (I think a small note nearby will do). – Luka Ramishvili Feb 21 '13 at 11:00
  • @haylem You're right: this solution completely fails to force IE8 to reload the page when the user presses the back button. Considering the documentation given by Microsoft, though, this is probably an IE8 bug (it doesn't happen in IE9), so we're highly unlikely to ever see a solution. – coredumperror Mar 22 '13 at 18:26
  • @CoreDumpError: note that haylem's comment concerns downloading files via IE8 which is answered [here](http://support.microsoft.com/kb/316431). This is further unrelated to "this solution". – BalusC Mar 22 '13 at 18:31
  • this might sound like a poor question . Are we supposed to write this in every page we don't want to be cached ? If I write this in jsp pages , should I write in meta tags or using response.setHeader() ? – Raman Ghai May 11 '13 at 13:27
  • 1
    @Raman: Do not repeat code nor write Java code in JSP. Use a filter. – BalusC May 12 '13 at 02:50
  • 1
    @BalusC, doesn't `no-cache` already imply `must-revalidate`? And doesn't `no-store` already imply `no-cache`? Why can't we simply do `Cache-Control:no-store` instead of the longer version you showed in the answer? – Pacerier Aug 25 '13 at 19:06
  • 1
    @Pacerier: you're right, but browsers not. The intent is to cover all browsers, also those who are wrong. – BalusC Aug 25 '13 at 19:08
  • @BalusC, so are you suggesting we do some rojak like http://stackoverflow.com/a/49549/632951 ? – Pacerier Aug 25 '13 at 19:20
  • 1
    @BalusC, your solution doesn't work on Safari 5.1.7 and Opera – Pacerier Aug 29 '13 at 04:32
  • @Keith this is wrong, IE 8 can accept Pragma over HTTPS, but just not no-cache http://stackoverflow.com/questions/3415370/ie-unable-to-download-from-unable-to-open-this-internet-site-the-request/19145399#19145399 – Robert Christ Jan 13 '14 at 23:48
  • 1
    @RobertChrist we found that it had to have `no-store` before `no-cache` and no `Pragma` - when we added the `Pragma` IE6&7 broke under HTTPS. As `Pragma`'s only needed by IE5 and older it's pretty pointless, so we didn't test IE8 specifically. Best practice is probably to just skip it, as (unless you support 90's browsers) it's just a waste of bytes. – Keith Jan 14 '14 at 16:30
  • 1
    OMG I cannot believe this has been voted up so much. 'Pragma: No-cache' is MEANINGLESS when sent from the server to the client. 'no-store' does NOT prevent in-memory caching. 'The HTML meta tag will only be used when the page is viewed from local disk' - is wrong too. PLEASE READ THE DOCUMENTS YOU LINKED, BalusC – symcbean Feb 14 '14 at 16:31
  • @symcbean I think the new wording in HTTPbis is sufficiently strong enough to prevent people from doing in-memory HTTP caching when `no-store` is present http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache-26#section-5.2.1.5 The browser may keep it as history, but it should never re-serve it in response to a request. – Darrel Miller Feb 20 '14 at 23:11
  • Actually, this list is not complete. The correct set would be: Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate Pragma: no-cache, no-store Expires: -1 Last-Modified: -1 – Chameera Dedduwage Jun 03 '14 at 09:26
  • @Chameera: Cache-control:proxy-revalidate is only relevant when you have accidentally cached the resources before by a stupid mistake and they meander around in proxies. You can safely remove this later. Pragma doesn't support no-store, this is specific to cache-control. Last-Modified is still irrelevant in this case. The -1 instead of 0 absolutely doesn't make sense and may possibly make things worse. The list in the answer is definitely complete if you start off the smart way. – BalusC Jun 03 '14 at 10:06
  • @BalusC, you're right, I slipped on the Pragma. It only supports no-cache, thanks for the pointer. On the other hand, Anyone who does not want to take a risk **should** go with proxy-revalidate. Plus, if I'm not mistaken, the -1 is specific to UNIX and will return the minimum possible date. Far better than hard-coding some day in the past, IMHO. – Chameera Dedduwage Jun 09 '14 at 06:01
  • @Chameera: the proxy-revalidate is only to fix your own mistake. Just do it right from the beginning on! The -1 is riskful because browsers may not expect a hyphen and/or a negative date and potentially cause an integer undeflow (and thus you effectively end up with max date). Just follow the instructions given in the current answer instead of unnecessarily tying to make it overcomplicated. This is almost ridiculous. – BalusC Jun 09 '14 at 06:03
  • @hvgotcodes: It seems to be interfering with Firefox 31. My approach is to set a small max-age, e.g. max-age = 10. This seems to make the appcache manifest work as expected on all browsers. – Hello World Aug 29 '14 at 12:25
  • Shouldn't "Expires" be a date (the same value as the "Date" header), not the number "0"? See https://www.mnot.net/cache_docs/ – Luke Hutchison Dec 30 '14 at 14:07
  • @Luke: An invalid format will be interpreted as "in the past". See also [HTTP header field spec](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21): *"HTTP/1.1 clients and caches MUST treat other invalid date formats, especially including the value "0", as in the past (i.e., "already expired")."*. – BalusC Dec 30 '14 at 14:16
  • I can't define `Pragma: no-cache` on Amazon S3, do you know why? Does it work without `Pragma: no-cache` too? – Uri Mar 08 '15 at 12:39
  • unfortunately, the w3c validator flags the html solution as an error (not warning). there must be some html solution that does not invalidate the html... – ivo Welch Sep 03 '15 at 15:03
  • @ivoWelch: it's indeed not allowed anymore in HTML5. Just use response headers as recommended in answer. – BalusC Sep 03 '15 at 19:34
  • Isn't pragma a request header? – sivann Mar 13 '16 at 10:27
  • @Christoph: from your link: *"HTTP/1.1 clients and caches MUST treat other invalid date formats, especially including the value "0", as in the past (i.e., "already expired")"* (see also my comment-reply on his comment). – BalusC Jun 05 '16 at 12:22
  • @BalusC are the HTTP headers required on every page or just the index page? – ilovetolearn Sep 13 '16 at 06:58
  • 1
    Technically, sending `Expires: 0` is not RFC-compliant. It says that *caches* must interpret it as "expired", but it does not say that you *as the server* are allowed to send malformed dates for this purpose. It even recommends that you send `Expires`/`Date` headers with the same value to mark it expired. – a cat Sep 16 '16 at 16:14
  • [This page from Oracle](http://docs.oracle.com/cd/E13158_01/alui/wci/docs103/devguide/tsk_pagelets_settingcaching_httpexpires.html) states "_Note: Never use Expires = 0 to prevent caching. The Expires header is sent by the remote server and passed through to the browser by the Portal Server. Unless the time on all three machines is synchronized, an Expires=0 header can mistakenly return cached content. To solve this problem, set the Expires header to a fixed date that is definitely in the past._". This seems to contradict most answers/comments (and the RFC). Is that something to be considered? – Alvaro Montoro Jan 25 '17 at 16:12
  • This helped me in making Jquery Ajax call without adding time stamp to the get url – venugopal Sep 08 '17 at 05:39
  • Forgive me for my ignorance, will this not cache the html response only, but allow updates on the css/js? That is to say if the html has script/link-css elements, it will still get them? I would not like to re-load the whole of bootstrap/jQuery at each page reload – GettnDer Nov 15 '17 at 19:21
  • @GettnDer They are in first place not requested by the same request as the HTML page. They are requested by their own requests which of course have their own responses as well. – BalusC Nov 15 '17 at 20:50
  • That's what I figured, but wanted to make sure. Specially since I'll probably deal with it on a global-filter basis – GettnDer Nov 15 '17 at 22:31
  • Could someone tell me how to fix this in Django? Everything I tried doesn't work for Safari – Hussain Feb 05 '18 at 02:34
  • In ASP.NET would code be put in Page_Load or Page_Init? – Dov Miller Jun 12 '19 at 10:36
  • The following post was helpful to me - I've used a combination of this answer and that one: https://stackoverflow.com/questions/24604921/ie-9-ignores-etag-value-change-and-returns-cached-response – JD Smith Aug 26 '19 at 18:30
  • This is the most detailed answer on a question that is really tricky. Thank you! – Alexander Nov 24 '20 at 12:59
  • @haylem Thanks for sharing that blog post, but the primary source is now gone. I found it on the Internet Archive Wayback Machine: https://web.archive.org/web/20150722044610/http://blogs.msdn.com/b/ieinternals/archive/2009/10/02/internet-explorer-cannot-download-over-https-when-no-cache.aspx – kevinarpe May 12 '21 at 12:18
261

(hey, everyone: please don't just mindlessly copy&paste all headers you can find)

First of all, Back button history is not a cache:

The freshness model (Section 4.2) does not necessarily apply to history mechanisms. That is, a history mechanism can display a previous representation even if it has expired.

In the old HTTP spec, the wording was even stronger, explicitly telling browsers to disregard cache directives for back button history.

Back is supposed to go back in time (to the time when the user was logged in). It does not navigate forward to a previously opened URL.

However, in practice, the cache can influence the back button, in very specific circumstances:

  • Page must be delivered over HTTPS, otherwise, this cache-busting won't be reliable. Plus, if you're not using HTTPS, then your page is vulnerable to login stealing in many other ways.
  • You must send Cache-Control: no-store, must-revalidate (some browsers observe no-store and some observe must-revalidate)

You never need any of:

  • <meta> with cache headers — it doesn't work at all. Totally useless.
  • post-check/pre-check — it's an IE-only directive that only applies to cachable resources.
  • Sending the same header twice or in dozen parts. Some PHP snippets out there actually replace previous headers, resulting in only the last one being sent.

If you want, you could add:

  • no-cache or max-age=0, which will make resource (URL) "stale" and require browsers to check with the server if there's a newer version (no-store already implies this even stronger).
  • Expires with a date in the past for HTTP/1.0 clients (although real HTTP/1.0-only clients are completely non-existent these days).

Bonus: The new HTTP caching RFC.

Farzad Karimi
  • 701
  • 10
  • 25
Kornel
  • 91,239
  • 30
  • 200
  • 278
  • 2
    will this have any side-effect on the performance of the website in terms of loading time ? how no-store , no-cache , must-revalidate affect performance ? – Raman Ghai May 22 '13 at 14:43
  • 1
    @RamanGhai Disabling cache generally hurts performance (and all 3 options you've mentioned disable caching). It may make CDNs and ISP proxies (e.g. commonly used by mobile operators) ineffective. It doesn't hurt first load by a new user (aside from the proxy issue), but then subsequent navigation may be a lot slower. – Kornel May 22 '13 at 16:01
  • 1
    @porneL you state that we must send `Cache-Control: must-revalidate`. Why not send `Cache-Control: no-cache` since `no-cache` already implies `must-revalidate`? http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.1 – Pacerier Aug 25 '13 at 19:15
  • 4
    @Pacerier the relationship of `no-cache` with `must-revalidate` is true for cache, but back history is not a cache. [Browsers special-case explicit `must-revalidate` to control history behavior](https://bugzilla.mozilla.org/show_bug.cgi?id=567365). – Kornel Aug 27 '13 at 17:29
  • 1
    @porneL, Hmm is there a supporting RFC that states that's the desired behavior? – Pacerier Aug 29 '13 at 03:35
  • @porneL, I've just tested your solution (https + must-revalidate). It works on Opera but not Chrome, IE, Safari, FireFox.... – Pacerier Jun 22 '14 at 12:58
  • Despite MDN and Google saying otherwise, 'no-store' does not currently prevent back button caching in Chrome v90. I had to use 'no-cache, no-store'. I wasted hours trying 'no-store' by itself. – Stevethemacguy Apr 27 '21 at 21:32
114

As @Kornel stated, what you want is not to deactivate the cache, but to deactivate the history buffer. Different browsers have their own subtle ways to disable the history buffer.

In Chrome (v28.0.1500.95 m) we can do this only by Cache-Control: no-store.

In FireFox (v23.0.1) any one of these will work:

  1. Cache-Control: no-store

  2. Cache-Control: no-cache (https only)

  3. Pragma: no-cache (https only)

  4. Vary: * (https only)

In Opera (v12.15) we only can do this by Cache-Control: must-revalidate (https only).

In Safari (v5.1.7, 7534.57.2) any one of these will work:

  1. Cache-Control: no-store
    <body onunload=""> in html

  2. Cache-Control: no-store (https only)

In IE8 (v8.0.6001.18702IC) any one of these will work:

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

  2. Cache-Control: no-cache

  3. Cache-Control: no-store

  4. Cache-Control: must-revalidate
    Expires: 0

  5. Cache-Control: must-revalidate
    Expires: Sat, 12 Oct 1991 05:00:00 GMT

  6. Pragma: no-cache (https only)

  7. Vary: * (https only)

Combining the above gives us this solution which works for Chrome 28, FireFox 23, IE8, Safari 5.1.7, and Opera 12.15: Cache-Control: no-store, must-revalidate (https only)

Note that https is needed because Opera wouldn't deactivate history buffer for plain http pages. If you really can't get https and you are prepared to ignore Opera, the best you can do is this:

Cache-Control: no-store
<body onunload="">

Below shows the raw logs of my tests:

HTTP:

  1. Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fail: Opera 12.15
    Success: Chrome 28, FireFox 23, IE8, Safari 5.1.7

  2. Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fail: Opera 12.15
    Success: Chrome 28, FireFox 23, IE8, Safari 5.1.7

  3. Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    Fail: Safari 5.1.7, Opera 12.15
    Success: Chrome 28, FireFox 23, IE8

  4. Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    Fail: Safari 5.1.7, Opera 12.15
    Success: Chrome 28, FireFox 23, IE8

  5. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fail: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Success: IE8

  6. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fail: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Success: IE8

  7. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fail: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Success: IE8

  8. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fail: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Success: IE8

  9. Cache-Control: no-store
    Fail: Safari 5.1.7, Opera 12.15
    Success: Chrome 28, FireFox 23, IE8

  10. Cache-Control: no-store
    <body onunload="">
    Fail: Opera 12.15
    Success: Chrome 28, FireFox 23, IE8, Safari 5.1.7

  11. Cache-Control: no-cache
    Fail: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Success: IE8

  12. Vary: *
    Fail: Chrome 28, FireFox 23, IE8, Safari 5.1.7, Opera 12.15
    Success: none

  13. Pragma: no-cache
    Fail: Chrome 28, FireFox 23, IE8, Safari 5.1.7, Opera 12.15
    Success: none

  14. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fail: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Success: IE8

  15. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fail: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Success: IE8

  16. Cache-Control: must-revalidate, max-age=0
    Fail: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Success: IE8

  17. Cache-Control: must-revalidate
    Expires: 0
    Fail: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Success: IE8

  18. Cache-Control: must-revalidate
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Fail: Chrome 28, FireFox 23, Safari 5.1.7, Opera 12.15
    Success: IE8

  19. Cache-Control: private, must-revalidate, proxy-revalidate, s-maxage=0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fail: Chrome 28, FireFox 23, IE8, Safari 5.1.7, Opera 12.15
    Success: none

HTTPS:

  1. Cache-Control: private, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    <body onunload="">
    Fail: Chrome 28, FireFox 23, IE8, Safari 5.1.7, Opera 12.15
    Success: none

  2. Cache-Control: private, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    <body onunload="">
    Fail: Chrome 28, FireFox 23, IE8, Safari 5.1.7, Opera 12.15
    Success: none

  3. Vary: *
    Fail: Chrome 28, Safari 5.1.7, Opera 12.15
    Success: FireFox 23, IE8

  4. Pragma: no-cache
    Fail: Chrome 28, Safari 5.1.7, Opera 12.15
    Success: FireFox 23, IE8

  5. Cache-Control: no-cache
    Fail: Chrome 28, Safari 5.1.7, Opera 12.15
    Success: FireFox 23, IE8

  6. Cache-Control: private, no-cache, max-age=0, proxy-revalidate, s-maxage=0
    Fail: Chrome 28, Safari 5.1.7, Opera 12.15
    Success: FireFox 23, IE8

  7. Cache-Control: private, no-cache, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    Fail: Chrome 28, Safari 5.1.7, Opera 12.15
    Success: FireFox 23, IE8

  8. Cache-Control: private, no-cache, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    Fail: Chrome 28, Safari 5.1.7, Opera 12.15
    Success: FireFox 23, IE8

  9. Cache-Control: must-revalidate
    Fail: Chrome 28, FireFox 23, IE8, Safari 5.1.7
    Success: Opera 12.15

  10. Cache-Control: private, must-revalidate, proxy-revalidate, s-maxage=0
    <body onunload="">
    Fail: Chrome 28, FireFox 23, IE8, Safari 5.1.7
    Success: Opera 12.15

  11. Cache-Control: must-revalidate, max-age=0
    Fail: Chrome 28, FireFox 23, Safari 5.1.7
    Success: IE8, Opera 12.15

  12. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fail: Chrome 28, Safari 5.1.7
    Success: FireFox 23, IE8, Opera 12.15

  13. Cache-Control: private, no-cache, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fail: Chrome 28, Safari 5.1.7
    Success: FireFox 23, IE8, Opera 12.15

  14. Cache-Control: no-store
    Fail: Opera 12.15
    Success: Chrome 28, FireFox 23, IE8, Safari 5.1.7

  15. Cache-Control: private, no-cache, no-store, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fail: Opera 12.15
    Success: Chrome 28, FireFox 23, IE8, Safari 5.1.7

  16. Cache-Control: private, no-cache, no-store, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    <body onunload="">
    Fail: Opera 12.15
    Success: Chrome 28, FireFox 23, IE8, Safari 5.1.7

  17. Cache-Control: private, no-cache
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    Fail: Chrome 28, Safari 5.1.7, Opera 12.15
    Success: FireFox 23, IE8

  18. Cache-Control: must-revalidate
    Expires: 0
    Fail: Chrome 28, FireFox 23, Safari 5.1.7,
    Success: IE8, Opera 12.15

  19. Cache-Control: must-revalidate
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Fail: Chrome 28, FireFox 23, Safari 5.1.7,
    Success: IE8, Opera 12.15

  20. Cache-Control: private, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: 0
    <body onunload="">
    Fail: Chrome 28, FireFox 23, Safari 5.1.7,
    Success: IE8, Opera 12.15

  21. Cache-Control: private, must-revalidate, max-age=0, proxy-revalidate, s-maxage=0
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    <body onunload="">
    Fail: Chrome 28, FireFox 23, Safari 5.1.7,
    Success: IE8, Opera 12.15

  22. Cache-Control: private, must-revalidate
    Expires: Sat, 12 Oct 1991 05:00:00 GMT
    Pragma: no-cache
    Vary: *
    Fail: Chrome 28, Safari 5.1.7
    Success: FireFox 23, IE8, Opera 12.15

  23. Cache-Control: no-store, must-revalidate
    Fail: none
    Success: Chrome 28, FireFox 23, IE8, Safari 5.1.7, Opera 12.15

Ritesh
  • 7,085
  • 2
  • 35
  • 41
Pacerier
  • 76,400
  • 86
  • 326
  • 602
  • 5
    I know this was posted a couple years back but it was an interesting read. This problem has been driving me crazy for a few months now, body seems to really know how to deal with cache control. I have seen a few people using the `` but it seems more like a way around the actual problem. I've tried using the .htaccess and modifying the headers that way, if I use HTTPS should it work that way? It's mainly safari where the problem arrises most. – Jordan Jul 15 '15 at 22:22
  • 6
    @Jordan, Per the logs above if you have HTTPS then adding `Cache-Control: no-store` would do the trick. `` is only needed when you don't have HTTPS. – Pacerier Nov 26 '15 at 04:52
43

I found the web.config route useful (tried to add it to the answer but doesn't seem to have been accepted so posting here)

<configuration>
<system.webServer>
    <httpProtocol>
        <customHeaders>
            <add name="Cache-Control" value="no-cache, no-store, must-revalidate" />
            <!-- HTTP 1.1. -->
            <add name="Pragma" value="no-cache" />
            <!-- HTTP 1.0. -->
            <add name="Expires" value="0" />
            <!-- Proxies. -->
        </customHeaders>
    </httpProtocol>
</system.webServer>

And here is the express / node.js way of doing the same:

app.use(function(req, res, next) {
    res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
    res.setHeader('Pragma', 'no-cache');
    res.setHeader('Expires', '0');
    next();
});
Royi Namir
  • 131,490
  • 121
  • 408
  • 714
Joseph Connolly
  • 640
  • 8
  • 19
  • For web.config I would modify just a bit to apply the custom headers only for those scripts which we know are loaded dynamically / using requirejs. Assuming your scripts are found in client folder: ..... – Ibrahim ben Salah Apr 12 '15 at 09:56
  • For who is may wondering what `web.conf` is: It is the main settings and configuration file for an `ASP.NET` web application. It is an XML document that resides in the root directory. ([wiki](https://en.wikipedia.org/wiki/Web.config)). – another Dec 13 '16 at 12:46
30

I found that all of the answers on this page still had problems. In particular, I noticed that none of them would stop IE8 from using a cached version of the page when you accessed it by hitting the back button.

After much research and testing, I found that the only two headers I really needed were:

Cache-Control: no-store
Vary: *

For an explanation of the Vary header, check out http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6

On IE6-8, FF1.5-3.5, Chrome 2-3, Safari 4, and Opera 9-10, these headers caused the page to be requested from the server when you click on a link to the page, or put the URL directly in the address bar. That covers about 99% of all browsers in use as of Jan '10.

On IE6, and Opera 9-10, hitting the back button still caused the cached version to be loaded. On all other browsers I tested, they did fetch a fresh version from the server. So far, I haven't found any set of headers that will cause those browsers to not return cached versions of pages when you hit the back button.

Update: After writing this answer, I realized that our web server is identifying itself as an HTTP 1.0 server. The headers I've listed are the correct ones in order for responses from an HTTP 1.0 server to not be cached by browsers. For an HTTP 1.1 server, look at BalusC's answer.

Community
  • 1
  • 1
Chris Vasselli
  • 11,894
  • 4
  • 42
  • 47
  • 5
    **This works for IE8's back button!!** AFter trying everything in every other suggestion, adding the "Vary: *" header is apparently the only thing that can force IE8 to reload the page when the user presses the back button. And this **does** work on HTTP/1.1 servers. – coredumperror Mar 22 '13 at 21:38
  • Combined with the headers suggested by BarlusC, plus a JS snippet that calls window.location.reload() when the onPageShow event triggers with the "persisted" attribute (needed for Safari), _every_ browser I've tested successfully forces a reload from the server when the user uses the Back button. – coredumperror Mar 22 '13 at 21:46
  • 2
    @CoreDumpError, oh you should not assume JavaScript is enabled. – Pacerier Aug 25 '13 at 19:55
  • @Pacerier At the time I wrote the answer in 2010, this worked on what were then the latest versions of both Safari and Opera, with our server identifying itself as an HTTP 1.0 server. Unfortunately, I don't have any way to easily test this anymore, so I can't say anything definitive about the latest versions of these browsers. – Chris Vasselli Aug 31 '13 at 00:45
  • 1
    What were the browser versions you tested with? – Pacerier Oct 02 '13 at 12:35
  • According to what I wrote then, it looks like I tested IE6 through 8, FF1.5 through 3.5, Chrome 2 and 3, Safari 4, and Opera 9 and 10. – Chris Vasselli Oct 03 '13 at 05:21
23

After a bit of research we came up with the following list of headers that seemed to cover most browsers:

In ASP.NET we added these using the following snippet:

Response.ClearHeaders(); 
Response.AppendHeader("Cache-Control", "no-cache"); //HTTP 1.1
Response.AppendHeader("Cache-Control", "private"); // HTTP 1.1
Response.AppendHeader("Cache-Control", "no-store"); // HTTP 1.1
Response.AppendHeader("Cache-Control", "must-revalidate"); // HTTP 1.1
Response.AppendHeader("Cache-Control", "max-stale=0"); // HTTP 1.1 
Response.AppendHeader("Cache-Control", "post-check=0"); // HTTP 1.1 
Response.AppendHeader("Cache-Control", "pre-check=0"); // HTTP 1.1 
Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0 
Response.AppendHeader("Expires", "Sat, 26 Jul 1997 05:00:00 GMT"); // HTTP 1.0 

Found from: http://forums.asp.net/t/1013531.aspx

edwgiz
  • 685
  • 5
  • 14
Edward Wilde
  • 23,831
  • 8
  • 52
  • 64
  • 39
    @bart: Even more troublesome yet is that the 26th of July in 1997 was a Saturday, not a Monday... – Cᴏʀʏ Feb 13 '12 at 19:51
  • 7
    `Cache-Control: no-cache` and `Cache-Control: private` clash - you should never get both together: the former tells browsers and proxies not to cache at all, the latter tells proxies not to cache but lets browsers hold their own private copy. I'm not sure which setting the browser will follow, but it's unlikely to be consistent between browsers and versions. – Keith Oct 29 '12 at 11:48
  • 1
    Do not use pre-check and post-check. http://blogs.msdn.com/b/ieinternals/archive/2009/07/20/using-post_2d00_check-and-pre_2d00_check-cache-directives.aspx – EricLaw Apr 14 '15 at 12:54
  • this didn't work for me - using asp.net 4.5 the code runs but does't produce the required result. I had to follow this: http://stackoverflow.com/questions/22443932/cache-control-no-store-must-revalidate-not-sent-to-client-browser-in-iis7-as – Andy Dec 18 '15 at 17:00
9

For ASP.NET Core, create a simple middleware class:

public class NoCacheMiddleware
{
    private readonly RequestDelegate m_next;

    public NoCacheMiddleware( RequestDelegate next )
    {
        m_next = next;
    }

    public async Task Invoke( HttpContext httpContext )
    {
        httpContext.Response.OnStarting( ( state ) =>
        {
            // ref: http://stackoverflow.com/questions/49547/making-sure-a-web-page-is-not-cached-across-all-browsers
            httpContext.Response.Headers.Append( "Cache-Control", "no-cache, no-store, must-revalidate" );
            httpContext.Response.Headers.Append( "Pragma", "no-cache" );
            httpContext.Response.Headers.Append( "Expires", "0" );
            return Task.FromResult( 0 );
        }, null );

        await m_next.Invoke( httpContext );
    }
}

then register it with Startup.cs

app.UseMiddleware<NoCacheMiddleware>();

Make sure you add this somewhere after

app.UseStaticFiles();
kspearrin
  • 8,280
  • 9
  • 41
  • 68
  • I would suggest to use constants from Microsoft.Net.Http.Headers.HeaderNames instead of string literals "Cache-Controls", "Pragma" and "Expires". – Victor Sharovatov Jun 07 '18 at 08:16
9

The use of the pragma header in the response is a wives tale. RFC2616 only defines it as a request header

http://www.mnot.net/cache_docs/#PRAGMA

Dave Cheney
  • 5,137
  • 2
  • 16
  • 23
  • 4
    This is a good example of why you need to go beyond the specs. If the specs were always crystal clear, there wouldn't be much point for sites like StackOverflow. From [Microsoft](http://support.microsoft.com/kb/234067) _For purposes of backward compatibility with HTTP 1.0 servers, Internet Explorer supports a special usage of the HTTP Pragma: no-cache header. If the client communicates with the server over a secure connection (https://) and the server returns a Pragma: no-cache header with the response, Internet Explorer does not cache the response._ – michaelok Jul 10 '12 at 18:42
  • @michaelok: Your reference is valid, but misses the larger point-- Set a proper Cache-Control/Expires and you don't need pragma. – EricLaw Apr 14 '15 at 12:53
9

DISCLAIMER: I strongly suggest reading @BalusC's answer. After reading the following caching tutorial: http://www.mnot.net/cache_docs/ (I recommend you read it, too), I believe it to be correct. However, for historical reasons (and because I have tested it myself), I will include my original answer below:


I tried the 'accepted' answer for PHP, which did not work for me. Then I did a little research, found a slight variant, tested it, and it worked. Here it is:

header('Cache-Control: no-store, private, no-cache, must-revalidate');     // HTTP/1.1
header('Cache-Control: pre-check=0, post-check=0, max-age=0, max-stale = 0', false);  // HTTP/1.1
header('Pragma: public');
header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');                  // Date in the past  
header('Expires: 0', false); 
header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT');
header ('Pragma: no-cache');

That should work. The problem was that when setting the same part of the header twice, if the false is not sent as the second argument to the header function, header function will simply overwrite the previous header() call. So, when setting the Cache-Control, for example if one does not want to put all the arguments in one header() function call, he must do something like this:

header('Cache-Control: this');
header('Cache-Control: and, this', false);

See more complete documentation here.

Steven Oxley
  • 6,148
  • 6
  • 37
  • 54
  • 15
    This is full of myths. pre-check and post-check are IE-only, relevant only for cached responses, and 0 value is a no-op. max-stale is proxy request header, not server response header. Expires accepts only single value. More than one will cause this header to be ignored. – Kornel Oct 19 '08 at 18:19
  • 1
    @porneL, will you be submitting a competing answer that deals with these myths correctly? – Oddthinking Nov 28 '08 at 01:56
  • @Oddthinking, looks like http://stackoverflow.com/questions/49547/making-sure-a-web-page-is-not-cached-across-all-browsers/2068353#2068353 is a competing answer. – Mike Ottum Jan 14 '10 at 23:55
  • @Pacerier yes, as I say in the disclaimer, use BalusC's answer. – Steven Oxley Sep 04 '13 at 16:17
8

There's a bug in IE6

Content with "Content-Encoding: gzip" is always cached even if you use "Cache-Control: no-cache".

http://support.microsoft.com/kb/321722

You can disable gzip compression for IE6 users (check the user agent for "MSIE 6")

Edson Medina
  • 8,352
  • 3
  • 37
  • 48
8

These directives does not mitigate any security risk. They are really intended to force UA's to refresh volatile information, not keep UA's from being retaining information. See this similar question. At the very least, there is no guarantee that any routers, proxies, etc. will not ignore the caching directives as well.

On a more positive note, policies regarding physical access to computers, software installation, and the like will put you miles ahead of most firms in terms of security. If the consumers of this information are members of the public, the only thing you can really do is help them understand that once the information hits their machine, that machine is their responsibility, not yours.

Community
  • 1
  • 1
Dustman
  • 4,567
  • 10
  • 30
  • 40
7

If you're facing download problems with IE6-IE8 over SSL and cache:no-cache header (and similar values) with MS Office files you can use cache:private,no-store header and return file on POST request. It works.

Albert
  • 71
  • 1
  • 1
7

Setting the modified http header to some date in 1995 usually does the trick.

Here's an example:

Expires: Wed, 15 Nov 1995 04:58:08 GMT
Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
Cache-Control: no-cache, must-revalidate
Anders Sandvig
  • 19,763
  • 16
  • 56
  • 71
  • 1
    Setting a long-ago Last-Modified has no impact on caching, other than letting a cached response be used longer due to heuristic revalidation. – EricLaw Apr 14 '15 at 12:52
7

The RFC for HTTP 1.1 says the proper method is to add an HTTP Header for:

Cache-Control: no-cache

Older browsers may ignore this if they are not properly compliant to HTTP 1.1. For those you can try the header:

Pragma: no-cache

This is also supposed to work for HTTP 1.1 browsers.

Chris Dail
  • 24,255
  • 9
  • 61
  • 73
  • 1
    The spec indicates that the response must not be reused without revalidation. It is the Cache-Control:no-store which is the official method to indicate that the response not even be stored in a cache in the first place. – AnthonyWJones Sep 19 '08 at 18:14
7

The PHP documentation for the header function has a rather complete example (contributed by a third party):

    header('Pragma: public');
    header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");                  // Date in the past   
    header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT');
    header('Cache-Control: no-store, no-cache, must-revalidate');     // HTTP/1.1
    header('Cache-Control: pre-check=0, post-check=0, max-age=0', false);    // HTTP/1.1
    header ("Pragma: no-cache");
    header("Expires: 0", false);
Community
  • 1
  • 1
Grey Panther
  • 12,110
  • 6
  • 40
  • 63
  • 11
    This is obviously wrong. Second calls to header() for Expires, Cache-control and Pragma completely overwrite previously set values. – Kornel Oct 19 '08 at 18:22
  • 1
    @porneL: No the do not overwrite previously set values as he pass false as a 2nd parameter, telling to not override previous values. – Julien Palard Feb 14 '13 at 10:52
  • 1
    @JulienPalard the answer has been edited after I made my comment. It still doesn't make much sense. – Kornel Feb 15 '13 at 10:31
  • Do not send multiple Cache-Control headers if you want to work in IE prior to 9. Don't EVER send pre-check or post-check. http://blogs.msdn.com/b/ieinternals/archive/2009/07/20/using-post_2d00_check-and-pre_2d00_check-cache-directives.aspx – EricLaw Apr 14 '15 at 12:51
6

in my case i fix the problem in chrome with this

<form id="form1" runat="server" autocomplete="off">

where i need to clear the content of a previus form data when the users click button back for security reasons

user2321638
  • 61
  • 1
  • 2
  • My mozilla 19.x browser issue also got resolved by the code snippet. autocomplete="off". Thank you. – Satya Nov 22 '13 at 07:33
5

The accepted answer does not appear to work for IIS7+, going by the large number of questions about cache headers not being sent in II7:

And so on

The accepted answer is correct in which headers must be set, but not in how they must be set. This way works with IIS7:

Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.AppendCacheExtension("no-store, must-revalidate");
Response.AppendHeader("Pragma", "no-cache");
Response.AppendHeader("Expires", "-1");

The first line sets Cache-control to no-cache, and the second line adds the other attributes no-store, must-revalidate

Community
  • 1
  • 1
JK.
  • 20,010
  • 29
  • 124
  • 204
  • This works for me: `Response.Cache.SetAllowResponseInBrowserHistory(false); Response.Cache.SetCacheability(HttpCacheability.NoCache); Response.Cache.SetNoStore(); Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);` – Vilx- Apr 29 '15 at 12:02
4

Also, just for good measure, make sure you reset the ExpiresDefault in your .htaccess file if you're using that to enable caching.

ExpiresDefault "access plus 0 seconds"

Afterwards, you can use ExpiresByType to set specific values for the files you want to cache:

ExpiresByType image/x-icon "access plus 3 month"

This may also come in handy if your dynamic files e.g. php, etc. are being cached by the browser, and you can't figure out why. Check ExpiresDefault.

BannerMan
  • 10,370
  • 18
  • 73
  • 121
4

The headers in the answer provided by BalusC does not prevent Safari 5 (and possibly older versions as well) from displaying content from the browser cache when using the browser's back button. A way to prevent this is to add an empty onunload event handler attribute to the body tag:

<body onunload=""> 

This hack apparently breaks the back-forward cache in Safari: Is there a cross-browser onload event when clicking the back button?

Community
  • 1
  • 1
Tobias
  • 61
  • 2
4

I've had best and most consistent results across all browsers by setting Pragma: no-cache

petr k.
  • 7,830
  • 6
  • 39
  • 52
3
//In .net MVC
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public ActionResult FareListInfo(long id)
{
}

// In .net webform
<%@ OutputCache NoStore="true" Duration="0" VaryByParam="*" %>
yongfa365
  • 342
  • 3
  • 7
3

In addition to the headers consider serving your page via https. Many browsers will not cache https by default.

Harry
  • 5,146
  • 6
  • 25
  • 21
1

To complete BalusC -> ANSWER If you are using perl you can use CGI to add HTTP headers.

Using Perl:

Use CGI;    
sub set_new_query() {
        binmode STDOUT, ":utf8";
        die if defined $query;
        $query = CGI->new();
        print $query->header(
                        -expires       => 'Sat, 26 Jul 1997 05:00:00 GMT',
                        -Pragma        => 'no-cache',
                        -Cache_Control => join(', ', qw(
                                            private
                                            no-cache
                                            no-store
                                            must-revalidate
                                            max-age=0
                                            pre-check=0
                                            post-check=0 
                                           ))
        );
    }

Using apache httpd.conf

<FilesMatch "\.(html|htm|js|css|pl)$">
FileETag None
<ifModule mod_headers.c>
Header unset ETag
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"
</ifModule>

Note: When I tried to use the html META, browsers ignored them and cached the page.

Community
  • 1
  • 1
Carlos Escalera Alonso
  • 2,293
  • 1
  • 22
  • 35
  • I don't know what will be the behavior of the Apache with this config : - Pragma/Cache-Control has the same role and you give them 2 differents configs. It's weird. - no-store mean that the ressource should not be cached. So how can it be revalidated ? – Camille Gerin-Roze Oct 16 '20 at 10:05
0

I just want to point out that if someone wants to prevent caching ONLY dynamic content, adding those additional headers should be made programmatically.

I edited configuration file of my project to append no-cache headers, but that also disabled caching static content, which isn't usually desirable. Modifying response headers in code assures that images and style files will be cached.

This is quite obvious, yet still worth mentioning.

And another caution. Be careful using ClearHeaders method from HttpResponse class. It may give you some bruises if you use it recklessly. Like it gave me.

After redirecting on ActionFilterAttribute event the consequences of clearing all headers are losing all session data and data in TempData storage. It's safer to redirect from an Action or don't clear headers when redirection is taking place.

On second thought I discourage all to use ClearHeaders method. It's better to remove headers separately. And to set Cache-Control header properly I'm using this code:

filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
filterContext.HttpContext.Response.Cache.AppendCacheExtension("no-store, must-revalidate");
0

See this link to a Case Study on Caching:

http://securityevaluators.com/knowledge/case_studies/caching/

Summary, according to the article, only Cache-Control: no-store works on Chrome, Firefox and IE. IE accepts other controls, but Chrome and Firefox do not. The link is a good read complete with the history of caching and documenting proof of concept.

YakovL
  • 5,213
  • 10
  • 46
  • 71
Paul
  • 827
  • 6
  • 2
0

I had no luck with <head><meta> elements. Adding HTTP cache related parameters directly (outside of the HTML doc) does indeed work for me.

Sample code in Python using web.py web.header calls follows. I purposefully redacted my personal irrelevant utility code.


    import web
    import sys
    import PERSONAL-UTILITIES

    myname = "main.py"

    urls = (
        '/', 'main_class'
    )

    main = web.application(urls, globals())

    render = web.template.render("templates/", base="layout", cache=False)

    class main_class(object):
        def GET(self):
            web.header("Cache-control","no-cache, no-store, must-revalidate")
            web.header("Pragma", "no-cache")
            web.header("Expires", "0")
            return render.main_form()

        def POST(self):
            msg = "POSTed:"
            form = web.input(function = None)
            web.header("Cache-control","no-cache, no-store, must-revalidate")
            web.header("Pragma", "no-cache")
            web.header("Expires", "0")
            return render.index_laid_out(greeting = msg + form.function)

    if __name__ == "__main__":
        nargs = len(sys.argv)
        # Ensure that there are enough arguments after python program name
        if nargs != 2:
            LOG-AND-DIE("%s: Command line error, nargs=%s, should be 2", myname, nargs)
        # Make sure that the TCP port number is numeric
        try:
            tcp_port = int(sys.argv[1])
        except Exception as e:
            LOG-AND-DIE ("%s: tcp_port = int(%s) failed (not an integer)", myname, sys.argv[1])
        # All is well!
        JUST-LOG("%s: Running on port %d", myname, tcp_port)
        web.httpserver.runsimple(main.wsgifunc(), ("localhost", tcp_port))
        main.run()

Racil Hilan
  • 22,887
  • 12
  • 43
  • 49
Richard Elkins
  • 279
  • 2
  • 6
  • Is this not covered many times already in the answers that have been on the site for years? – Martin Tournoij Aug 10 '16 at 22:33
  • META directives work in Internet Explorer and versions of Edge 18 and earlier. Modern browsers do not support them. https://crbug.com/2763 – EricLaw Mar 02 '20 at 19:51
0

i have solved in this way.

2 considerations:

1) the server side events are not fired on back click, instead of javascript.

2) i have 2 javascript to read/write cookies

function setCookie(name, value, days)
{
    var expires = "";
    if (days)
    {
        var date = new Date();
        date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
        expires = "; expires=" + date.toUTCString();
    }
    document.cookie = name + "=" + (value || "") + expires + "; path=/";
}

function getCookie(name)
{
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');

    for (var i = ca.length - 1; i >= 0; i--)
    {
        var c = ca[i];
        while (c.charAt(0) == ' ')
        {
            c = c.substring(1, c.length);
        }

        if (c.indexOf(nameEQ) == 0)
        {
            return c.substring(nameEQ.length, c.length);
        }
    }
    return null;
}

in my Page_Load i inserted this: (this is NOT fired on back click)

    protected void Page_Load(object sender, EventArgs e)
    {
       Page.RegisterClientScriptBlock("", "<script>setCookie('" + Session.SessionID + "', '" + Login + "', '100');</script>");
    }

where 'Login' is my id value that is -1 after logout (you could use something else, a boolean for example).

then in my page i added this: (this IS fired on back click)

<script type="text/javascript">
if (getCookie('<%= Session.SessionID %>') < 0)
        {
            if (history.length > 0)
            {
                history.go(+1);
            }
        }

</script>

nothing else.

with this solution back click is enable on every page and disable only after logout on each page on the same browser.

elle0087
  • 712
  • 5
  • 20
-1

you can use location block for set individual file instead of whole app get caching in IIS

 <location path="index.html">
    <system.webServer>
      <httpProtocol>
        <customHeaders>
          <add name="Cache-Control" value="no-cache" />
        </customHeaders>
      </httpProtocol>
    </system.webServer>
  </location>
CodeMind
  • 517
  • 5
  • 16
-3

Not sure if my answer sounds simple and stupid, and perhaps it has already been known to you since long time ago, but since preventing someone from using browser back button to view your historical pages is one of your goals, you can use:

window.location.replace("https://www.example.com/page-not-to-be-viewed-in-browser-history-back-button.html");

Of course, this may not be possible to be implemented across the entire site, but at least for some critical pages, you can do that. Hope this helps.

Antonio Ooi
  • 866
  • 7
  • 21