211

I have an ASP.NET MVC 3 application. This application requests records through jQuery. jQuery calls back to a controller action that returns results in JSON format. I have not been able to prove this, but I'm concerned that my data may be getting cached.

I only want the caching to be applied to specific actions, not for all actions.

Is there an attribute that I can put on an action to ensure that the data does not get cached? If not, how do I ensure that the browser gets a new set of records each time, instead of a cached set?

Luke Girvin
  • 13,221
  • 9
  • 64
  • 84
JavaScript Developer
  • 3,968
  • 11
  • 41
  • 46
  • 1
    If you are guessing that something is being cached, then I recommend that you read up on cache control mechanisms here: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 –  Apr 05 '12 at 10:01

9 Answers9

317

To ensure that JQuery isn't caching the results, on your ajax methods, put the following:

$.ajax({
    cache: false
    //rest of your ajax setup
});

Or to prevent caching in MVC, we created our own attribute, you could do the same. Here's our code:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed 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 just decorate your controller with [NoCache]. OR to do it for all you could just put the attribute on the class of the base class that you inherit your controllers from (if you have one) like we have here:

[NoCache]
public class ControllerBase : Controller, IControllerBase

You can also decorate some of the actions with this attribute if you need them to be non-cacheable, instead of decorating the whole controller.

If your class or action didn't have NoCache when it was rendered in your browser and you want to check it's working, remember that after compiling the changes you need to do a "hard refresh" (Ctrl+F5) in your browser. Until you do so, your browser will keep the old cached version, and won't refresh it with a "normal refresh" (F5).

Donnelle
  • 5,689
  • 3
  • 27
  • 31
