[ Team LiB ] Previous Section Next Section

17.6 Preventing Caching of JSP Pages

A browser can cache web pages so that it doesn't have to get them from the server every time the user asks for them. Proxy servers can also cache pages that are frequently requested by all users going through the proxy. Caching helps cut down the network traffic and server load, and provides the user with faster responses. But caching can also cause problems in a web application in which you really want the user to see the latest version of a dynamically generated page.

Both browsers and proxy servers can be told not to cache a page by setting response headers. You can use a scriptlet like this in your JSP pages to set these headers:

<%
  response.addHeader("Pragma", "no-cache");
  response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
  response.addHeader("Cache-Control", "pre-check=0, post-check=0");
  response.setDateHeader("Expires", 0);
%>

An alternative is to use a custom action that's included with the book examples:

<%@ taglib uri="orataglib" prefix="ora" %>
<ora:noCache/>

The <ora:noCache> action sets the exact same headers as the scriptlet example, but it's cleaner.

The reason so many headers are needed is that different browsers and proxies respect different headers. The Pragma header is intended for old HTTP/1.0 clients. According to the HTTP/1. 0 specification, this header is really a request header but proxies and some browsers are known to respect it even when it's used as a response header. The Cache-Control header is an HTTP/1.1 header, so older browsers may not recognize it. The no-cache and no-store values mean that the client is not allowed to save a local copy of the page, and the must-revalidate that it must always ask the server if a new version is available. The second set of Cache-Control values are "extensions" supported by Internet Explorer, with basically the same meaning as the standard values used in the first set. The Expires header, finally, is defined by the HTTP/1.0 specification, so all browsers should recognize it. The value is the date and time when the response is no longer valid. The setDateHeader( ) method converts the value 0 to Thu, 1 Dec 1970 00:00:00 GMT; in other words, a date way in the past to ensure that the client gets a new copy every time this page is requested.

There's a subtle difference between telling the browser that the response has already expired and telling it not to cache the response. According to the HTTP/1.1 specification, if you say only that the response has expired, the browser is supposed to show a cached copy when the user uses the Back button or selects the page from the history list. It should ask the server for a new version only when the user makes an explicit request for the page by clicking a link, submitting a form, or typing the URL in the address field. However, if you tell the browser not to cache the response, it's not allowed to ever use a cached copy, not even for a Back button or history list selection. This is the safest model for responses that include sensitive information intended only for an authorized user, but it may not be the right choice for all responses.

By including or excluding the Pragma and Cache-Control headers, you can get the behavior that is appropriate for your specific application. In theory, that is. Unfortunately, browsers don't always behave as they should. Most (if not all) versions of Netscape and Mozilla, for instance, don't cache a response that has expired so excluding the cache headers makes no difference. Some versions of Internet Explorer are infamous for ignoring the cache headers, forcing you to use <http-equiv> elements in the page instead of (or in addition to) setting the headers to avoid a response to be cached. For more on the Internet Explorer problems, see http://support.microsoft.com/default.aspx?scid=kb;EN-US;q222064. Finally, some proxy and caching servers ignore all of this. An ugly but effective workaround in this case is to generate unique URLs for all application pages by including a query string parameter with a new value for each request, for instance using a counter like this:

<c:set var="${counter + 1}" scope="application" />
<a href="mypage.jsp?nocache=${counter}">
  This page is never cached</a>

As you can see, in the real world it can be hard to get this right. I recommend that you set the appropriate headers for the behavior you want first, assuming that the browsers and proxies used by the web application users are specification-compliant. Then test with the set of browser versions you want to support, using all the caching options each browser supports. Revert to <http-equiv> elements or the unique query string solution only if nothing else works.

    [ Team LiB ] Previous Section Next Section