I have a strange issue with IIS 7.
Sometimes it seems to return a 304 instead of a 200.
Here is a sample request captured with Fiddler:
(Note that the file requested is not located in my browsers cache yet.)
GET https://[mysite]/Content/js/jquery.form.js HTTP/1.1 Accept: */* Referer: https://[mysite]/Welcome/News Accept-Language: sv-SE User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; OfficeLiveConnector.1.4; OfficeLivePatch.1.3; .NET4.0C; .NET4.0E) Accept-Encoding: gzip, deflate Host: [mysite] Connection: Keep-Alive Cache-Control: no-cache Cookie: ...
Note that there is no If-Modified-Since or If-None-Match in the request.
But still the response is:
HTTP/1.1 304 Not Modified Cache-Control: public Expires: Tue, 02 Mar 2010 06:26:08 GMT Last-Modified: Mon, 22 Feb 2010 21:58:44 GMT ETag: "1CAB40A337D4200" Server: Microsoft-IIS/7.5 X-Powered-By: ASP.NET Date: Mon, 01 Mar 2010 17:06:34 GMT
Does anyone have a clue of what could be wrong here?
I'm running IIS 7 on Windows Web Server 2008 R2.
EDIT:
I've found a workaround, enable caching and then disable it on an extension level did the trick for me.
<configuration>
<system.webServer>
<caching enabled="true" enableKernelCache="true">
<profiles>
<add extension=".png" policy="DisableCache" kernelCachePolicy="DisableCache" />
<add extension=".gif" policy="DisableCache" kernelCachePolicy="DisableCache" />
<add extension=".js" policy="DisableCache" kernelCachePolicy="DisableCache" />
<add extension=".css" policy="DisableCache" kernelCachePolicy="DisableCache" />
</profiles>
</caching>
<staticContent>
<clientCache cacheControlMode="NoControl" />
</staticContent>
</system.webServer>
</configuration>
According to section 14.9 of the HTTP1.1 spec, the
no-cache
directive for the Cache-Control header is only imposable by the origin server, which means IIS is ignoring the header in your request.Section 14.9.1 defines
public
,private
, andno-cache
as the directives restricting what is cacheable, which can only be imposed by the server.If you don't want your .js file to be cached you'll either need to set the
no-cache
directive in the app (ie- the ASP.NET code) or you'll need to change theCache-Control
header in the request to use theno-store
directive instead ofno-cache
.EDIT:
Based on your comment - yes I assumed you did not want the file cached. The 304, then, might be coming as a result of the file being in one of IIS's internal caches. Have a look at these:
I have been having the same problem for a while and have all caching turned off... However, I installed the Compression module for IIS7 at some point which by default had enabled compression of static files on my existing sites. I turned all compression off for the impacted sites and now they seems to be working fine touch wood.
We were also experiencing this bug but we were using an asset management library (Cassette). After an extensive investigation of this issue, we've found that the root cause of this issue is with a combination of ASP.NET, IIS, and Cassette. I'm not sure if this is your problem (using the
Headers
API rather than theCache
API), but the pattern seems to be the same.Bug # 1
Cassette sets the
Vary: Accept-Encoding
header as part of its response to a bundle since it can encode the content with gzip/deflate:However, the ASP.NET output cache will always return the response that was cached first. For example, if the first request has
Accept-Encoding: gzip
and Cassette returns gzipped content, the ASP.NET output cache will cache the URL asContent-Encoding: gzip
. The next request to the same URL but with a different acceptable encoding (e.g.Accept-Encoding: deflate
) will return the cached response withContent-Encoding: gzip
.This bug is caused by Cassette using the
HttpResponseBase.Cache
API to set the output cache settings (e.g.Cache-Control: public
) but using theHttpResponseBase.Headers
API to set theVary: Accept-Encoding
header. The problem is that the ASP.NETOutputCacheModule
is not aware of response headers; it only works via theCache
API. That is, it expects the developer to use an invisibly tightly-coupled API rather than just standard HTTP.Bug # 2
When using IIS 7.5 (Windows Server 2008 R2), bug # 1 can cause a separate issue with the IIS kernel and user caches. For example, once a bundle is successfully cached with
Content-Encoding: gzip
, it's possible to see it in the IIS kernel cache withnetsh http show cachestate
. It shows a response with 200 status code and content encoding of "gzip". If the next request has a different acceptable encoding (e.g.Accept-Encoding: deflate
) and anIf-None-Match
header that matches the bundle's hash, the request into IIS's kernel and user mode caches will be considered a miss. Thus, causing the request to be handled by Cassette which returns a 304:However, once IIS's kernel and user modes process the response, they will see that the response for the URL has changed and the cache should be updated. If the IIS kernel cache is checked with
netsh http show cachestate
again, the cached 200 response is replaced with a 304 response. All subsequent requests to the bundle, regardless ofAccept-Encoding
andIf-None-Match
will return a 304 response. We saw the devastating effects of this bug where all users were served a 304 for our core script because of a random request that had an unexpectedAccept-Encoding
andIf-None-Match
.The problem seems to be that the IIS kernel and user mode caches are not able to vary based on the
Accept-Encoding
header. As evidence of this, by using theCache
API with the workaround below, the IIS kernel and user mode caches seem to be always skipped (only the ASP.NET output cache is used). This can be confirmed by checking thatnetsh http show cachestate
is empty with the workaround below. ASP.NET communicates with the IIS worker directly to selectively enable or disable the IIS kernel and user mode caches per-request.We were not able to reproduce this bug on newer versions of IIS (e.g. IIS Express 10). However, bug # 1 was still reproducible.
Our original fix for this bug was to disable IIS kernel/user mode caching only for Cassette requests like others mentioned. By doing so, we uncovered bug # 1 when deploying an extra layer of caching in front of our web servers. The reason that the query string hack worked is because the
OutputCacheModule
will record a cache miss if theCache
API has not been used to vary based on theQueryString
and if the request has aQueryString
.Workaround
We've been planning to move away from Cassette anyways, so rather than maintaining our own fork of Cassette (or trying to get a PR merged), we opted to use an HTTP module to work around this issue.
I hope this helps someone ?!