200

I am looking for a method to disable the browser cache for an entire ASP.NET MVC Website

I found the following method:

Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
Response.Cache.SetNoStore();

And also a meta tag method (it won't work for me, since some MVC actions send partial HTML/JSON through Ajax, without a head, meta tag).

<meta http-equiv="PRAGMA" content="NO-CACHE">

But I am looking for a simple method to disable the browser cache for an entire website.

alamin
  • 1,791
  • 1
  • 22
  • 28
Palani
  • 8,532
  • 10
  • 51
  • 62
  • 3
    possible duplicate of [How can I disable client side and proxy caching in ASP.NET MVC?](http://stackoverflow.com/questions/860678/how-can-i-disable-client-side-and-proxy-caching-in-asp-net-mvc) – Robert MacLean Sep 10 '11 at 10:17

8 Answers8

368

Create a class that inherits from IActionFilter.

public class NoCacheAttribute : ActionFilterAttribute
{  
    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        filterContext.HttpContext.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
        filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false);
        filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
        filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
        filterContext.HttpContext.Response.Cache.SetNoStore();

        base.OnResultExecuting(filterContext);
    }
}

Then put attributes where needed...

[NoCache]
[HandleError]
public class AccountController : Controller
{
    [NoCache]
    [Authorize]
    public ActionResult ChangePassword()
    {
        return View();
    }
}
John Gietzen
  • 45,925
  • 29
  • 140
  • 183
