Does setting cache control to public prevent private cache? - http

Can you enable both private and public caching through the cache-control header? How would you do that?
Thanks!

The Cache-Control: public response header enables any kind of intermediate cache. If you want the response to be cached as widely as possible, then this is what you use.
The Cache-Control: private response header disables any caching that might expose the response to any user but the current user. This will prevent caching by most proxies and intermediate servers, allowing the response to be cached only in user-specific client-side caches. Any cache that can be used with this setting can also be used with the public setting.

Related

Caching full response on the server

Here's a very small sample Razor Page:
#page
#model IndexModel
#{
ViewData["Title"] = "Home page";
}
<h1>
#DateTime.Now.ToString()
</h1>
//model
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public void OnGet()
{
}
}
If I use this code, the time will update every 30 seconds which is what is intended:
<cache expires-after="TimeSpan.FromSeconds(30)">
<h1>
#DateTime.Now.ToString()
</h1>
</cache>
However, adding the ResponseCache attribute to the model doesn't do this:
[ResponseCache(Duration = 30)]
public class IndexModel : PageModel
After doing some research it seems like the attribute only sends the appropriate headers to the client, asking it to cache the content. How can I store the entire response in memory so when the user asks for the specific page, the server just sends the cached response and eliminate the process of computing the result again?
Also, with the <cache> tag helper, I couldn't find a way to invalidate the cached entry. So one scenario for me would be to cache every single page in memory for 30 days and if I change something on the admin panel, I would then invalidate the cache for that specific item so the next request would produce the fresh result. I used to do this on Asp.Net MVC 3+ but couldn't find any method to achieve the same result in Asp.Net Core 3.1
From your question it seems you might need to roll out your own version
How can I store the entire response in memory so when the user asks
for the specific page, the server just sends the cached response and
eliminate the process of computing the result again?
See the source of
https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/ResponseCacheAttribute.cs
And
https://github.com/aspnet/Mvc/blob/d8c6c4ab34e1368c1b071a01fcdcb9e8cc12e110/src/Microsoft.AspNetCore.Mvc.Core/Internal/ResponseCacheFilter.cs
It seems that it sets headers only.
You may implement your own version of caching like this one
https://www.devtrends.co.uk/blog/custom-response-caching-in-asp.net-core-with-cache-invalidation
See CachedPage class in above example.
Edit: No, it doesn't work (I cannot delete this answer).
https://github.com/dotnet/AspNetCore.Docs/tree/master/aspnetcore/performance/caching/middleware/samples/3.x/ResponseCachingMiddleware
Browsers often add a cache control header on reload that prevent the
middleware from serving a cached page.
Therefore, it doesn't work and I don't see how this could be any useful besides machine-machine requests using specially crafted requests.
Use a developer tool that permits setting the request headers
explicitly, such as Fiddler or Postman.
I haven't tested it yet, but it looks like the "Response Caching Middleware" could be the answer.
https://learn.microsoft.com/en-us/aspnet/core/performance/caching/response?view=aspnetcore-3.1
For server-side caching that follows the HTTP 1.1 Caching
specification, use Response Caching Middleware. The middleware can use
the ResponseCacheAttribute properties to influence server-side caching
behavior.
Response Caching Middleware in ASP.NET Core:
https://learn.microsoft.com/en-us/aspnet/core/performance/caching/middleware?view=aspnetcore-3.1
The middleware determines when responses are cacheable, stores
responses, and serves responses from cache.
You'll not always get a cached response, though.
The middleware respects the rules of the HTTP 1.1 Caching
specification. The rules require a cache to honor a valid
Cache-Control header sent by the client. Under the specification, a
client can make requests with a no-cache header value and force the
server to generate a new response for every request. Currently,
there's no developer control over this caching behavior when using the
middleware because the middleware adheres to the official caching
specification.
In addition
Caching should only be enabled for content that doesn't change based
on a user's identity or whether a user is signed in.

