views:

84

answers:

3

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?

+1  A: 

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>
sushil bharwani
How would I do that in IIS7? Also - why does that work when `cache-control` doesn't?
Keith
Thanks for the clarification - so is `cache-control` broken?
Keith
no i believe there something both you and i are missing i am reading more on cache-control at this point will let you know if i figure out clear. Yes but did my suggestion worked for you
sushil bharwani
Your XML config here sets the static content to use the HTTP `Expires` header, while mine in the question causes it to use the `Cache-Control` header. After some investigation it appears that either works fine, though some really old browsers don't recognise `Cache-Control` (before IE 5.5). Lots of advice on the web states to use `Expires`, but we don't support IE5 so it's not a problem. Thanks for the answer(+1)
Keith
+2  A: 

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.

Gary
Thanks - your steps in IIS7 will actually produce the same web.config XML that is in my question though.
Keith
+1  A: 

Short anwser: remove Etag and use Expire header.

You should check out the 35 Yahoo Performance best practices, and more specifically:

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...

Brian Clozel
Unfortunately it doesn't seem possible to remove the ETags on IIS7 - see http://stackoverflow.com/questions/477913
Keith
Why would the `expire` header work when `cache-control` doesn't? We don't have to support IE5 and everything past that supports HTTP 1.1
Keith
Thanks for the answer (+1). `Expires` and `Cache-Control` work equally well (unless you're on IE5 or below, then only `Expires` does) as long as either is set you're good. Turns out that my actual problem was that I was hitting F5/refresh to check, and that always causes the browser to ping for each file and get the _304 Not Modified_ responses.
Keith