JKG
  • 4,359
  • 2
  • 15
  • 10
  • 19
    Rather than HttpContext.Current.Response, you should probably use filterContext.HttpContext.Response since HttpContext.Current returns the pre-MVC HttpContext object and the filterContext.HttpContext returns the post-MVC HttpContextBase. It increases testability and consistency. – mkedobbs Jan 25 '10 at 20:57
  • Good catch and lazy on my part, I've made the changes. – JKG Jan 27 '10 at 03:09
  • 5
    IActionFilter is already implemented on the ActionFilterAttribute, so you don't need to repeat it. – Andrew Davey Apr 22 '10 at 08:50
  • 107
    In current versions of ASP.NET MVC you can simply use OutputCacheAttribute to prevent caching: [OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")] – Ashley Tate May 14 '10 at 18:34
  • Isn't the OutputCacheAttribute only for the server caching? But Palani's question is about browser caching.. – jhexp Jun 28 '10 at 15:39
  • 1
    @jhexp, OutputCache and setting Response.Cache is (almost) the same thing, except some minor limitations on OutputCache. – Bill Yang Jul 14 '10 at 17:33
  • @bill-yang Thats true, good point. But there is still a difference between browser caching (controlled by the meta tags or SetExpires() etc) and the ASP.NET Output caching (OutputcacheAttribute and Response.Cache()). For example you may want to completely disable browser caching, but use output caching (to achieve "donut" caching). Its bit offtopic now, but the original question was only about browser cache, eg setting the right metatags and http response headers. – jhexp Jul 20 '10 at 06:16
  • 9
    I'd like to point out that I just spent several days using every "put this in your code to stop caching" solution under the sun for ASP.NET MVC, including the accepted answer to this question, to no avail. This answer - the attribute - worked. +1M Rep if I could... – Tom Kidd Jul 22 '10 at 22:14
  • 5
    You might want to add `if (filterContext.IsChildAction) return;` at the top - this will prevent the outer action to be 'no cached' if it calls a child action that is decorated with `NoCache` attribute. In other words `NoCache` attribute won't leak to other actions if they execute child actions. Also, the class name should be `NoCacheAttribute` to comply with generally accepted naming convention for attributes. – Jakub Konecki May 23 '12 at 15:26
  • The caching in my case was due to an html. We changed on homepage some a href links from blabla.aspx to #, but after a post and press back back button in IE9, the presented html was the old one with blabla.aspx :-/ Output cache did not solve our problem, and we don't want to mess with cacheability. So we changed the port of the web application in VS2010 and now it seems to be fixed. – Junior Mayhé Jul 23 '12 at 17:32
  • Another benefit of this method is that it does not cause issues with RenderAction as the [OutputCache] attribute can see http://stackoverflow.com/questions/4797968/asp-net-mvc-3-partial-page-output-caching-not-honoring-config-settings/4800140#4800140 – TJB Jan 03 '13 at 16:16
  • +1 Nice answer! But what about using `OnResultExecuted` instead of `OnResultExecuting`? Things can be changed when the ActionResult has a override `ExecuteResult` method. `OnResultExecuted` did the operation at last which guarantees the result won't be cached. Is it better? – Cheng Chen Jan 20 '13 at 07:19
  • How would you override this to enable caching in some Actions of the Controller while the NoCache attribute was put on the Controller? Can't get that to work. – mwijnands Aug 27 '13 at 23:42
  • 1
    This solution didn't work for me either. I'm losing the will to live. – Wilky Oct 22 '13 at 13:07
  • +1 for the example. Makes it easier to understand when using it for the first time. – Nolonar Aug 18 '14 at 14:00
  • This answer didn't seem to work when testing with chrome and the default IISExpress. I checked chrome dev tools if the headers were being set and they were. I also tried adding "SetAllowResponseInBrowserHistory(false)", however chrome just doesn't care and the back button works fine; no "page expired" message. – Slight Apr 02 '15 at 16:50
  • 1
    @AshleyTate that didn't seem to work for me, "duration must be a positive number" when applying it to the controller – Michiel Cornille Aug 04 '15 at 09:55
  • I have two comments for the first code block: Firstly, the SetExpires() call is actually not necessary, since the SetCacheability() also sets the expiration to "-1". Secondly, I don't think the SetValidUntilExpires() call is necessary, since logically it only changes the behavior of the ASP.NET pipeline to not care about cache headers from the client, but if I understand correctly we disable all caching anyway so this is probably not necessary. – chrischu Apr 14 '16 at 08:47
132

Instead of rolling your own, simply use what's provided for you.

As mentioned previously, do not disable caching for everything. For instance, jQuery scripts used heavily in ASP.NET MVC should be cached. Actually ideally you should be using a CDN for those anyway, but my point is some content should be cached.

What I find works best here rather than sprinkling the [OutputCache] everywhere is to use a class:

[System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public class NoCacheController  : Controller
{
}

All of your controllers you want to disable caching for then inherit from this controller.

If you need to override the defaults in the NoCacheController class, simply specify the cache settings on your action method and the settings on your Action method will take precedence.

[HttpGet]
[OutputCache(NoStore = true, Duration = 60, VaryByParam = "*")]
public ViewResult Index()
{
  ...
}
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Adam Tuliper - MSFT
  • 29,569
  • 4
  • 49
  • 68
  • In my experience changing the OutputCache duration merely changes the duration that the page is stored in IIS's OutputCache - i.e. the dynamic content cache on the web server - it has nothing to do with browser caching settings. The answer above with an ActionFilter solves the problem of disabling browser caching. – ozziepeeps Aug 22 '11 at 16:05
  • 4
    @Ozziepeeps, your comment is not correct. The msdn docs discuss browser caching as well as a simple test will show this attribute changes the cache-control response header to "Cache-Control: public, no-store, max-age=0" from "Cache-Control: private" without using the attribute. – Adam Tuliper - MSFT Aug 22 '11 at 19:47
  • 2
    also fyi - you can control all three locations (server, proxy, client) with this attribute so absolutely can control beyond the server cache. See http://www.asp.net/mvc/tutorials/improving-performance-with-output-caching-cs for some additional details. – Adam Tuliper - MSFT Aug 22 '11 at 23:44
  • Ok, thanks @Adam Tuliper. I guess I must have been seeing some spurious behaviour. In my project, we actually needed to disable Output Caching on IIS for other reasons, so resorted to implementing a no-cache action filter similar to the above – ozziepeeps Aug 24 '11 at 10:45
  • 1
    +1 "If you need to override the defaults in the NoCacheController class simply specify the cache settings on your action method and the settings on your Action method will take precedence." – Korayem Apr 01 '12 at 18:03
  • 2
    Please note that if you use `[System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]` at the class level, you can't have PartialViews in your class. – Vivian River Apr 04 '12 at 18:47
  • @Daniel Allen Langdon You are right this method really messes up peopel that use partials I'm going to try the NOCache attribute instead – Chris Stephens Jul 13 '12 at 18:37
  • 1
    The OutputCache method didn't prevent IE caching when two conditions were present: 1) the action took no parameters and 2) the action returned only text via Content(someText). When I return JSON and take a parameter, IE caching is properly defeated. – Kasey Speakman Mar 13 '13 at 20:26
  • Interesting @KaseySpeakman , I'll have to test that out a bit more to see why IE does this and if the headers come down or not, but are ignored because its text, etc. Thanks – Adam Tuliper - MSFT Mar 14 '13 at 03:16
  • @DanielAllenLangdon and Chris Stephens - Why wouldn't this work for partial views? – Daniel Schilling Jun 20 '13 at 13:23
  • If I recall one of the params prevents partials from using these settings at a class level. Not sure if this resolved in mvc4 or not though – Adam Tuliper - MSFT Jun 21 '13 at 08:10
  • @Kasey Speakman Thanks for verifying what I have been seeing. This solution does not disable all caching scenarios in IE. – Jace Rhea Sep 20 '13 at 14:33
93
HttpContext.Current.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
HttpContext.Current.Response.Cache.SetValidUntilExpires(false);
HttpContext.Current.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Response.Cache.SetNoStore();

All requests get routed through default.aspx first - so assuming you can just pop in code behind there.

SteveCav
  • 6,231
  • 42
  • 51
Squiggs.
  • 3,037
  • 6
  • 40
  • 73
  • 17
    I would put it into Global.asax.cs in Application_BeginRequest(). I don't trust this default.aspx thing... Another question: does this have precedence over [OutputCache] attributes? – chris166 Jul 21 '09 at 17:31
  • 5
    I like the idea of simply creating a Global Action Filter an putting this stuff in that way. Negates the need to worry about Default.aspx and Global.asax. – Keith Adler Jul 21 '09 at 18:16
  • 13
    Putting this in Application_BeingRequest can cause some issues. If your images get routed through the .net runtime (which can happen if you're using wildcard mapping for nice urls) then no images will be cached on the browser. This can REALLY slow down your page load times as each page request will re-download all images. – herbrandson Mar 31 '10 at 07:24
  • 4
    Using anything programmatically will always override any declared Attribute. In other words, using the OP's code will override any declared [OutputCache] attribute. – Dave Black Sep 13 '11 at 18:05
  • Any thoughts on how to smoke test and verify that the cache disable is actually working? – paaone Mar 18 '13 at 13:22
  • Easiest smoke test would be to Logout and press your back button to see if the previous page (requiring authorization) renders or redirects you to login. A better test would be to use something like FireBug to review caching (you will see lots of 200 OK instead of 304 not modified in the Net panel) – Graeme Wicksted Apr 09 '13 at 15:45
  • @herbrandson, where do you suggest this code is placed if not in the `Application_BeginRequest()`? – ᴍᴀᴛᴛ ʙᴀᴋᴇʀ Aug 07 '15 at 10:45
  • @MatthewT.Baker Unfortunately it's been a little while since I left this response (and since I worked on a .net web app) and I don't recall off hand where we had placed this logic. It seems like there are multiple options. It also partly depends on if you're using web forms or mvc. If you're using mvc, checkout out the answer from JKG for an example using an IActionFilter. – herbrandson Aug 10 '15 at 02:05
10

You may want to disable browser caching for all pages rendered by controllers (i.e. HTML pages), but keep caching in place for resources such as scripts, style sheets, and images. If you're using MVC4+ bundling and minification, you'll want to keep the default cache durations for scripts and stylesheets (very long durations, since the cache gets invalidated based on a change to a unique URL, not based on time).

In MVC4+, to disable browser caching across all controllers, but retain it for anything not served by a controller, add this to FilterConfig.RegisterGlobalFilters:

filters.Add(new DisableCache());

Define DisableCache as follows:

class DisableCache : ActionFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    }
}
Edward Brey
  • 35,877
  • 14
  • 173
  • 224
6

I know this answer is not 100% related to the question, but it might help someone.

If you want to disable the browser cache for the entire ASP.NET MVC Website, but you only want to do this TEMPORARILY, then it is better to disable the cache in your browser.

Here's a screenshot in Chrome

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Carlos Martinez T
  • 6,298
  • 1
  • 31
  • 39
  • This is exactly what I was looking for... during dev, if I change a .js file, it's a major pain to get that to come through immediately when I'm trouble to do little troubleshoot/refresh/test cycles. This is perfect, thank you! Just made my client side debugging life far easier – jleach Jun 10 '16 at 17:16
2

I implemented all the previous answers and still had one view that did not work correctly.

It turned out the name of the view I was having the problem with was named 'Recent'. Apparently this confused the Internet Explorer browser.

After I changed the view name (in the controller) to a different name (I chose to 'Recent5'), the solutions above started to work.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
0

You can try below code in Global.asax file.

protected void Application_BeginRequest()
    {
        Response.Cache.SetCacheability(HttpCacheability.NoCache);
        Response.Cache.SetExpires(DateTime.UtcNow.AddHours(-1));
        Response.Cache.SetNoStore();
    }
NidhinSPradeep
  • 798
  • 2
  • 9
  • 12
-1

UI

<%@ OutPutCache Location="None"%>
<%
    Response.Buffer = true;
    Response.Expires = -1;
    Response.ExpiresAbsolute = System.DateTime.Now.AddSeconds(-1);
    Response.CacheControl = "no-cache";
%>

Background

Context.Response.Cache.SetCacheability(HttpCacheability.NoCache); 
Response.Expires = -1;          
Response.Cache.SetNoStore();
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Alpha
  • 1
  • 1