It's now so natural and frequent that we don't even realize it: Every response we get through a web browser is cached somewhere in our local disk so that the browser can serve successive requests for the same resource from the local cache instead of disturbing the always busy web server. OK, the process isn't quite so simple and straightforward, but you get the point. If every response is cached, however, how can we develop our web applications easily? If you're developing an ASP.NET application that links JavaScript files, you know what I mean. Frequent changes to the script file are not detected by the browser because they are cached somewhere. So you need to clear the browser's cache a million times a day and refresh the page to see your latest changes. With ASPX pages, however, this never happens. In which way is an ASP.NET page different from a JavaScript file or a CSS stylesheet?
For one thing, an ASP.NET page is a dynamic resource, whereas a JavaScript file is mostly a static resource. But this difference doesn't explain much—browsers aren't supposed to distinguish between resource types. Browsers are like a dummy terminal: They do exactly what the response they receive tells them to do. The main difference between ASP.NET pages and JavaScript files is in the HTTP headers that accompany the response.
In this article, I'll review browser caching basics and the techniques you can use to modify cache settings for ASP.NET pages.
Browser Caching Rules
Note that browsers won't cache any responses that explicitly prohibit caching. Browsers also won't cache responses that come from a secure HTTPS channel or that require authentication. Aside from that, any response is cacheable.
For each requested URL, a browser always looks to the local cache first. If the requested URL doesn't have a match in the local cache, the browser sends the request on to the server. If a match is found, the browser checks whether the cached output of the requested resource is still valid. A valid output is any output that has not expired. If a valid output is found for a requested URL, the request is served directly from the cache without contacting the server.
The browser uses information originally contained in HTTP response headers to determine whether a given output is fresh or stale. The Expires HTTP header, for example, indicates the absolute expiration date of the resource. The max-age HTTP header indicates for how long the response has to be considered fresh since the time of download.
If the response is stale, but a clear expiration date or an age was specified, the browser will ask the original server to validate the representation. In this case, the browser request will include the If-Modified-Since request header set to the value of the expiration date. The status code will be HTTP 304 (not modified), if nothing has changed since—meaning that the currently cached resource is still the most up to date. In this case, the request is still served from the cache; otherwise, a new request is made to get a fresher response.
These simple rules express the behavior of any web caches, including not only browser caches but also downstream proxy caches. If two types of resources (i.e., ASPX and JS files) behave differently, the trick is in the HTTP response headers they are downloaded with.
Behavior of ASPX Resources
An ASP.NET page is not a readily cacheable resource because its content might be different, even when the requesting URL is the same. An image that represents a company's logo, or a script file, is a much more static type of resource and inherently more cacheable. The default behavior of the ASP.NET page results from the following similar list of HTTP headers, the most important of which is the Cache-Control header:
Cache-Control private
Content-Type text/html; charset=utf-8
Server Microsoft-IIS/7.5
X-AspNet-Version 4.0.30319
X-Powered-By ASP.NET
Date Wed, 08 Dec 2010 20:09:06 GMT
Content-Length 12315
A value of private in the Cache-Control header indicates that an ASP.NET page is cacheable by web browsers, but not by downstream proxy servers. In addition, the lack of Expires and max-age headers indicates that an ASP.NET page has no expiration set; therefore, it's always stale. In addition, the browser has no information to submit to the server through the If-Modified-Since header. The bottom line is that any request you make for an ASPX resource will always result in an immediate new fetch from the server as if the page was never cached.