So yesterday I applied the windows update to install IE9, and discovered that my ajax calls to update a grid stopped working. Chrome & Firefox gave the expected results (i.e. my grid showed changes), but in IE9 seemed to display whatever the first load was.

IE9, it turns out, makes 2 different kinds of requests, conditional and unconditional requests (for more checkout http://blogs.msdn.com/b/ie/archive/2010/07/14/caching-improvements-in-internet-explorer-9.aspx). Unconditional requests occur when the browser doesn’t have a cached copy, and so the browser makes a new request to the server, and using details in the response header, determines how to cache the document. Conditional requests occur when a cached copy is available. It first evaluates whether the document is still fresh, and if so, serves the cached copy of the document.

Many people seem to counter this by passing a date time stamp string as a URL parameter, making each request unique, and thus the browser is always making an unconditional request, and always getting a new copy. This just seems dirty, as you’re passing up a parameter thats not required. It’s also quite error prone, as if you forget to add the parameter you’ll run into caching problems that often aren’t easy to spot.

Luckily ASP.NET MVC makes it relatively straight forward to control the “Cache-Control” section of the HTTP 200 response header field, which is what IE9 uses to determine its caching strategy.

A normal MVC response header looks something like this:

By default, MVC responses set the Cache-Control setting to private, which the HTTP specification (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.1) describes as:

Indicates that all or part of the response message is intended for a single user and MUST NOT be cached by a shared cache. This allows an origin server to state that the specified parts of theresponse are intended for only one user and are not a valid response for requests by other users. A private (non-shared) cache MAY cache the response.

The result of this is that IE9 will cache the page in the current user’s private store, and will conditionally serve the cached copy until it reaches it’s default expiry time.

To change this setting, we simply add an attribute to our MVC controller.

[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public class CacheTestController : Controller

We’ve set “no-store” to true (meaning don’t cache), and the “duration” to 0 (so it immediately expires). This will ensure that every subsequent request for this page will result in a new trip to the server, and a fresh copy being served.

A much neater solution, that requires no changes to our URL’s, or the parameters passed to it.