When serving a response cached by the server, IIS 7 is stripping the Vary header - caching

I’m using a custom framework for bundling stylesheets and scripts. (I.e., these are dynamically generated responses, not static files.)
The response for the initial request, when the response is being generated for the very first time, includes these headers:
HTTP/1.1 200 OK
Cache-Control: public, no-transform, max-age=31536000
Content-Type: text/css; charset=utf-8
Content-Encoding: gzip
Last-Modified: Mon, 25 Aug 2014 18:15:50 GMT
Vary: Accept-Encoding
Date: Tue, 09 Sep 2014 16:19:36 GMT
Content-Length: 3126
Now that the response above has been generated and cached by the server, subsequent requests for the same stylesheet are responded to with these headers:
HTTP/1.1 200 OK
Cache-Control: public, no-transform, max-age=31536000
Content-Type: text/css; charset=utf-8
Content-Encoding: gzip
Last-Modified: Mon, 25 Aug 2014 18:15:50 GMT
Date: Tue, 09 Sep 2014 16:20:00 GMT
Content-Length: 3126
Ignoring the new Date value, the headers are identical with the obvious exception of the missing Vary header.
One nasty consequence that I’ve seen in the wild is that if the very first response generated for a given asset is not compressed (due to the corresponding client not supporting compression), then the server caches that non-compressed response and serves it for all subsequent requests to all clients.
Any idea how to have the server retain the Vary header for cached responses?
I’m using HttpCacheability.Public for these responses. I can avoid the issue by using HttpCacheability.Private instead, but I’d prefer to allow the server and proxies to cache responses.
Some reading has led me to believe that IIS can’t do “kernel caching” if you vary by encoding. But I’m not sure if that means I can’t cache on the server at all or if it just prevents a special kind of server-side caching.
Update:
I was originally using the following to set the Vary header:
response.AppendHeader("Vary", "Accept-Encoding");
I tried a different method of specifying it:
response.Cache.SetVaryByCustom("Accept-Encoding");
That caused Vary to never be emitted. Not even on the very first response.
As a last resort, I’m also considering using:
response.Cache.SetNoServerCaching();
That causes the Cache-Control header to still specify public (so that proxies can still cache), but prevents the server from caching.

In line with the update I made to the question, I tried yet another way of specifying the Vary header:
response.Cache.VaryByHeaders["Accept-Encoding"] = true;
…And it fixed the problem. Responses now retain the Vary header across requests from multiple clients and are also cached by the server.

Related

Why is this file not cached in the browser? What's wrong with my headers?

I'm using Cloudfront (with Cloudflare in front) to serve the following file:
https://app.astrobin.com/assets/i18n/en.po?version=1623337803841
These are the response header at the time of writing:
accept-ranges: bytes
age: 2825
cf-cache-status: DYNAMIC
cf-ray: 65d3e1df6fe70f9a-VIE
cf-request-id: 0a984b7fa400000f9ac0139000000001
content-length: 6626
content-type: application/octet-stream
date: Thu, 10 Jun 2021 16:12:37 GMT
etag: "6336cef3a9da96c3c432813762cd7d70"
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
last-modified: Thu, 10 Jun 2021 15:17:27 GMT
nel: {"report_to":"cf-nel","max_age":604800}
report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v2?s=TipuJtVNh6YvmqS4rzXv5mtQ%2BUKVHBvok88InKPm%2FNGqK13b2EX1%2BxMEuMfCgp4qslhW0xc1qeIzX1xpUzNW5u5rC8Di3lsb4zNqaoN0zhhH5E3wbizW%2FjlbqD5C"}],"group":"cf-nel","max_age":604800}
server: cloudflare
via: 1.1 3d4555926457517be3e728d2175d92a3.cloudfront.net (CloudFront)
x-amz-cf-id: MfDo647EBTxmyo7TZ-sseALca4bmzBPdPPnuiesvGjNnlkgqLzgwDA==
x-amz-cf-pop: VIE50-C2
x-cache: Hit from cloudfront
The following image is my Cloudfront "behavior" rule for that file:
I don't have any Page Rules in Cloudflare for this file.
I'm performing multiple requests to this file, for testing purposes, but the file is never fetched from Chrome's disk or memory cache. It always goes to the network.
As you can see from the request URL, I append the timestamp of the last known modification to this file, for cache-busting purposes, so I would like the file to be cached in the browser.
What am I missing?
Thanks!
Browser caching is determined by the caching headers (namely Cache-Control or Expires) in the response from the origin server. But the response you listed doesn't have such a header, so it's up to the browser to determine how long the resource is considered fresh.
If you want the browser to use a particular cache policy you should add a Cache-Control header to the response.

Cloudfront - why is this not being "browser cached"?

Here's the curl -I response to my Javascript file:
HTTP/1.1 200 OK
Content-Type: text/javascript
Content-Length: 72640
Connection: keep-alive
Date: Sat, 18 Feb 2017 16:12:06 GMT
Cache-Control: 86400
Last-Modified: Wed, 15 Feb 2017 15:09:28 GMT
ETag: "a6ee06ff5e49a4290bb2aabe5e0f9029"
Server: AmazonS3
Vary: Accept-Encoding
Age: 1173
X-Cache: Hit from cloudfront
Via: 1.1 3b17302562f1709d8b6c9f7be1.cloudfront.net (CloudFront)
I can see the Cache-Control tag there. Not sure what the Vary and the ETag are doing, but so be it. Does this somehow specify to a user's browser NOT to cache this file? Why are Pingdom or Goog PageSpeed not recognising this as a browser-cacheable file?
Your Cache-Control header is present, but the value is not actually valid. The correct format looks like this:
Cache-Control: max-age=86400
The number, by itself, is meaningless.
ETag: is the entity tag -- an opaque value that uniquely identifies the current content of a given URL. If the content changes, the ETag will also change. A browser with a cached copy may use this value for subsequent requests to ask that the server only return the content if it differs, by sending an If-None-Match: request header, including the last-seen ETag.
Vary: tells the browser that certain changes to the request may generate a different response. Unlike browsers, curl doesn't advertise its ability to support gzipped payload unless you specify the --compressed option. Adding that option when invoking curl triggers the addition of Accept-Encoding: gzip to the request, which may trigger the response to be compressed if you have that option enabled in CloudFront.