Mathew Thompson
  • 55,877
  • 15
  • 127
  • 148
  • 1
    I tried everything in the above solution and it does not work for me. – Obi Wan Oct 15 '13 at 13:44
  • 10
    It's my understanding (and I'm no jQuery expert) that cache:false only makes jQuery tack on to the query string a changing value to "trick" the browser into thinking the request is for something else. In theory, this means the browser would still cache the results, just wouldn't use the cached results. Should be more efficient on the client to disable caching via response headers. – Josh Dec 26 '13 at 20:30
  • 2
    Worked only on controller level and not on action level. – Ramesh Dec 15 '14 at 14:11
  • 4
    I would upvote including such an attribute in the official ASP.NET package :-) – usr-local-ΕΨΗΕΛΩΝ Jul 31 '15 at 08:04
  • @usr-local-ΕΨΗΕΛΩΝ There is no need for such an attribute, see other answers involving OutputCache attribute instead. – Ted Nyberg Sep 08 '15 at 14:46
  • @TedNyberg See the last comment on the other answer from Mark Rucker, the built in attribute doesn't offer **complete** cache disabling. – Mathew Thompson Sep 08 '15 at 18:39
  • @TedNyberg, `OutputCache` does mix http cache control with server output cache (especially with it `vary` handling), does not honor the full semantics of [http caching](https://tools.ietf.org/html/rfc7234) and does allow real deactivation of http caching (`no-cache` directive) by specifying `Location=OutputCacheLocation.None` (or `Server`). In short, `outputcache` is plainly broken when it is about http caching. We should only use it for controlling server output caching (which is broken by itself on IIS for loads of other reasons anyway). – Frédéric Nov 20 '15 at 16:12
  • Pity it does not even compile with asp.net 5 :( – Sam Jan 15 '16 at 15:19
  • @Sam Really? I haven't tried yet. What's the issue? – Mathew Thompson Jan 16 '16 at 11:22
  • @mattytommo No worries, I used [ResponseCache(NoStore = true)] instead, it works like charm and is build-in. – Sam Jan 17 '16 at 12:58
  • @Sam `no-store` alone is wrong because volatile caches are allowed by [http spec](https://tools.ietf.org/html/rfc7234#section-5.2.2.3) to cache content marked as no-store. You may have issues in some browsers. – Frédéric Jan 21 '16 at 17:59
  • @Frédéric what is recommended instead of using NoStore? – Sam Feb 01 '16 at 14:22
  • @Sam, [`no-cache`](https://tools.ietf.org/html/rfc7234#section-5.2.2.2). But `OutputCache` does not allow to control it directly, you have to use `Location` with `OutputCacheLocation` value `None` or `Server`. This still does not really forbid caching, but it forces the client to at least check with the server its cached response is still valid. – Frédéric Feb 16 '16 at 15:28
  • 1
    @Frédéric, the section of the spec you point to says that caches cannot cache no-store content: The "no-store" response directive indicates that a cache MUST NOT store any part of either the immediate request or response. – kristianp Oct 27 '16 at 04:29
272

You can use the built in cache attribute to prevent caching.

For .net Framework: [OutputCache(NoStore = true, Duration = 0)]

For .net Core: [ResponseCache(NoStore = true, Duration = 0)]

Be aware that it is impossible to force the browser to disable caching. The best you can do is provide suggestions that most browsers will honor, usually in the form of headers or meta tags. This decorator attribute will disable server caching and also add this header: Cache-Control: public, no-store, max-age=0. It does not add meta tags. If desired, those can be added manually in the view.

Additionally, JQuery and other client frameworks will attempt to trick the browser into not using it's cached version of a resource by adding stuff to the url, like a timestamp or GUID. This is effective in making the browser ask for the resource again but doesn't really prevent caching.

On a final note. You should be aware that resources can also be cached in between the server and client. ISP's, proxies, and other network devices also cache resources and they often use internal rules without looking at the actual resource. There isn't much you can do about these. The good news is that they typically cache for shorter time frames, like seconds or minutes.

Jaguir
  • 3,620
  • 1
  • 19
  • 26
  • 25
    It is impossible to force the browser to disable caching. The best you can do is provide suggestions that most browsers will honor, usually in the form of headers or meta tags. This decorator attribute will disable the .NET server caching and also add the header `Cache-Control:public, no-store, max-age=0`. It does not add meta tags. If desired, those can be added manually in the view. – Jaguir Jun 20 '14 at 14:13
  • 1
    I can understand why you would use `NoStore = true` and `Duration = 0` (which I have used successfully, thanks), but what additional effect would `VaryByParam = "None"` have as the other two options affect all requests regardless of parameter? – iCollect.it Ltd Apr 27 '15 at 09:15
  • I don't think it's required in MVC, I was just being explicit. I do remember that in ASP.NET web forms and user controls, either this attribute or the VaryByControl attribute is required. – Jaguir Apr 27 '15 at 14:22
  • Warning, I've been messing with this today. From what I can tell this attribute does not modify Cache-Control if ``. In the case that OutputCache has been explicitly disabled you'll need to set the CacheControl headers manually (either in the Action or by using [mattytommo's answer][http://stackoverflow.com/a/10011896/1066291]). – Mark Rucker Jun 24 '15 at 18:12
  • [`no-store`](https://tools.ietf.org/html/rfc7234#section-5.2.2.3) is not guaranteed to be enough. The response may still be cached in a volatile cache (as ram memory). [`max-age=0`](https://tools.ietf.org/html/rfc7234#section-5.2.2.8) is neither sufficient alone or with `no-store`. It does not prevent caching, it just mark the content as immediately stale. It may still be cached, but caches are invited to revalidates it before use (but are allowed to serve it without revalidation). – Frédéric Nov 20 '15 at 16:00
  • @Jaguire for example i need to disable for the login page, do i need to place in account controller head or in each and every action or where!? – SAR Dec 17 '16 at 05:45
  • @SAR Placing it on your login controller or its actions should suffice. – Jaguir Dec 18 '16 at 22:25
  • @Jaguire i did put the code on top of the Account Controller which login is located there, again in google chrome i login and with out loging out i close the IE then again when i try IE i get the page with request for login – SAR Dec 19 '16 at 07:45
  • @SAR That sounds like your issue is beyond simple caching. Maybe with your session or cookies? Start a question detailing your issue and you will get better help. – Jaguir Dec 20 '16 at 15:07
  • 1
    For ASP.NET Core use: '[ResponseCache(NoStore = true, Duration = 0)]' – Jeff Jun 08 '17 at 23:34
50

All you need is:

[OutputCache(Duration=0)]
public JsonResult MyAction(

or, if you want to disable it for an entire Controller:

[OutputCache(Duration=0)]
public class MyController

Despite the debate in comments here, this is enough to disable browser caching - this causes ASP.Net to emit response headers that tell the browser the document expires immediately:

OutputCache Duration=0 Response Headers: max-age=0, s-maxage=0

Chris Moschini
  • 36,764
  • 19
  • 160
  • 190
  • 6
    IE8 still renders the cached version of the page when the back button is clicked using only Duration=0 on a Controller Action. Using NoStore = true along with Duration = 0 (see Jared's answer) fixed the behavior in my case. – Keith Ketterer Mar 17 '15 at 19:32
  • 3
    This has the somewhat curious behavior of setting `Cache-Control` to `public` – ta.speot.is Apr 22 '15 at 06:33
  • [`max-age=0`](https://tools.ietf.org/html/rfc7234#section-5.2.2.8) has never meant 'cache disabled'. This does only mean that response content is to be considered immediately [stale](https://tools.ietf.org/html/rfc7234#section-4.2), but a cache is allowed to cache it. Browsers should validate freshness of cached stale content before using it, but it is not mandatory unless the additional directive [`must-revalidate`](https://tools.ietf.org/html/rfc7234#section-5.2.2.1) is specified. – Frédéric Nov 20 '15 at 15:52
17

In the controller action append to the header the following lines

    public ActionResult Create(string PositionID)
    {
        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.
dfortun
  • 704
  • 6
  • 7
5

Here's the NoCache attribute proposed by mattytommo, simplified by using the information from Chris Moschini's answer:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class NoCacheAttribute : OutputCacheAttribute
{
    public NoCacheAttribute()
    {
        this.Duration = 0;
    }
}
Konamiman
  • 49,681
  • 17
  • 108
  • 138
  • For some reason MVC 3 doesn't just let you set the duration to 0. You have to add these annotations ... thanks for the workaround! – micahhoover Mar 19 '15 at 16:17
  • [`max-age=0`](https://tools.ietf.org/html/rfc7234#section-5.2.2.8) has never meant 'cache disabled'. This does only mean that response content is to be considered immediately [stale](https://tools.ietf.org/html/rfc7234#section-4.2), but a cache is allowed to cache it. Browsers should validate freshness of cached stale content before using it, but it is not mandatory unless the additional directive [`must-revalidate`](https://tools.ietf.org/html/rfc7234#section-5.2.2.1) is specified. – Frédéric Nov 20 '15 at 16:04
  • For completeness, the minimal and more appropriate directive is [`no-cache`](https://tools.ietf.org/html/rfc7234#section-5.2.2.3), which still allows caching but mandate to revalidates on origin server before any use. To avoid even revalidated caching you have to add `no-store` along with `no-cache`. (`no-store` alone is plainly wrong because volatile caches are allowed to cache content marked as `no-store`.) – Frédéric Nov 20 '15 at 16:25
4

For MVC6 (DNX), there is no System.Web.OutputCacheAttribute

Note: when you set NoStore Duration parameter is not considered. It is possible to set an initial duration for first registration and override this with custom attributes.

But we have Microsoft.AspNet.Mvc.Filters.ResponseCacheFilter

 public void ConfigureServices(IServiceCollection services)
        ...
        services.AddMvc(config=>
        {
            config.Filters.Add(
                 new ResponseCacheFilter(
                    new CacheProfile() { 
                      NoStore=true
                     }));
        }
        ...
       )

It is possible to override initial filter with a custom attribute

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public sealed class NoCacheAttribute : ActionFilterAttribute
    {
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            var filter=filterContext.Filters.Where(t => t.GetType() == typeof(ResponseCacheFilter)).FirstOrDefault();
            if (filter != null)
            {
                ResponseCacheFilter f = (ResponseCacheFilter)filter;
                f.NoStore = true;
                //f.Duration = 0;
            }

            base.OnResultExecuting(filterContext);
        }
    }

Here is a use case

    [NoCache]
    [HttpGet]
    public JsonResult Get()
    {            
        return Json(new DateTime());
    }
Manfred
  • 5,320
  • 3
  • 35
  • 29
Davut Gürbüz
  • 5,526
  • 4
  • 47
  • 83
4

ASP.NET MVC 5 solutions:

  1. Caching prevention code at a central location: the App_Start/FilterConfig.cs's RegisterGlobalFilters method:
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            // ...
            filters.Add(new OutputCacheAttribute
            {
                NoStore = true,
                Duration = 0,
                VaryByParam = "*",
                Location = System.Web.UI.OutputCacheLocation.None
            });
        }
    }
  1. Once you have that in place my understanding is that you can override the global filter by applying a different OutputCache directive at Controller or View level. For regular Controller it's
[OutputCache(NoStore = true, Duration = 0, Location=System.Web.UI.ResponseCacheLocation.None, VaryByParam = "*")]

or if it's an ApiController it'd be

[System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, Location = System.Web.UI.OutputCacheLocation.None, VaryByParam = "*")]
Csaba Toth
  • 10,021
  • 5
  • 75
  • 121
2

Correct attribute value for Asp.Net MVC Core to prevent browser caching (including Internet Explorer 11) is:

[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]

as described in Microsoft documentation:

Response caching in ASP.NET Core - NoStore and Location.None

Nenad
  • 24,809
  • 11
  • 75
  • 93
-1

Output Caching in MVC

[OutputCache(NoStore = true, Duration = 0, Location="None", VaryByParam = "*")]

OR
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")]

Deepak
  • 121
  • 5
  • See other comments ([1](/questions/10011780/prevent-caching-in-asp-net-mvc-for-specific-actions-using-an-attribute/25144198#comment55427507_10011896), [2](/questions/10011780/prevent-caching-in-asp-net-mvc-for-specific-actions-using-an-attribute/25144198#comment55427124_28743069), [3](/questions/10011780/prevent-caching-in-asp-net-mvc-for-specific-actions-using-an-attribute/25144198#comment55426941_18620970)) on the numerous answers already suggesting using this. Your second line is wrong and will lead to issues with some browsers. – Frédéric Jan 21 '16 at 17:52