17

Why does all responses from ASP.NET contain Cache-Control: private? Even a 404 response? Is there something in IIS that sets this default value, and is there a way to configure it? Or is there something in ASP.NET that sets this?

For dynamic content (that is, all MVC results) I would not like it to be cached by the browser, since it is dynamic and can change at any time. Static content is hosted on a CDN, so is not served by IIS.

Edit:

To clarify, I understand very well what Cache-Control: private is, the difference between private, public, no-store, etc and how/when to use them. The question I have is why Cache-Control: private is added by default by IIS/ASP.NET and how to prevent it from being added by default. I understand that it can be useful to cache dynamic pages, but in my application I don't want to cache dynamic pages/responses. For example, I don't want XHR JSON responses to be cached, since they contain dynamic content. Unfortunately the server adds Cache-Control: private to all responses automatically, so I have to manually override it everywhere.

How to reproduce: Open visual studio and create a new ASP.NET Framework (yes, framework, no not Core. We are not able to migrate our system to core yet) solution with an MVC project. Now start the project in IIS Express (just press the play button), and use F12 devtools in the browser to look at the http response. You will see that it contains Cache-Control: private. My question is, what adds this header, and how can I prevent it from being added by default?

enter image description here

Community
  • 1
  • 1
Marius
  • 57,995
  • 32
  • 132
  • 151
  • Have you looked into using `meta` tags? You can set `Cache-Control` and `Expires` this way on the web page level. – Froopy Nov 10 '17 at 14:14
  • how can a meta tag set the cache-control of an ajax response? – Marius Nov 10 '17 at 14:15
  • ajax is a different story. What are you using to do requests? jQuery has a method of setting cache-control on ajax requests. – Froopy Nov 10 '17 at 14:17
  • this is about the response, not the request. The request contains no headers with cache information – Marius Nov 10 '17 at 14:56
  • 1
    It may be helpful to see the *Web.config* file. `` in `` can cause this. We can also add a `cache-control` header directive to the `` block in ``. – Cy Rossignol Nov 13 '17 at 08:09
  • `runAllManagedModulesForAllRequests` is set to false, and Iwhen I `` the ``, I end up with the rather ridiculous `Cache-Control: private,no-store` response header. – Marius Nov 13 '17 at 09:17
  • I don't know about ASP.NET, so I will just put a comment. Note that `Cache-Control: public` is the only way to mark the cache as being public. A directive such as `Cache-Control: max-age=3600` is implicitly private. (i.e. `private` is the default.) – Alexis Wilke Aug 12 '18 at 22:16

7 Answers7

10

Adding my bit to the great answers, given by community;

1. http caching header attrubute Cache-Control: private is added by default by IIS/ASP.NET ?

Cache request directives

Standard Cache-Control directives that can be used by the client in an HTTP request.

    Cache-Control: max-age=<seconds>
    Cache-Control: max-stale[=<seconds>]
    Cache-Control: min-fresh=<seconds>
    Cache-Control: no-cache 
    Cache-Control: no-store
    Cache-Control: no-transform
    Cache-Control: only-if-cached

Cache response directives

Standard Cache-Control directives that can be used by the server in an HTTP response.

    Cache-Control: must-revalidate
    Cache-Control: no-cache
    Cache-Control: no-store
    Cache-Control: no-transform
    Cache-Control: public
    Cache-Control: private
    Cache-Control: proxy-revalidate
    Cache-Control: max-age=<seconds>
    Cache-Control: s-maxage=<seconds>

IIS uses the secure and more obvious/useful one for default, which happens to be private

2. how to prevent it from being added by default?

IIS/asp.net allows this to be configured from the day it was introduced like this, ref1, ref2, ref3, ref4 and

System.Web Namespace

The System.Web namespace supplies classes and interfaces that enable browser-server communication. This namespace includes the System.Web.HttpRequest class, which provides extensive information about the current HTTP request; the System.Web.HttpResponse class, which manages HTTP output to the client; and the System.Web.HttpServerUtility class, which provides access to server-side utilities and processes. System.Web also includes classes for cookie manipulation, file transfer, exception information, and output cache control.

