MVC3 OutputCache VaryByHeader=Cookie not being set - asp.net

I have the following action method:
[HttpGet, Authorize, OutputCache(Duration = 60, VaryByHeader = "Cookie", Location = OutputCacheLocation.Any)]
public ActionResult Index()
But when I make a request, these are the headers issued:
Cache-Control: private, max-age=60, s-maxage=0
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Expires: Fri, 22 Jun 2012 09:56:32 GMT
Last-Modified: Fri, 22 Jun 2012 09:55:32 GMT
Vary: Accept-Encoding
Why isn't it including the Cookie header in the Vary?
I've tried many variations of the OutputCache settings but to no avail :(

I hate IIS almost as much as I hate ASP.NET.
http://blogs.msdn.com/b/chaun/archive/2009/10/01/iis-compression-overwrites-the-vary-header-average-rating-0-ratings.aspx

So the response is included in the linked by Andrew Bullock url "page file name", aka "iis compression overwrites the vary header".
But since December 2013 a hotfix is available (found on Andrew Bullock link). As usual, this hotfix may be included in regular patches, so make sure you still need it before applying it.
And as it is "best practice" to explicitly supply information from links rather than only supplying the link (in case the page goes down), here is an abstract :
IIS dynamic compression overwrites the Vary header. This seems to apply to IIS from version 5 to version 8 included (when cumulating information from msdn blog and from msdn kb). Either disable IIS dynamic compression, or try applying the hotfix available at http://support.microsoft.com/kb/2877816/en-us if needed. (This issue may be fixed by regular patches on windows 8.1/2012 R2 series. The hotfix is available starting from IIS 7, personally tested on win7 sp1, not yet on my servers.)
In my specific case, I had also another issue : asp.net was not emitting any Vary header (compression enabled or not) with outputCache location set to Client. Setting it to Downstream "solved" this additional issue. But of course, this has the side-effect of changing Cache-Control from private to public. (Now cacheable on proxy instead of just on client browser.)

Related

Why does browser not use cache even when Cache-Control/Expires headers are set for static files?

I have implemented caching on my webserver for static files like images. I've tried setting response headers such as:
Cache-Control: public, max-age=864000
Expires: Mon, 21 Aug 2018 14:00:00 GMT
This will at least cache the files for one day. But using the Developer Tools, I can see the requests still being fetched without caching.
The browser seems to send request headers like:
Cache-Control: no-cache
Even though I explicitly instruct it from my webserver not to do that.
In most browsers, when you have Developer Tools open, the browser actually sends Cache-Control: no-cache in the request as a supposedly "useful feature" since you are probably debugging.
By default this feature is enabled. In FireFox this feature can be found listed below the 'Advanced settings' in the settings-menu (use the gear icon) as: Disable HTTP Cache (when toolbox is open)
Uncheck that feature, and caching should work.
When you are developing a web application using Visual Studio (e.g. ASP.net MVC) and you use Chrome for running your application, Chrome will always send header Cache-Control: no-cache. No matter whether you Disable HTTP Cache. Also closing the Developer Tools does not help.
What does help: Use another browser for debugging or start a new instance of Chrome.

ASP.NET Core 2.0 - Http Response Caching Middleware - Nothing cached

