We just discovered at Plaxo that redirecting using meta-refresh tags has a surprising performance penalty as a side-effect: it causes all cached resources on the redirected-to page to be re-requested (as if the user had hit the “refresh” button). Even though most of them should return 304s (if you’re caching them properly), this still results in a ton of extra requests and round-trips. A good workaround is to replace the meta-refresh tags with JavaScript redirects.

A bit more detail

A standard technique for redirecting users from one web page to another is to put a “meta refresh” tag in the first page that says to immediately redirect to the other page, e.g.

<html>
<head>
<title>Redirecting...</title>
<meta http-equiv=refresh content="0; url=http://new-page" />
...

When browsers see this, they go to the URL specified, and if the time specified before redirecting is (the “0;” in the above example), they’re even smart enough to remove the redirecting page from the browser history so when you press Back you don’t get re-redirected.

However, there is a side effect that none of us knew about or expected. We only discovered it while performance-tuning Plaxo Online 3.0 (coming real soon now!!) while using HTTPAnalyzer. For some people, after the initial visit to the site, on return visits none of the images would be re-requested (we send long cache expiration times, so this is good behavior). But for others, they’d be requested each time they went to the page.

The difference turned out to be that some people were accessing the site by a slightly different URL that uses a meta-refresh tag to go to the actual site. Since “http-equiv=refresh” means “treat this as if I’d sent the equivalent HTTP header ‘refresh'”, the browser (especially IE) acts as if the user had hit the reload button and re-requests all cached images on the redirected-to page with If-Modified-Since and If-None-Match headers. If you’ve got the right cache headers, these requests will all return 304 (i.e. the images won’t actually be re-downloaded), but it still results in a big–and unnecessary/unintended–performance hit because you’re now sending a bunch of extra round-trip requests while loading the page.

The ideal solution for redirecting is to send a 302 redirect response from the web server itself to avoid even loading the intermediate web page. However, there are times when this isn’t feasible, e.g. if you don’t have that level of control over the server or if your template system wants to only do this if certain variables are set. Another case is if you want to redirect from an HTTPS page to an HTTP page–if you try to do this on the server, you’ll get a browser warning about redirecting from a secure page to an insecure page, but if you do it with meta-refresh, it works fine (bravo, browser security dudes, heh). So in these cases you want to redirect client-side, but you don’t want to incur the side-effect of re-fetching all the cached resources.

A good solution is to use JavaScript (while some web developers like to degrade gracefully when JavaScript is disabled, we’re redirecting to a rich Ajax app, so this isn’t really an issue). Wherever you’d use a meta-refresh tag, instead insert the following script block:

<script type="text/javascript">
location.replace('http://real-page');
</script>

By using location.replace (instead of, say, location.href=), the browser will purge the redirecting page from the browser history, just like a meta-refresh tag with 0 wait time, which is a good thing. And you won’t get any bad caching side effects, which is also a good thing.

Thanks to the two Ryans for figuring this out! Now you know too. :)

Liked this post? Follow this blog to get more.