ASP.NET 3.5 : Caching ASP.NET Pages (part 1) - The @OutputCache Directive

2/18/2013 6:26:04 PM
There are many situations where it is acceptable for a page response to be a little stale if this brings significant performance advantages. Want an example? Think of an e-commerce application and its set of pages for the products catalog. These pages are relatively expensive to create because they could require one or more database calls and likely some form of data join. All things considered, a page like this could easily cost you a few million CPU cycles. Why should you regenerate this same page a hundred times per second? Product pages tend to remain the same for weeks and are rarely updated more than once per day. Whatever the refresh rate is for the pages, there’s little value in regenerating them on a per-request basis.

A much better strategy is to create the page once, cache it somewhere, and give the page output a maximum duration. Once the cached page becomes stale, the first incoming request will be served in the standard way, running the page’s code, and the new page output will be cached for another period until it also becomes stale.

ASP.NET page output caching is the feature that allows you to cache page responses so that following requests can be satisfied without executing the page; instead, the requests are satisfied by simply returning the cached output. Output caching can take place at two levels—entire pages or portions of the page. Page caching is smart enough to let you save distinct output based on the requesting URL, query string or form parameters, and even custom strings.

Easy to set up and terrifically effective, output caching can either be configured declaratively through the @OutputCache directive or programmatically through an API built around the HttpCachePolicy class.


Page output caching is simply a way to have the application serve more pages more quickly. It has nothing to do with sophisticated caching strategies or elegant code design. In other words, it will enable your application to serve pages faster, but it won’t necessarily make the application more efficient and scalable. With page output caching, you can certainly reduce the workload on the server as pages are cached downstream. Finally, be aware that page output caching works only for anonymous content. Requests for cached pages are served by Internet Information Services (IIS) 6.0 directly or by the ASP.NET worker process under IIS 5.0. In any case, a page request never reaches stages in the ASP.NET pipeline where it can be authenticated, which is a strategy employed to prevent access to protected content.

1. The @OutputCache Directive

Caching the output of a page is as easy as defining an @OutputCache directive at the top of the page. The directive accepts a handful of attributes, a couple of which—Duration and VaryByParam—are mandatory. The Duration attribute indicates in seconds how long the system should cache the page output. The VaryByParam attribute allows you to vary the cached output depending on the GET query string or form POST parameters. The following declaration will cache the page for one minute regardless of any GET or POST parameters:

<%@ OutputCache Duration="60" VaryByParam="None" %>

For frequently requested pages and relatively static pages, the @OutputCache directive is a real performance booster. With a shorter duration, even limited to one second or two, it provides a way to speed up the entire application.

The @OutputCache directive consists of six attributes that indicate the location of the cache, its duration, and the arguments to use to vary page caching. The list of supported attributes is shown in Table 1. Note that the directive can be applied to both pages (.aspx) and user controls (.ascx). Some of the attributes are valid in one case but not the other.

Table 1. Attributes of the @OutputCache Directive
AttributeApplies toDescription
CacheProfilePageAssociates a page with a group of output caching settings specified in the web.config file (More later). Not supported in ASP.NET 1.x.
DurationPage, User controlThe time, in seconds, that the page or user control is cached.
LocationPageSpecifies a valid location to store the output of a page. The attribute takes its value from the OutputCacheLocation enumeration.
NoStorePageIndicates whether to send a Cache-Control:no-store header to prevent additional storage of the page output. Not supported in ASP.NET 1.x.
SharedUser controlIndicates whether the user control output can be shared with multiple pages. It is false by default.
SqlDependencyPage, User controlIndicates a dependency on the specified table on a given SQL Server database. Whenever the contents of the table changes, the page output is removed from the cache. Not supported in ASP.NET 1.x.
VaryByControlUser controlA semicolon-separated list of strings that represent properties of the user control. Each distinct combination of values for the specified properties will originate a distinct copy of the page in the cache.
VaryByCustomPage, User controlA semicolon-separated list of strings that lets you maintain distinct cached copies of the page based on the browser type or user-defined strings.
VaryByHeaderPageA semicolon-separated list of HTTP headers.
VaryByParamPage, User controlA semicolon-separated list of strings representing query string values sent with GET method attributes, or parameters sent using the POST method.

Note that the VaryByParam attribute is mandatory. If you omit it, a runtime exception is always thrown. However, if you don’t need to vary by parameters, set the attribute to None. The empty string is not an acceptable value for the VaryByParam attribute.

Choosing a Duration for the Page Output

When the output caching service is active on a page, the Duration attribute indicates the number of seconds that the caching system will maintain an HTML-compiled version of the page. Next, requests for the same page, or for an existing parameterized version of the page, will be serviced while bypassing most of the ASP.NET pipeline. As mentioned, this process has two important repercussions—no authentication is possible and no code is run, meaning that no page events are fired and handled and no state is updated. The implementation of output caching varies with the ASP.NET process model in use. 

