Configuring HTTP Response caching in ASP.Net and ASP.Net MVC

Also known as simply HTTP caching or Response caching.

About HTTP Response Caching

When requesting a web page from a site, the browser interprets the HTML it receives from the server. It turn, it uses this new information to make more requests for other files; css, JavaScript, images, etc.

Here are the requests as shown by Firefox’s Developer Tools’ network tab when starting a sample ASP.Net / ASP.Net MVC application (it could be any website):

cache1

cache1a

Here we can see the additional files requested and served for displaying the page: Site.css, bootstrap.min.css, etc.

If we hit F5 to reload the same page once again, we get a slightly different result:

cache2

cache2a

This time we can see the same files being requested, with two differences. The HTTP return codes for the subsequent files are 304 instead of 200 (more on that later) and the total for all the 6 requests has gone from 0.11s to 0.07s.

The HTTP status code 200 is the OK status code. In this case it means the GET request was successful and that we have obtained the requested file. Since this was the first time accessing the site we had to download each file.

On the other hand, on the second page load we had HTTP status code 304 which is the NOT MODIFIED status code. When a browser requests some files it keeps them in it’s local cache. This cache is on the user’s machine and managed by the browser.

When receiving the original files the browser checks their http headers which contain amongst other things information about caching. When making a second request it sends (depending on the caching configuration in the headers) some information to validate if the files have been changed and needs to be downloaded again.

If the server can ascertain the files have not changed it returns a 304 result and the browser doesn’t download the file on the subsequent requests saving us some time.

If some of the files have changed, it will download them again. To know if a file has changed the browser will either send a last modification date or an ETag (a sort of fingerprint). We can test that by modifying one of our files, in my case Site.css, and reloading again.

cache3

Now we can see that the server was able to know that Site.css had changed and needed to be downloaded again by the client.

We can also test, by clearing our local cache, by doing a Ctrl-F5 or by starting a private browsing session that in the case where there is no cache anymore the files are downloaded again and that we get a 200 result like in the first image.

In the case where we used the local cache and got 304s, even tough the files haven’t been downloaded a second time we still had to make requests and wait for responses. We can do away with those requests altogether by configuring our http headers. This means that while getting a 304 is better than downloading the whole file, we can do even better by not making any requests at all.

Configuring your ASP.Net or ASP.Net MVC application for HTTP Response caching

What we’ve seen so far applies to all web servers and clients. Let’s see now how to configure our caching for an ASP.Net application running on IIS.

In an ASP.Net or ASP.Net MVC application it is possible to configure this through the web.config file. Here I will show you how to configure this for static files. Take note that these settings do not apply to Visual Studio’s built-in IIS server, only to applications deployed on an IIS instance. You may need to deploy your web application on IIS to test if you have been successful in applying your settings.

The following settings are for IIS 7. For IIS 6 you must use different configuration elements as mentioned here.

<system.webServer>
<staticContent>
<clientCache cacheControlCustom="public"
cacheControlMode="UseMaxAge"
cacheControlMaxAge="1.00:00:00" />
</staticContent>
</system.webServer>

Here we have configured our static files so that the browser will use the cached version for 1 day. MaxAge indicates how long the cached files will be used. The other way to configure this is to use httpExpires which instead specifies a date up until which the cached content will be used.

After configuring this we can run our test again. But this time by navigating on another page that uses the same .css and .js files.

clientcache

As we can see the static content files haven’t been requested. The browser won’t request those files until the max age expires. In the Transferred column, instead of seeing some byte count we see cached which indicates the cached version has been used.

We need to navigate to another page to see those results because reloading the same page with the Network tab will always result in a 304 in the Firefox Developer Tools.

Back to our web.config file. We can also use the location element to target specific folders. This allows us to use various caching policies on different files.

<location path="Content">
<system.webServer>
<staticContent>
<clientCache cacheControlCustom="public"
cacheControlMode="UseMaxAge"
cacheControlMaxAge="1.00:00:00" />
</staticContent>
</system.webServer>
</location>

Be careful if you set a MaxAge or httpExpires too far in the future or you might get some problems as you change those files and clients aren’t aware of the changes since they are not making any request.

One way to get around that is to change the name of the file. Say the client has cached Site_ASDF9494.css if you change to Site_BWDF9596.css the browser will treat this as a different file and download it again whatever the caching directives may be. It is possible to append a hash or some form of fingerprint to your files. Better yet, if you are using the bundling facilities in ASP.Net or ASP.Net MVC, a cache busting identifier is appended for you behind the scenes so you don’t have to do anything.

Details on cacheControlCustom

cacheControlCustom allows to specify some custom parameters of which public and private  are of particular importance. These indicate whether the files are to be treated as public (for example your site’s css file) or private (for example some file served to an authenticated user). The reason is that while we have only talked about browser caching up until now, caching can also happen at other levels, a CDN or a proxy for example. These will not cache private files but may cache public files.

By forgetting to specify this we may cause some sensitive information to be displayed to incorrect users. If some files contain content specific to a particular user and these files are cached on a proxy, they could be served to several different users who are behind this same proxy.

Make sure to be mindful of this one as the default value is public unless specified otherwise.

Conclusion

As modern sites can make a lot of requests, even when taking into account bundling and CDNs, it is important to be aware of how to reduce these requests to a maximum while still keeping a control of our files for future updates. Response caching is a good way of achieving this.

Leave a comment