In my previous post about search engine optimisation of WordPress I described how I was using the Permalink Redirect plugin to ensure all the URLs at this site are correctly slash-terminated. Today however I was using rebuilding my Google site map using GSiteCrawler when I was dismayed to notice that all the “wrong” URLs were still appearing in the list. How could that be? The answer took some finding (and it’s all rather technical), but I think it’s worth mentioning as others may unawares have the same problem, which causes the plugin to be defeated even though it seems to be working.
The first thing I did was to use Live HTTP Headers to see what was being sent back by WordPress. The redirection was there as expected, except that the status code being sent was 302 instead of 301.
It’s worth explaining the difference between a 302 and a 301 redirect, to show why the latter rather than the former is to be desired for SEO purposes. A 301 redirect is a permanent redirection: it is equivalent to saying “gone away”. Anyone receiving a 301 redirect is effectively requested to stop using the address they asked for and use the new one sent back by the server instead. So the Google spider, when it receives a 301, ignores the original link and indexes the new one. A 302 on the other hand is a temporary redirection, somewhat like having a phone call forwarded when you are away from your desk. It implies that the original redirected URL is still good, and the Google spider will therefore happily add it to its index.
So why was a 302 be sent instead of a 301? I had a quick look through the code and as far as I could tell the status being set in the relevant section was 301. Slightly puzzled, I referred to the PHP manual, where I found these helpful words:
[A] special case is the “Location:” header. Not only does it send this header back to the browser, but it also returns a REDIRECT (302) status code to the browser unless some 3xx status code has already been set.
So, if for some reason the Status header with 301 in it was not being sent before the Location header, that would explain why a 302 was being output. I looked again at the code, and found these lines in pluggable.php:
if ( php_sapi_name() != 'cgi-fcgi' ) status_header($status); // This causes problems on IIS and some FastCGI setups header("Location: $location");
In other words, on a FastCGI system the header to send the status would be completely (and silently) ignored! Once I commented out the test against php_sapi_name() the redirects started working correctly. Patching the code is not an ideal solution, but fortunately since the problem occurs in a pluggable function there is a better way, which is to provide a custom plugin to override the troublesome function. And here it is:
The plugin has only been tested with the latest version of WordPress, 2.2, and you should install it ONLY if you have the problem described in this post. To install it, download the file, rename it to ‘redirect-fix.php’ and place it in the plugins directory of your WordPress installation. You can then activate it from the Plugins page of your control panel. NOTE: THIS CODE IS SUPPLIED WITHOUT ANY WARRANTY AND YOU INSTALL IT ENTIRELY AT YOUR OWN RISK!