With the IIS 5.0 process model, any request for an ASP.NET page is always handed over to the worker process, assigned to an HttpApplication object, and processed by the pipeline. The ASP.NET pipeline includes an HTTP module named OutputCacheModule that captures two application-level events related to output caching—ResolveRequestCache and UpdateRequestCache. In particular, the module uses the ResolveRequestCache event handler to short-circuit the processing of requests for pages that have been cached. In the end, the request is hooked by the HTTP module and served by returning the copy of the page stored in the cache. When the page is being generated, OutputCacheModule grabs the output of the pages marked with the @OutputCache directive and stores it internally for further use. The output of the page is stored in a private slot of the ASP.NET Cache object. Setting the Duration attribute on a page sets an expiration policy for the HTTP response generated by the ASP.NET runtime. The output is cached by the module for exactly the specified number of seconds. In the meantime, all the incoming requests that hit one of the cached pages are serviced by the module rather than by the ASP.NET pipeline.

With the IIS 6.0 process model, the output caching mechanism is integrated in the Web server, resulting in much better performance and responsiveness, thanks to the IIS 6.0 kernel caching. When enabled, this feature makes it possible for IIS to intercept the output of a page generated by ASP.NET. A copy of the page output is then cached by the IIS kernel. Incoming requests for an ASP.NET page are filtered by a kernel-level driver (http.sys) and examined to see whether they match cached pages. If so, the output is served to callers directly from kernel-level code without even bothering the worker process and the ASP.NET pipeline. If you have any ASP.NET applications and are still considering an upgrade to IIS 6.0, this is a great reason to do it as soon as possible. Note that this facility in IIS 6.0 is used by ASP.NET since version 1.1 to host the output cache. So, when using the output cache directive in ASP.NET 1.1 and ASP.NET 2.0 (or newer) applications, your responses are being served from the kernel cache. See the sidebar “Inside IIS 6.0 Kernel Caching” for more performance details.

A fair value for the Duration attribute depends on the application, but it normally doesn’t exceed 60 seconds. This value usually works great, especially if the page doesn’t need to be updated frequently. A short duration (say, 1 second) can be useful for applications that claim live data all the time.

Inside IIS 6.0 Kernel Caching

IIS 6.0 employs an ad hoc component to cache the dynamically generated response to a request in the kernel. This feature has tremendous potential and can dramatically improve the performance of a Web server, as long as enough of the content being served is cacheable. What’s the performance gain you can get?

According to the numbers provided with the IIS 6.0 technical documentation, an application using the kernel cache returns a throughput of over ten times the throughput you would get in the noncached case. Additionally, the latency of responses is dramatically better. The following table compares caching in IIS 6.0 kernel mode and user-mode caching to caching as implemented by the ASP.NET runtime in IIS 5.0. (Note that TTFB stands for “time to first byte” while TTLB stands for “time to last byte” for serving the page in question.)

TTFB/TTLB (msec)70.82/70.973.39/4.02
User Mode CPU %76.57%0.78%
Kernel Mode CPU %22.69%99.22%
System Calls/Sec20,1102,101
Network Util (KB/Sec)6,15368,326
Context Switches/Sec2,6216,261

The numbers in the preceding table provide you with an idea of the results, but the results will vary according to the amount of work and size of the response. The bottom line, though, is that leveraging the kernel cache can make a dramatic difference in the performance of an application. The great news for ASP.NET developers is that no code changes are required to benefit from kernel caching, except for the @OutputCache directive.

On a high-volume Web site, an output cache duration of only one second can make a significant difference for the overall throughput of a Web server. There’s more to know about kernel caching, though. First and foremost, kernel caching is available only for pages requested through a GET verb. No kernel caching is possible on postbacks.

Furthermore, pages with VaryByParam and VaryByHeader attributes set are also not stored in the kernel cache. Finally, note that ASP.NET Request/Cache performance counters will not be updated for pages served by the kernel cache.

Choosing a Location for the Page Output

The output cache can be located in various places, either on the client that originated the request or the server. It can also be located on an intermediate proxy server. The various options are listed in Table 2. They come from the OutputCacheLocation enumerated type.

Table 2. Output Cache Locations
AnyPublicThe HTTP header Expires is set according to the duration set in the @OutputCache directive. A new item is placed in the ASP.NET Cache object representing the output of the page.
ClientPrivateThe output cache is located on the browser where the request originated. The HTTP header Expires is set according to the duration set in the @OutputCache directive. No item is created in the ASP.NET Cache object.
DownStreamPublicThe output cache can be stored in any HTTP cache capable devices other than the origin server. This includes proxy servers and the client that made the request. The HTTP header Expires is set according to the duration set in the @OutputCache directive. No item is created in the ASP.NET Cache object.
NoneNo-CacheThe HTTP header Expires is not defined. The Pragma header is set to No-Cache. No item is created in the ASP.NET Cache object.
ServerNo-CacheThe HTTP header Expires is not defined. The Pragma header is set to No-Cache. A new item is placed in the ASP.NET Cache object to represent the output of the page.
ServerAndClient The data can be stored at the origin server (creating an item in the ASP.NET Cache) or on the receiving client.

