[http] 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.

This question is related to http caching https http-headers

The answer is


(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 IE-only directive that only applies to cachable resources.
  • Sending same header twice or in dozen parts. Some PHP snippets out there actually replace previous headers, resulting in only 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.


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>

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?


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");

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


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.


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();

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);

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.


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.


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.


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


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.


//In .net MVC
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public ActionResult FareListInfo(long id)
{
}

// In .net webform
<%@ OutputCache NoStore="true" Duration="0" VaryByParam="*" %>

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.


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


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


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.


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


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.


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

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


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.


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();
});

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.


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")


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


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


Examples related to http

Access blocked by CORS policy: Response to preflight request doesn't pass access control check Axios Delete request with body and headers? Read response headers from API response - Angular 5 + TypeScript Android 8: Cleartext HTTP traffic not permitted Angular 4 HttpClient Query Parameters Load json from local file with http.get() in angular 2 Angular 2: How to access an HTTP response body? What is HTTP "Host" header? Golang read request body Angular 2 - Checking for server errors from subscribe

Examples related to caching

Disable nginx cache for JavaScript files How to prevent Browser cache on Angular 2 site? Curl command without using cache Notepad++ cached files location Laravel 5 Clear Views Cache Write-back vs Write-Through caching? Tomcat 8 throwing - org.apache.catalina.webresources.Cache.getResource Unable to add the resource Chrome - ERR_CACHE_MISS How do I use disk caching in Picasso? How to clear gradle cache?

Examples related to https

What's the net::ERR_HTTP2_PROTOCOL_ERROR about? Requests (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available.") Error in PyCharm requesting website Android 8: Cleartext HTTP traffic not permitted ssl.SSLError: tlsv1 alert protocol version Invalid self signed SSL cert - "Subject Alternative Name Missing" How do I make a https post in Node Js without any third party module? Page loaded over HTTPS but requested an insecure XMLHttpRequest endpoint How to force Laravel Project to use HTTPS for all routes? Could not create SSL/TLS secure channel, despite setting ServerCertificateValidationCallback Use .htaccess to redirect HTTP to HTTPs

Examples related to http-headers

Set cookies for cross origin requests Adding a HTTP header to the Angular HttpClient doesn't send the header, why? Passing headers with axios POST request What is HTTP "Host" header? CORS error :Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response Using Axios GET with Authorization Header in React-Native App Axios get access to response header fields Custom header to HttpClient request Send multipart/form-data files with angular using $http Best HTTP Authorization header type for JWT