Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do we control web page caching, across all browsers?

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.

like image 513
Edward Wilde Avatar asked Sep 08 '08 12:09

Edward Wilde


People also ask

How do I stop browser caching my page?

Here's how... When you're in Google Chrome, click on View, then select Developer, then Developer Tools. Alternatively, you can right click on a page in Chrome, then click Inspect. Click on the Network tab, then check the box to Disable cache.

How does cache control work in a Web browser?

Cache-Control is a HTTP cache header that contains a set of parameters to define the browser's caching policies in the client requests and server responses. When a client makes a request to the server, the browser can cache, or store copies of resources for faster access and lower latency.

What provides control over caching?

The Cache-Control HTTP header field holds directives (instructions) — in both requests and responses — that control caching in browsers and shared caches (e.g. Proxies, CDNs).


2 Answers

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:

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.

like image 83
45 revs, 25 users 55% Avatar answered Sep 21 '22 08:09

45 revs, 25 users 55%


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

like image 37
Kornel Avatar answered Sep 18 '22 08:09

Kornel