protected void Application_BeginRequest()
{
  Context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
}
Community
  • 1
  • 1
Anil
  • 3,722
  • 2
  • 24
  • 49
4

TL; DR

  1. Caching is not used by default for dynamic ASP.NET pages. You need to make an efforts to enable caching in ASP.NET.
  2. Presence of 'Cache-Control: private' header does not mean at all that cached version of the page will be used on repeated requests.

--

There is a very simple test to validate above statements. Create an action that returns current time:

public ActionResult Index()
{
    ViewBag.CurrTime = DateTime.Now.ToString("T");
    return View();
}

View:

@{
    ViewBag.Title = "Home Page";
}

<h1>@ViewBag.CurrTime</h1>

If you refresh such page in abrowser, you'll see fresh time on every request:

enter image description here

enter image description here

There is a possibility to use caching with ASP.NET MVC but you should make some efforts to enable it. See this article for details.

If in spite of this you still for some reason want to exclude any possibility of the caching, you could do it by setting specific HTTP headers. There is a great SO answer that lists which headers should be set:

Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0

You could use action filter to set those headers in every ASP.NET response:

public class CachingHeadersFilterAttribute : ActionFilterAttribute
{
    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        var response = filterContext.HttpContext.Response;

        response.Cache.SetCacheability(HttpCacheability.NoCache);
        response.Cache.AppendCacheExtension("no-store, must-revalidate");
        response.AppendHeader("Pragma", "no-cache");
        response.AppendHeader("Expires", "0");

        base.OnResultExecuted(filterContext);
    }
}

In FilterConfig.cs (crated automatically in ASP.NET MVC template):

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        filters.Add(new CachingHeadersFilterAttribute());
    }
}

Here are result headers from the response:

HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Content-Type: text/html; charset=utf-8
Expires: -1
Vary: Accept-Encoding
Server: Microsoft-IIS/10.0
X-AspNetMvc-Version: 5.2
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcRHJvcGJveFxwcm9nXFN0YWNrT3ZlcmZsb3dcUTQ3MjI0NTYxQ2FjaGVcUTQ3MjI0NTYxQ2FjaGU=?=
X-Powered-By: ASP.NET
Date: Mon, 13 Nov 2017 17:44:33 GMT
Content-Length: 837

As you see there is no 'Cache-Control: private' header.

But again, I don't see a reason why you should add such filter to your application.

CodeFuller
  • 30,317
  • 3
  • 63
  • 79
  • "1.Caching is not used by default for dynamic ASP.NET pages. You need to make an efforts to enable caching in ASP.NET." This is incorrect, the setting that @VDWWD is in fact the default setting that IIS 7 ships with. As attested by all the other answers. If you look at see the answer below: "The IIS 7 and later (included in the default installation) sends it to Web clients as one of the cache-related HTTP headers." – DaniDev Nov 14 '17 at 17:54
  • It seems like your answer tells the User how to deal with this header with the use of a filter. But the question is really about where does this header come from originally (IIS) and how to change the default setting. – DaniDev Nov 14 '17 at 17:54
  • If I understood OP correctly his main concern is caching of dynamic pages: "I understand that it can be useful to cache dynamic pages, but in my application I don't want to cache dynamic pages/responses.". I've shown that despite a presence of that 'Cache-Control' header, dynamic pages are not actually cached in this case. – CodeFuller Nov 14 '17 at 19:05
  • What you have shown is useful and solid information and is a viable option to deal with the default caching header designation. That is why I did not down vote it, even though it does not address the Question as stated: "You will see that it contains Cache-Control: private. My question is, what adds this header, and how can I prevent it from being added by default?" while on the other hand my answer addressed this specifically. And showd how to modify the default setting. while giving MSDN references. You on the other hand, down voted my answer and cited inaccurate reasons. – DaniDev Nov 14 '17 at 19:49
  • :) I've tried to undo my down vote after reading your args but SO locked my vote and doesn't allow to change it without answer edit. Could you make some minor edit to your answer so that I could remove my down vote, it was somewhat huried. – CodeFuller Nov 14 '17 at 19:53
  • Thank you for your acknowledgment and honesty. I have made an edit to my answer. – DaniDev Nov 14 '17 at 19:58
  • 1
    Refresh does indeed update the page, since hitting F5 adds `Cache-Control: max-age=0` to the request and bypasses the browser cache. If instead you add a link that on click takes you to the same page, then the request will not contain `Cache-Control: max-age=0` and the browser cache will be used. This is the problem I have – Marius Nov 15 '17 at 08:29