Why caching ajax request is not working?

I have a large json data object (over 300K uncompressed, 40K gzipped) which is used on every page of my internal system. I want to fetch it every 15 minutes. In this time user will probably visit tens of pages of my system.
The HTTP Response headers in Firebug look like:
Cache-Control max-age=899, public
Connection Keep-Alive
Content-Encoding gzip
Content-Length 44017
Content-Type text/html; charset=iso-8859-2
Date Tue, 04 Dec 2012 16:21:45 GMT
Expires Tue, 04 Dec 12 17:36:45 +0100
Keep-Alive timeout=15, max=99
Last-Modified Tue, 04 Dec 12 17:21:45 +0100
Pragma no-cache
Server Apache
Set-Cookie user_auth=xxx; expires=Wed, 12-Dec-2012 16:21:45 GMT; path=/; domain=example.com
Vary Accept-Encoding
X-Genaration-Time 0.13282179832458 sec.
X-Genarator vCRM 3.1 (c) Veracomp S.A.
X-Powered-By PHP/5.3.3-7+squeeze14
The cache headers are set to 15 minutes in future, but neither Chrome nor Firefox caches it.
The Firebug says this about cache:
Data Size 44017
Device disk
Expires Thu Jan 01 1970 01:00:00 GMT+0100
Fetch Count 5
Last Fetched Tue Dec 04 2012 17:21:45 GMT+0100
Last Modified Tue Dec 04 2012 17:21:45 GMT+0100
It seams the Expires header is ignored, but why?
This sould not mattery, but I better write, that the content type is text/html, so the server can gzip it, but in reality the content is JSON.
I am using Prototype.js to request this. I set the request header:
Cache-control: max-age=900
Prototype.js does not add any cache buster parameter to the url.
I am using PHP with Zend_Framework to set serve the response.
What am I doing wrong?
Problem solved.
As #Victor pointed out, I had the "Pragma: no-cache" header set. The header was somehow set by PHP. I worked with our webserver admin and we had managed to unset the header with Apache.
Still it was not enough. Our framework sets cookies with every page refresh and I could not turn it of. Browsers did not want to cache the requests which set cookies. We had to unset the Set-Cookie headers too.
Finally this two unsets allowed us to enable cache:
<LocationMatch "(?i)/url/we/want/to/be/cached/.+">
Header unset Pragma
Header unset Set-Cookie
</LocationMatch>

How do I know if image from my site is getting cached by proxy servers?

The following is a http response header from a image on our company's website.
HTTP/1.1 200 OK
Content-Type: image/png
Last-Modified: Thu, 03 Dec 2009 15:51:57 GMT
Accept-Ranges: bytes
ETag: "1e61e38a3074ca1:0"
Date: Wed, 06 Jan 2010 22:06:23 GMT
Content-Length: 9140
Is there anyway to know if this image is publicly cacheable in some proxy server? The RFC definition seems to be ambiguous http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.1 and http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4.
Run RED on your URL and it'll tell you whether the response is cacheable, among other information.
The headers you show appear to be cacheable.
If you would like to control the caching behavior of correctly configured proxies and web browsers, you might investigate using the Cache-Control and Expires headers to gain additional control.
Here is a webpage I had bookmarked that has one person's opinion of how to intepret the specifications you list (plus some other ones):
http://www.web-caching.com/mnot_tutorial/how.html
If you need to guarantee that someone sees a completely new image each time (even with misconfigured devices between you and them), you may want to consider using a randomized or GUID value as part of the URL.
Here is a tutorial on setting headers for proxy caching. Be sure to read the part about setting cookies!

Why doesn't my expires headers make my files stay in cache?

I'm using valid expires and no-cache headers for my static files and they stay cached for as long as I keep browsing, but when I close my browser and use it back after a while I see the static files loading again, even when not refreshing with ctrl (+ shift) + r
I'm using Firefox, cache size set to 250MB and I don't let it remove any private or cached data.
Headers:
Accept-Ranges: bytes
Cache-Control: max-age=29030400, public
Content-Length: 142061
Content-Type: image/png
Date: Tue, 08 Dec 2009 19:18:43 GMT
Expires: Tue, 09 Nov 2010 19:18:43 GMT
Last-Modified: Sun, 18 Jan 2009 18:33:48 GMT
Server: Apache/2.2.14 (EL)
Which version of Firefox? Is the server sending Etags for the static files? You can view details about Firefox cache by going to the address about:cache and poking around. That will give you an idea of what Firefox is caching.
Update: After looking at your header tags, it seems as if the max-age value is set to a date that is way in the past and that is overriding the the value being set in the Expires header. See the HTTP 1.1 protocol definition at: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.3.
If a response includes both an Expires
header and a max-age directive, the
max-age directive overrides the
Expires header, even if the Expires
header is more restrictive. This rule
allows an origin server to provide,
for a given response, a longer
expiration time to an HTTP/1.1 (or
later) cache than to an HTTP/1.0
cache. This might be useful if certain
HTTP/1.0 caches improperly calculate
ages or expiration times, perhaps due
to desynchronized clocks.
You will have to modify your Cache-Control header being sent by the server.

Resources