WordPress Ate My Redirects!

Technology Blog

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:

Redirect fix plugin

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!

5 Responses to “WordPress Ate My Redirects!”

  1. Binh

    Hi, I notice this problem and am looking for a solution. Your download file is not working. I’m using WP 2.3.1 The 302 is annoying me too much because it’s keeping the ugly /?p=x addresses in the Google index. If you have some free time I wish to hear from you. Thanks.

  2. Binh Nguyen

    Thanks for uploading the file. The problem still remain after I activated the plugin. Worse thing is that I found out all the none trailing dash and all the trackbacks are using 302. Which may describe why many of my posts are at PR0 or PR N/A.

    I hope this can be fixed soon. Maybe we should change that
    function wp_redirect($location, $status = 302)
    into
    function wp_redirect($location, $status = 301)

    Will such change cause any problems, at all?

  3. Alfred Armstrong

    Well spotted! I never thought of testing the trackback links and they were indeed issuing a 302. I’ve change to a default of 301 and it now works. I don’t know why the author of that WordPress code thougt a 302 was warranted.

    So hopefully that will now work for you. If not, is it possible that another plugin is installing a version of that function ahead of mine?

  4. Peter

    How can I include this into a theme’s functions.php? I pasted in the code including the if statement but it didn’t work. I then removed the if statement and then my wordpress install gave me an error about not being able to redeclare wp_redirect

    Thoughts?

Leave a Reply

  • (will not be published)

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>