4

what adds this header?

The IIS 7 and later (included in the default installation) sends it to Web clients as one of the cache-related HTTP headers.

and how can I prevent it from being added by default?

I describe two ways of disabling cache mechanism (server-side approach and client-side approach) with a focus on server-side based on your question:

Server-Side Approach

Follow this steps Inside the Internet Information Services (IIS) Manager to change Cache-Control value (while I think this works for static contents):

  1. In the Connections pane, go to the site, application, or directory for which you want to disable caching.
  2. In the Home pane, double-click HTTP Response Headers.
  3. Click Set Common Headers ... in the Actions pane.
  4. Check the box to expire Web content, select the option to expire after a specific interval or at a specific time, and then click OK.

One way is to annotate your controller as follows which is quite powerful and the response headers contain a Cache-Control: public, max-age=0 header.:

[OutputCache(Duration = 0)]
public class SomeController : Controller  {

}

You can also Define a cache profile in your application's Web.config file and in the profile, include duration and varyByParam settings:

<caching>
  <outputCacheSettings>
    <outputCacheProfiles>
      <add name="nocache" duration="0" 
        varyByParam="none" />
    </outputCacheProfiles>
  </outputCacheSettings>
</caching>

Then use it by the [OutputCache CacheProfile="nocache"] before the action/controller.

Note that there is a configuration in web.config file (following) which won’t prevent caching. Instead it just indicates that no kind of caching mechanism should be applied. By just disabling the output cache we get the default cache headers used by ASP.net MVC which falls back to Cache-Control: private, thus again opening the browser the possibility to cache requests.

<caching>
    <outputCache enableOutputCache="false" />
</caching>

Client-Side Approach

Using cache: false inside your js request like:

$.ajax({
    type: 'GET',
    cache: false,
    url: '/nation',
    ...
});

For additional information, visit:

Amirhossein Mehrvarzi
  • 18,024
  • 7
  • 45
  • 70
3

An answer from RickNZ, copied from https://forums.asp.net

Cache-Control private says that it's OK for the client to cache the page, subject to its expiration date. The expiration can either be provided with Cache-Control: max-age, or with an Expires HTTP header. In the default case, the page is set to expire immediately, which means that it won't be cached.

One of the purposes of Cache-Control: private is really to tell intermediate proxies that they should not cache the page.

BTW, just because a page is dynamic doesn't mean that it should never be cached. There are many cases where caching a dynamic page is appropriate. You can cache not only at the client, but also in proxies and in the server's output cache.

More info:

IIS 7.0 - IIS adding "private" to cache-control, where is that coming from

Private vs Public in Cache-Control

https://msdn.microsoft.com/en-us/library/ms524721(v=vs.90).aspx

https://msdn.microsoft.com/en-us/library/system.web.httpcacheability(VS.71).aspx

https://forums.asp.net/t/1443346.aspx?Cache+control+private+

https://forums.asp.net/t/2052325.aspx?Remove+the+private+value+from+the+Cache+Control+in+the+Response+Header