A page marked with the @OutputCache directive also generates a set of HTTP headers, such as Expires and Cache-Control. Downstream proxy servers such as Microsoft ISA Server understand these headers and cache the page along the way. In this way, for the duration of the output, requests for the page can be satisfied even without reaching the native Web server.

In particular, the Expires HTTP header is used to specify the time when a particular page on the server should be updated. Until that time, any new request the browser receives for the resource is served using the local, client-side cache and no server round-trip is ever made. When specified and not set to No-Cache, the Cache-Control HTTP header typically takes values such as public or private. A value of public means that both the browser and the proxy servers can cache the page. A value of private prevents proxy servers from caching the page; only the browser will cache the page. The Cache-Control is part of the HTTP 1.1 specification and is supported only by Internet Explorer 5.5 and higher.

If you look at the HTTP headers generated by ASP.NET when output caching is enabled, you’ll notice that sometimes the Pragma header is used—in particular, when the location is set to Server. In this case, the header is assigned a value of No-Cache, meaning that client-side caching is totally disabled both on the browser side and the proxy side. As a result, any access to the page is resolved through a connection.


To be precise, the Pragma header set to No-Cache disables caching only over HTTPS channels. If used over nonsecure channels, the page is actually cached but marked as expired.

Let’s examine the client and Web server caching configuration when each of the feasible locations is used.

  • Any This is the default option. This setting means that the page can be cached everywhere, including in the browser, the server, and any proxies along the way. The Expires header is set to the page’s absolute expiration time as determined by the Duration attribute; the Cache-Control header is set to public, meaning that the proxies can cache if they want and need to. On the Web server, a new item is placed in the Cache object with the HTML output of the page. In summary, with this option the page output is cached everywhere. As a result, if the page is accessed through the browser before it expires, no round-trip is ever made. If, in the same timeframe, the page is refreshed—meaning that server-side access is made anyway—the overhead is minimal, the request is short-circuited by the output cache module, and no full request processing takes place.

  • Client The page is cached only by the browser because the Cache-Control header is set to private. Neither proxies nor ASP.NET stores a copy of it. The Expires header is set according to the value of the Duration attribute.

  • DownStream The page can be cached both on the client and in memory of any intermediate proxy. The Expires header is set according to the value of the Duration attribute, and no copy of the page output is maintained by ASP.NET.

  • None Page output caching is disabled both on the server and on the client. No Expires HTTP header is generated, and both the Cache-Control and the Pragma headers are set to No-Cache.

  • Server The page output is exclusively cached on the server, and its raw response is stored in the Cache object. The client-side caching is disabled. No Expires header is created, and both the Cache-Control and Pragma headers are set to No-Cache.

  • ServerAndClient The output cache can be stored only at the origin server or at the requesting client. Proxy servers are not allowed to cache the response.

Adding a Database Dependency to Page Output

The SqlDependency attribute is the @OutputCache directive’s interface to the SqlCacheDependency class that we discussed earlier. When the SqlDependency attribute is set to a Database:Table string, a SQL Server cache dependency object is created. When the dependency is broken, the page output is invalidated and the next request will be served by pushing the request through the pipeline as usual. The output generated will be cached again.

<% @OutputCache Duration="15" VaryByParam="none"
                SqlDependency="Northwind:Employees" %>

A page that contains this code snippet has its output cached for 15 seconds or until a record changes in the Employees table in the Northwind database. Note that the Northwind string here is not the name of a database—it’s the name of an entry in the <databases> section of the configuration file. That entry contains detailed information about the connection string to use to reach the database. You can specify multiple dependencies by separating multiple Database:Table pairs with a semicolon in the value of the SqlDependency attribute.


A user control is made cacheable in either of two ways: by using the @OutputCache directive, or by defining the PartialCaching attribute on the user control’s class declaration in the code-behind file, as follows:

public partial class CustomersGrid : UserControl {

The PartialCaching attribute allows you to specify the duration and values for the VaryByParam, VaryByControl, and VaryByCustom parameters.

Top 10
Review : Sigma 24mm f/1.4 DG HSM Art
Review : Canon EF11-24mm f/4L USM
Review : Creative Sound Blaster Roar 2
Review : Philips Fidelio M2L
Review : Alienware 17 - Dell's Alienware laptops
Review Smartwatch : Wellograph
Review : Xiaomi Redmi 2
Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
Popular Tags
Video Tutorail Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Exchange Server Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe Photoshop CorelDRAW X5 CorelDraw 10 windows Phone 7 windows Phone 8 Iphone