A few months ago we launched a new website on SharePoint 2010. One of my main foci on the project was performance and caching is one of the most effective ways to achieve that for a WCM solution. We enabled Output, Object and BLOB caching, configured exclusions as necessary and were quite pleased with the results, especially since issues with BLOB Caching in 2007 have been resolved in 2010.
A few weeks later I was demonstrating these approaches when it was pointed out that we were getting lots of 304 responses. They occurred with each request for a previously-downloaded BLOB Cached asset (more detail added below). Basically, I overlooked the max-age attribute in the BLOB Cache web.config settings. By default, this attribute isn’t present in the web.config file and I simply missed it. Adding this attribute eliminated the 304 results and the caching configuration was complete. Or so we thought.
Edit to provide more detail on the 304 status and Max-Age
A 304 response is a File Not Modified status (not an error), in this case indicating that the browser is making (potentially) surplus checks for each previously-downloaded BLOB Cached file. The max-age attribute gives the file a lifetime in the client’s browser cache in order to reduce these update checks. To be clear, the BLOB Cache stores large objects on web servers to reduce database traffic, but those objects can be served with a max-age attribute that will determine the object’s lifetime in the client’s browser cache. A max-age value of “14400” means that browsers will cache the file for four hours before checking for an update. This means that updates to BLOB Cached content may become stale if this value is set too high. A common value would be “86400” (24 hours) but we were satisfied with the balance at four hours. In our case, making this update has not yielded a perceptible increase in performance with the current levels of traffic, but it’s the sort of thing you want to set appropriately in order to optimise things and to allow the environment to scale.
So… yesterday we were told that the 304 results were still appearing after making this change some time ago. When I started testing it I quickly confirmed there were loads of 304 results when I requested BLOB Cached assets for a second time. I tried clearing my browser caches, closed down all browsers and tested in multiple browsers but couldn’t get to the bottom of it, as the BLOB Cache max-age attribute was definitely set in the web.config file.
A bit stumped, I asked knowledgeable people for help. No luck. To recap, this was the full context:
- The Caching column in Fiddler revealed that the max-age attribute was set. I could see the specified value coming through as “public, max-age=14400”. If I changed the value to 86400, the value would get updated on refresh. Clearly the web.config attributes were served correctly.
- In IE I noticed the “Pragma: no-cache” header. In Firefox I saw “Cache-Control: max-age=0”. I couldn’t figure out where these headers were coming from and suspected that was the source of the problem.
Eventually I pinpointed that this behaviour only occurred when I hit F5 or pressed Refresh in the browser. If I typed in the address or clicked on a link I would just get the HTTP 200 results as I would expect. If I hit CRTL+F5, everything would get downloaded afresh (lots of 200s), also as expected. I asked Chris O’Brien if he had any ideas why this would only happen with F5/Refresh and he pointed out that this is exactly what does happen, and a lot of people don’t realise it. Excellent! Learned something new today. Either max-age or F5 could be the cause of the 304 results, but F5 gives a false impression of what users would see when accessing an asset for the second time, unless those users are pressing Refresh a lot. If they’re browsing normally, they’ll in fact get a 200 result. If max-age isn’t set, the 304 results will accurately reveal that users are checking to see if the BLOB Cached file is updated every time they try to access it. In some cases this will be desirable, but it definitely adds up quickly.
Pressing on, I wanted to get to the bottom of what else might be different with F5/Refresh and figure out what was up with those Pragma/Cache-Control headers. It turns out, it’s the F5/Refresh itself that generates the Pragma/Cache-Control header. All mysteries solved! Many thanks to Chris for his help with this. Hope this can help someone else.