212

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?

1

9 Answers 9

318

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

14
  • 1
    I tried everything in the above solution and it does not work for me.
    – Obi Wan
    Commented Oct 15, 2013 at 13:44
  • 11
    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
    Commented Dec 26, 2013 at 20:30
  • 2
    Worked only on controller level and not on action level.
    – Ramesh
    Commented Dec 15, 2014 at 14:11
  • 4
    I would upvote including such an attribute in the official ASP.NET package :-) Commented Jul 31, 2015 at 8:04
  • 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
    Commented Oct 27, 2016 at 4:29
276

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

10
  • 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
    Commented Jun 20, 2014 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? Commented Apr 27, 2015 at 9: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
    Commented Apr 27, 2015 at 14:22
  • Warning, I've been messing with this today. From what I can tell this attribute does not modify Cache-Control if <outputCache enableOutputCache="false" />. 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][stackoverflow.com/a/10011896/1066291]). Commented Jun 24, 2015 at 18:12
  • 1
    For ASP.NET Core use: '[ResponseCache(NoStore = true, Duration = 0)]'
    – Jeff
    Commented Jun 8, 2017 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

3
  • 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. Commented Mar 17, 2015 at 19:32
  • 3
    This has the somewhat curious behavior of setting Cache-Control to public Commented Apr 22, 2015 at 6:33
  • max-age=0 has never meant 'cache disabled'. This does only mean that response content is to be considered immediately stale, 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 is specified.
    – Frédéric
    Commented Nov 20, 2015 at 15:52
19

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.
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;
    }
}
3
  • 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! Commented Mar 19, 2015 at 16:17
  • max-age=0 has never meant 'cache disabled'. This does only mean that response content is to be considered immediately stale, 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 is specified.
    – Frédéric
    Commented Nov 20, 2015 at 16:04
  • For completeness, the minimal and more appropriate directive is no-cache, 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
    Commented Nov 20, 2015 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());
    }
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 = "*")]
0
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

-2

Output Caching in MVC

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

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

1
  • See other comments (1, 2, 3) on the numerous answers already suggesting using this. Your second line is wrong and will lead to issues with some browsers.
    – Frédéric
    Commented Jan 21, 2016 at 17:52

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.