IIS/ASP.NET responds with cache-control: private for all requests

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?
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);
}
TL; DR
Caching is not used by default for dynamic ASP.NET pages. You need to make an efforts to enable caching in ASP.NET.
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:
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.
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):
In the Connections pane, go to the site, application, or
directory for which you want to disable caching.
In the Home pane, double-click HTTP Response Headers.
Click Set Common Headers ... in the Actions pane.
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:
Client Cache <clientCache>
How to: Set the Cacheability of an ASP.NET Page Declaratively
output-caching-in-aspnet-mvc
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
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>
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.
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:
go to IIS Manager
Select Site/Application desired
in Features View select HTTP Response Headers:
In actions pane click: Set Common Headers
check Expire Web Content
Set to: Immediately
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.

Symfony HTTP Cache invalidation

Here's the deal. Symfony 2.8 setup. I want to serve cached rendered content with Symfony HTTP Cache (content rarely changes) and to be able to invalidate it whenever I want.
When I use Symfony HTTP cache like this:
$response = new JsonResponse($data, Response::HTTP_OK);
$response->setPublic();
$response->setSharedMaxAge(3600);
$response->headers->addCacheControlDirective('must-revalidate', true);
$response->setVary('Accept-Encoding');
It results in following headers:
Cache-Control:must-revalidate, public, s-maxage=3600
vary:Accept-Encoding
Content gets cached, but because of cache control set to public, intermediate proxies can cache it as well and obviously I can't invalidate data there.
On the other hand setting cache control to private will force Symfony HTTP cache to not store it.
With Varnish (which I can't use in this case) I would send Cache-Control: public from the app server and overwrite it to Cache-Control: private with VCL rule, so browsers always return to check for a fresh version.
I wonder if I should do something similar with Symfony HTTP Cache (override Cache-Control header once response passes caching layer) or is there a simpler way?

Allow reverse proxy cache but not browser cache

There are so many questions asking "how to make sure the pages are not cached", and answers like "this is how to instruct the both clients and proxy servers not to cache". I'm instead looking for a way to achieve "allow proxy cache but not clients (i.e. browsers) cache".
In fact, I found that setting no cache-related headers can achieve this, but I'm not sure if this is the right way to do it and there's no explicit way of instructing it.
In the HTTP jargon these caches are referred as shared or public (proxy) and private caches (browser).
You should use a response header similar to this:
cache-control: public, max-age=0, s-maxage=${seconds}
Being ${seconds} the TTL of the cached elements. The key here is using the directive s-maxage.
If a response includes an s-maxage directive, then for a shared cache (but not for a private cache), the maximum age specified by this directive overrides the maximum age specified by either the max-age directive or the Expires header. The s-maxage directive also implies the semantics of the proxy-revalidate directive (see section 14.9.4), i.e., that the shared cache must not use the entry after it becomes stale to respond to a subsequent request without first revalidating it with the origin server. The s- maxage directive is always ignored by a private cache.
Notice that this header does not exempt the browser from caching the resource (using it to serve a future request), but instead forces it to revalidate its content (if a last-modified or etag header is also returned)

ASP.NET cache on client or server

If you set caching (as below) in an HTTP handler, will it be cached on the server or client or both?
_context.Response.Cache.SetCacheability(HttpCacheability.Public);
_context.Response.Cache.SetExpires(DateTime.Now.AddSeconds(180));
For the following call:
_context.Response.Cache.SetCacheability(HttpCacheability.Public);
it turns out that in addition to setting the Cache-Control: public HTTP header, it also enables server-side output caching.
This sets the http header, which means it will be cached by:
The client
A server "on the way" to the client, such as an ISA server
The code you used above will cache the content on the clients browser.
If the expiry date of the content is within the time specified then the browser (client side) will issue a 304 "Not Modified" i.e. The Content is cached and not re fetched from the server.
Hope this helps
G
Cache-Control: public to specify that the response is cacheable by clients and shared (proxy) caches.
http://msdn.microsoft.com/en-us/library/system.web.httpcacheability(VS.71).aspx
Regards
--Jocke

Resources