Adam Prescott

Detecting redirects with the Navigation Timing API

When pages on a website get moved, if the old URLs don’t redirect to new ones then there’ll be a lot of dead links pointing to broken content. Set up proper redirects, and you’ve covered. As an added bonus, what about going through thousands and thousands of links to change any old internal links so you cut down on redirection times? Yeah, I wouldn’t either. But if you did want to…

To work out which URLs are linking where, you could jump straight to parsing web server access logs, but that’s too obvious to be fun.

HTML5 introduced a Navigation Timing API which provides access to performance-related navigation metrics. With it, you can measure the time taken to setup a connection with the server, or how long a request spent on a DNS lookup. Useful stuff, and Google makes use of this same data for its Analytics site speed timings and reports.

Navigation Timing isn’t the only timing API available, in fact. There is also a Resource Timing specification, which is worth a mention if only for its potential privacy implications.

Anyhow, one of the metrics provided by the Navigation Timing API is the time taken when following redirects:

If there are HTTP redirects or equivalent when navigating and all redirects and equivalents are from the same origin, this attribute must return the time immediately after receiving the last byte of the response of the last redirect. Otherwise, this attribute must return zero.

Not only can you get the time taken, but you can see how many redirects there were, with redirectCount:

This attribute must return the number of redirects since the last non-redirect navigation under the current browsing context. If there is no redirect or there is any redirect that is not from the same origin as the destination document, this attribute must return zero.

Obvious, right? Grab the redirectCount value and, say, send off an asynchronous browser request to something that’ll log the old and new URL.

vesper.js

Vesper is so simple that it only took an evening to write. It does everything just outlined:

<script src="vesper.js"></script>
<script>
Vesper.configure({
  onRedirected: function(redirectSource, redirectTarget, originatingPage) {
    console.log("redirect from " + redirectSource + " to " + redirectTarget);
    console.log("redirect found on " + originatingPage);
  }
});
</script>

Implementing this is perhaps not as obvious as you might think. A redirect response is followed immediately by the browser, with no JavaScript execution at all, let alone a browser event to hook into. On a redirect, the referrer in document.referrer is the previous page you were on, not the URL you were originally requesting. There is no API to look at HTTP headers and responses, probably for a very good reason.

Because of these limitations, HTML5’s sessionStorage is used. Session storage is a lot like local storage, except values are scoped to a page session and are not persisted except for as long as the tab is open:

This is a global object (sessionStorage) that maintains a storage area that’s available for the duration of the page session. A page session lasts for as long as the browser is open and survives over page reloads and restores. Opening a page in a new tab or window will cause a new session to be initiated.

Session storage makes more sense than local storage in this case, because we don’t really care about persisting the data.

Thanks to Peter Cooper for the name Vesper, otherwise I would’ve probably spent longer naming the thing than I did writing it. He also happens to run a HTML5 Weekly newsletter, so if you’re curious about HTML5 happenings with new APIs like this, you know where to go for more!