Cache control and redirection with mod_expires and mod_rewrite

mod_expires and mod_rewrite are two indispensable tools in any Apache administrator’s arsenal.  But there is at least one situation where they don’t play too nicely with each other: when you have a global cache policy in place, but you define a conditional redirect that you don’t want to be cacheable.

First, let me give a little background as to how we got into this mess in the first place.  First, like any smart Web publisher, we’re very concerned with our cache policies, and we strive to make as much of the site cacheable as possible.  We put highly cacheable content into directories together so that we can use mod_expires to set cache control headers that allow our site’s visitors to cache that content for days and weeks on end.  Other content that is volatile is grouped into directories (or using filename patterns) so that we can define very tight parameters on the cacheability of it, even making some of it completely uncacheable.  We use a default directive with mod_expires so that anything we didn’t explicitly specify gets a cache window of 5 minutes.

All of this works quite well, but we found one interesting glitch when we set up a conditional redirect.  As many sites do, we perform some basic user agent detection and send mobile devices to a version of the site that is optimized for smaller displays and the limited capabilities of mobile browsers.  Here’s the problem:  because of our cache control headers, proxy servers were caching the redirection response, and they were then sending all of their clients to the mobile version of the site!  Some corporate networks are set up so that Blackberry traffic, for instance, moves through the same caching proxy servers as the desktop traffic.  If a Blackberry hits our server and gets the redirect, the proxy server will cache that response.  It doesn’t know that the response was not intended for all clients.  So desktop users may be surprised to see the mobile version of our site pop up in Firefox or Internet Explorer.

Sadly, there is no easy answer for this problem.  If you have set a default cache policy like this in your Apache config:

any redirects you use will reflect the 5-minute expiration window in their cache control headers, making the redirect fair game for caching.  If you have conditional redirects like this:

then you would get exchanges like this with Apache:

What we opted to do was to do our redirection in two stages to circumvent this mechanism.  First, do an internal redirect to a file that would not be cacheable.

We had hoped to then do a second redirect within mod_rewrite to the mobile site (and avoid the need to even build and host a redirect.php), but it seems that in order for mod_expires to be governed by the <Directory /nocache/> context, the content must actually be generated by a file or script under the /nocache/ directory.  So we really had to implement redirect.php, which is a simple script that just does something like this:

The net result is that the mobile device gets a redirect that looks like this:

Proxy caches won’t cache this redirect, so you don’t have to worry about desktop users getting mobile-formatted content.

If you need to perform a number of such redirections, you could build one redirect.php script that takes a CGI parameter that dictates where it should redirect, rather than maintaining a different redirect.php for each redirect URL.  This example has been simplified for clarity.

Leave a Reply

Your email address will not be published. Required fields are marked *