I created a new solution from WebApi .Net Core 2.0 template in Visual Studio.
I added the following in startup.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddHttpCacheHeaders(opt => opt.MaxAge = 600);
services.AddResponseCaching();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseResponseCaching();
app.UseHttpCacheHeaders();
app.UseMvc();
}
Then, with postman, I hit http://localhost:xxxx/api/values which is a endpoint created by the template and that returns ["value1","value2"]
Note that I made sure that Postman doesn't send no-cache header (in Postman settings).
The HttCacheHeaders service comes from that repo. It adds HTTP Cache Headers. So my endpoint response header is:
Cache-Control: public,max-age=600
Content-Type: application/json; charset=utf-8
Date: Fri, 29 Sep 2017 14:02:29 GMT
ETag: C5DFA8974BB722D27E71EE50D3D14625
Expires: Fri, 29 Sep 2017 14:03:29 GMT
Last-Modified: Fri, 29 Sep 2017 14:02:29 GMT
Server: Kestrel
Transfer-Encoding: chunked
Vary: Accept, Accept-Language, Accept-Encoding
X-Powered-By: ASP.NET
X-SourceFiles: =?UTF-8?B?................
The problem is that nothing gets cached. the Ouput windows only shows The response could not be cached for this request.
So I'm a bit lost on how to use ASP.NET Core ResponseCaching Middleware.
Update
If I don't use the HttCacheHeaders service but add [ResponseCache(Duration = 600)] to the action of my controller, the cache works.
Note that reason that I want to use HttCacheHeaders is for ETag and Last-Modified to later do Validation Caching as well as Expiration Caching.
This is kind of a non-answer, with some troubleshooting tips.
I tried your exact code & it worked fine for me. Maybe a bug has been fixed in the HttpCacheHeaders or ResponseCaching repo?
Unfortunately, debugging the server-side ResponseCaching is tricky because it has weird rules & there's not adequate logging. When I've had similar issues with it in the past I've had to pull down Microsoft's source code to step through it & find the issue with my code.
The note you found in the output window "The response could not be cached for this request" is a clue.
There's 2 parts to the server-side caching of a request. The server has to prime the cache the first time the url is requested. It will serve the cached version the 2nd time it's requested. Pay attention to when the error message shows up, if it's on the 1st or 2nd request. That'll tell you if it couldn't be stored in the cache or if it couldn't be retrieved from the cache.
The rules for both storage & retrieval are in this source code file: https://github.com/aspnet/ResponseCaching/blob/3bf5f6a1ce69b65c998d6f5c739822a9bed4a67e/src/Microsoft.AspNetCore.ResponseCaching/Internal/ResponseCachingPolicyProvider.cs
Your "Cache-Control:public,max-age=600" header should match these rules just fine.
You already found the no-cache/no-store "gotcha". There are also a few other others with ResponseCaching to watch out for:
Authenticated requests & responses with set-cookie won't be cached. Only requests using GET or HEAD method will be cached. If the QueryString is different, it'll make a new cache entry. Also, usually you'll want a "Vary" header to prevent caching if certain conditions of a request differ from the previously-cached request (example: user-agent, accept-encoding, etc).
On a side note, honoring the no-cache/no-store request headers was probably a poor design choice since ASP.Net Core's ResponseCache will most likely be used by a server who owns the response, rather than an intermediary cache like a CDN/ISP. I've extended the base ResponseCache with an option to disable honoring these headers (as well as serialize the cache to disk, rather than in-memory only). It's an easy drop-in replacement for the default cache.
You can find my extension here: https://github.com/speige/AspNetCore.ResponseCaching.Extensions https://www.nuget.org/packages/AspNetCore.ResponseCaching.Extensions
HttpCacheHeader is not a cache store.
This middleware handles the "backend"-part: it generates the correct cache-related headers, and ensures a cache can check for expiration (304 Not Modified) & preconditions (412 Precondition Failed) (often used for concurrency checks).
Source

Why wont my application cache using OutputCache

I am at a loss to explain why my application is not caching the front page.
I have placed a datestamp on the page: #DateTime.Now
Here is the Home controller action
[OutputCache(Duration = 60)]
public ActionResult Index()
{
return View();
}
However, caching is not working and the front page keeps changing the date.
I have tried to reduce the front page and associated layouts to bare minimum so only the DateTime.Now was being stamped.
I have inspected all base controllers for any disable caching code.
Also inspected the global.asax
Inspected the web.config for any caching configuration - none.
Inspected with fiddler. Here is the response header.
HTTP/1.1 200 OK
Cache-Control: public, no-cache="Set-Cookie", max-age=60
Content-Type: text/html; charset=utf-8
Expires: Wed, 04 Mar 2015 05:20:45 GMT
Last-Modified: Wed, 04 Mar 2015 05:19:45 GMT
Vary: *
I have also tried different browsers that would not have any local caching settings modified.
Have also tried deploying the app to a stage server so it is not run on my dev machine.
Have tried making a fresh asp.net mvc application. That did work. Somewhat helpful in at least telling me the technology works.
Where could the problem be?
A couple of things to check:
If this is on your production environment, check output caching is enabled on IIS. See this page.
Try switching the location of your cache to help narrow down where the issue lies:
e.g.
[OutputCache(Duration = 60, Location = OutputCacheLocation.Server)]
[OutputCache(Duration = 60, Location = OutputCacheLocation.Client)]
Check your web.config for any [caching] entries which may override default behaviour

CRM 4.0 Preview View (preview.aspx) is cached at client

When doing updates to accounts in CRM 4.0, the preview does not update unless we clear the client cache. When requesting the preview (which happens when you click the little arrow shown in the screenshot) with URL shown below, the headers shown below are returned.
GET http://crmdev/DevOrg/_grid/preview.aspx?type=1&id={755E4F74-007D-E011-A3BC-005056B6001D} HTTP/1.1
As can be seen from the headers, there is no expiration date/time on the cache.
HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 1006
Content-Type: text/xml; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 2.0.50727
Persistent-Auth: false
X-Powered-By: ASP.NET
WWW-Authenticate: Negotiate <token removed>
Date: Tue, 19 Jul 2011 13:35:10 GMT
Setting the cache to expire using the method described in the following link has no effect. http://www.iis.net/ConfigReference/system.webServer/staticContent/clientCache
Does anyone have a supported fix for Dynamics CRM 4.0 regarding the caching of the preview.aspx page? Almost all other examined pages in CRM returns 'Cache-Control: no-cache', so seemingly this file overrides the output header.
ADDITIONAL INFO ADDED
This behavior only affects IE9. Either IE9 is handling the caching unlike IE8 and before or IE9 is not allowing the corss tab/popup cache invalidation.
Suggested hotfix (unsupported): Replace preview.aspx with a custom aspx file that uses the original renamed file and has manual control over the cache header. Setting the cache-header to 'no-cache' solves this issue in IE9. We have opted out of this solution as it is unsupported, but it has been tested ok and requires no altering of aspx-content, only "rerouting".
This behavior only affects IE9. Either IE9 is handling the caching unlike IE8 and before or IE9 is not allowing the cross tab/popup cache invalidation.
Suggested hotfix (unsupported): Replace preview.aspx with a custom aspx file that uses the original renamed file and has manual control over the cache header. Setting the cache-header to 'no-cache' solves this issue in IE9. We have opted out of this solution as it is unsupported, but it has been tested ok and requires no altering of aspx-content, only "rerouting".

