This post will be the first of what will be many posts with regards to application performance. After giving a number of presentations over the last 1-2 years, including one this evening in the Minneapolis/St. Paul area I've decided that some of these tips/tricks that I have with regards to improving application performance should really get put out here so people can find them and make the improvements to their applications. Going forward all of these posts will be categorized with at least the "Performance" category and then others that identify what systems are benefited by the changes. The subject of this post is Static Content Caching.
What is Static Content Caching and Why Do I Care?
Simply put what I am talking about here is an added piece of information that is sent along with the content of your web application that specifies the period of time that the user should be able to keep a copy of the file without requesting anything new from the server. In actual implementation what this means is when User A requests Picture1.jpg from your server not only do they get the file, but they get additional metadata within the response header that says, "Cache this until XX/XX /XXXX at yy:yyAM". With this information the browser can then elect to simply show the image from cache rather than re-requesting the image file every time you go to the page.
Why can this be helpful? Take into consideration a web application that has a graphics intensive design, potentially with multiple images in a rotator or similar function. Each time the page loads there are a number of assets that have to be downloaded. By default browsers will only make 4 requests to the same server at the same time, so if you have 16 elements that need to be loaded there are going to be at least 4 sets of trips to the server to get the results. If we put expiration duration's on the files, we can avoid re-loading the content that doesn't change.
In this post I am going to talk specifically around the .NET configuration items for static content caching. This applies to any static piece of content that is NOT processed by the ASP.NET runtime. So items such as images, css files, js files, and the like with extensions that are NOT dynamically generated.
The Impact - Show Me The Data!
To provide a numerical example of exactly how drastic of an improvement this can have on page load, user perceived performance, and reduced server bandwidth I'll share some statics on a demonstration I gave this evening.
Looking at a web application homepage that does a default load with a number of images in content, skin, and content rotators using Fiddler we are able to see that the baseline configurations of the environment that a total of 52 web requests are needed to assemble the homepage and a total page size of 1.97Mb. Now, don't get me wrong, this page is WAY too heavy and that is a separate discussion for another post, but this met a clients need.
After adding the three lines to the web.config as outlined in the next section I tried to make a second request to the page. This time with the content refreshed the expiration headers all came down with the content. Upon a third load to the page I reviewed the results and a total of 3 web requests were needed for a total page size of 31 Kb. Now, 51 -> 3 and 1.97Mb -> 31 K is a huge difference for a simple web.config change.
Yes, the initial page load is still strong, but we are doing everything we can to ensure that the web server is only spending time serving the content that it needs to. We also make sure that we are not unnecessarily using bandwidth on the client side to request content that hasn't changed.
If you are running ASP.NET and running on IIS7, implementation is truly simple. Within the web.config of the application you would like to update, simply add the following within the <system.webserver> node.
<clientCache cacheControlMaxAge="7.00:00:00" cacheControlMode="UseMaxAge" />
This sets the cache expiration time to 7 days for all static content. You can modify the value in the cacheControlMaxAge property as you see fit, the values are dd:hh:dd:ss so you can control with great precision how long it is cached.
As always feel free to share you successes, questions, or comments below! If you have an idea for a future performance post, please feel free to contact me.