VDWWD
  • 35,079
  • 22
  • 62
  • 79
  • 5
    Links to external resources are encouraged, but please add context around the link so your fellow users will have some idea what it is and why it’s there. (https://stackoverflow.com/help/how-to-answer) – Emanuele Nov 13 '17 at 13:18
3

The Cache-Control: private header is added to the response by the framework by default.

https://learn.microsoft.com/en-us/dotnet/api/system.web.configuration.httpruntimesection.sendcachecontrolheader?view=netframework-4.8

Definition

Gets or sets a value indicating whether the cache-control:private header is sent by the output cache module by default.

...

Remarks

The HttpResponse class checks both the HttpRuntimeSection.SendCacheControlHeader property and the OutputCacheSection.SendCacheControlHeader property to determine whether to send the cache-control:private header in the HTTP response. If either property is set to false, the header will not be sent. When the cache-control header is set to private, then the client will not cache the response in a shared cache.

Support for the SendCacheControlHeader property in the HttpRuntimeSection class is provided for compatibility with legacy applications; this property is obsolete in the .NET Framework version 2.0. For more information, see the OutputCacheSection class.

To prevent the Cache-Control: private header from being added, simply disable the OutputCacheSection.SendCacheControlHeader property, which defaults to true.

https://learn.microsoft.com/en-us/dotnet/api/system.web.configuration.outputcachesection.sendcachecontrolheader?view=netframework-4.8#system-web-configuration-outputcachesection-sendcachecontrolheader

Property Value

true if the sending of cache-control:private header is enabled; otherwise, false. The default is true.

Example web.config

<configuration>
  <system.web>
    <caching>
      <outputCache sendCacheControlHeader="false" />
    </caching>
  </system.web>
</configuration>
1

The default is specified in System.Web.HttpResponse.CacheControl:

        /// <devdoc>
        ///    <para>
        ///       Provided for ASP compatiblility. Use the <see cref='System.Web.HttpResponse.Cache'/>
        ///       property instead.
        ///    </para>
        /// </devdoc>
        public string CacheControl {
            get {
                if (_cacheControl == null) {
                    // the default
                    return "private";
                }

                return _cacheControl;
            }

While you can override the header through (global) filters, this doesn't work for error pages caused by authentication/authorization. Luckily there's a nice entry point for each request, allowing you to override this default:

// In Global.asax.cs:
        protected void Application_BeginRequest()
        {
            Context.Response.CacheControl = "no-cache";
        }

Furthermore when there's an error and the YSOD (yellow error page) is rendered through ReportRuntimeError, the framework will call ClearHeaders and your custom cache-control setting will be overridden. I haven't found a solution for this.

Bouke
  • 11,768
  • 7
  • 68
  • 102
0

Addressing the question:

You will see that it contains Cache-Control: private. My question is, what adds this header, and how can I prevent it from being added by default?

The Short answer is: as others have noted, this is a default setting by IIS(7+).

To answer more fully: If you are looking to control or modify the default setting Cache-Control: private for caching HTTP requests, server side.

This will depend on your the which specific version of IIS you are running, and how specific you want to be with your modification. Generally speaking however you can do this using your HTTP Response Headers interface (in IIS Manager => Features view - IIS)

If you want to simply disable caching for a Web site or Application:

  1. go to IIS Manager
  2. Select Site/Application desired
  3. in Features View select HTTP Response Headers:
  4. In actions pane click: Set Common Headers
  5. check Expire Web Content
  6. Set to: Immediately
  7. OK

alternatively, from your command line:

appcmd.exe set config  "Default Web Site" -section:system.webServer/staticContent /clientCache.cacheControlMode:"DisableCache" 

For Detailed Information and specifics please check:

https://learn.microsoft.com/en-us/iis/configuration/system.webserver/staticcontent/clientcache

Hope this is what your were looking for, cheers.

DaniDev
  • 2,471
  • 2
  • 22
  • 29
  • OP asks specifically for caching headers of ASP.NET responses. Disabling caching for static content is a very bad idea. – CodeFuller Nov 14 '17 at 04:28
  • @CodeFuller "My question is, what adds this header, and how can I prevent it from being added by default?" (was this your edit?) My answer deals specifically with the MS recommended method for removing or modifying this default setting. While the default setting is recommended it is perfectly fine to modify it for specific types of sites, so that caching is not automatic. Please have confidence that if your answer is in fact the better one then it will be accepted and/or up voted. – DaniDev Nov 14 '17 at 17:21