HTTP caching confusion

I'm not sure whether this is a server issue, or whether I'm failing to understand how HTTP caching really works.
I have an ASP MVC application running on IIS7. There's a lot of static content as part of the site including lots of CSS, Javascript and image files.
For these files I want the browser to cache them for at least a day - our .css, .js, .gif and .png files rarely change.
My web.config goes like this:
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge"
cacheControlMaxAge="1.00:00:00" />
</staticContent>
</system.webServer>
The problem I'm getting is that the browser (tested Chrome, IE8 and FX) doesn't seem to be caching the files as I'd expect. I've got the default settings (check for newer pages automatically in IE).
On first visit the content downloads as expected
HTTP/1.1 200 OK
Cache-Control: max-age=86400
Content-Type: image/gif
Last-Modified: Fri, 07 Aug 2009 09:55:15 GMT
Accept-Ranges: bytes
ETag: "3efeb2294517ca1:0"
Server: Microsoft-IIS/7.0
X-Powered-By: ASP.NET
Date: Mon, 07 Jun 2010 14:29:16 GMT
Content-Length: 918
<content>
I think that the Cache-Control: max-age=86400 should tell the browser not to request the page again for a day.
Ok, so now the page is reloaded and the browser requests the image again. This time it gets an empty response with these headers:
HTTP/1.1 304 Not Modified
Cache-Control: max-age=86400
Last-Modified: Fri, 07 Aug 2009 09:55:15 GMT
Accept-Ranges: bytes
ETag: "3efeb2294517ca1:0"
Server: Microsoft-IIS/7.0
X-Powered-By: ASP.NET
Date: Mon, 07 Jun 2010 14:30:32 GMT
So it looks like the browser has sent the ETag back (as a unique id for the resource), and the server's come back with a 304 Not Modified - telling the browser that it can use the previously downloaded file.
It seems to me that would be correct for many caching situations, but here I don't want the extra round trip. I don't care if the image gets out of date when the file on the server changes.
There are a lot of these files (even with sprite-maps and the like) and many of our clients have very slow networks. Each round trip to ping for that 304 status is taking about a 10th to a 5th of a second. Many also have IE6 which only has 2 HTTP connections at a time. The net result is that our application appears to be very slow for these clients with every page taking an extra couple of seconds to check that the static content hasn't changed.
What response header am I missing that would cause the browser to aggressively cache the files?
How would I set this in a .Net web.config for IIS7?
Am I misunderstanding how HTTP caching works in the first place?
You need to use the Expires directive, otherwise the browser will always check to see if the content has updated.
If a cached entry has a valid expiration date the browser can reuse the content without having to contact the server at all when a page or site is revisited. This greatly reduces the number of network round trips for frequently visited pages. For example, the Google logo is set to expire in 2038 and will only be downloaded on your first visit to google.com or if you have emptied your browser cache. If they ever want to change the image they can use a different image file name or path.
To change in IIS7 use following. This is easiest to manage if you keep static content in specific directories.
Log onto the server
Open IIS Manager (start -> adminstrative tools -> iis manager
Expand the server node
Expand the sites node
Open the site and navigate to the directory you want to change
Open the IIS HTTP Response Headers section
Click Set Common Headers on the task pane on the right
Set "Expire Web Content" as your app requires.
use expires header instead of using the cache-control.Tell your server for the first time that serve me content from my browser cache until this expiry date. There will be no cross checking for changes in file until your expiry date.
add the header in your web.config’s system.webServer section like so:
<system.webServer>
<staticContent>
<clientCache httpExpires="Sun, 29 Mar 2020 00:00:00 GMT"
cacheControlMode="UseExpires" />;
</staticContent>
</system.webServer>
Short anwser: remove Etag and use Expire header.
You should check out the 35 Yahoo Performance best practices, and more specifically:
Configure ETags on your server (removing them is a good choice)
Add Expire header for static resources
For each rule, they usually cover Apache and IIS web server configurations.
Edit: okay, it looks like there is no simple way to remove Etags in IIS, besides installing some 3rd party software...

Resources