Blog

Taking the 404 further

Update: I have now written a WordPress plugin that does all of this for you. Please use this plugin instead.

I’ve just changed my permalink structure for my blog to something a bit prettier. In the process, I realised that some previously-working permalinks weren’t operating any more, despite having a plugin set up to maintain old permalinks.

WordPress is fairly good at figuring out what viewers are requesting when a post can’t be found immediately – for example, if you’re using a permalink structure with an ID number in it, and the requested ID is incorrect, WordPress seems to be able to redirect to the correct address.

However, it’s not 100%, as I recently realised.

Consequently, a few pages were heading to the 404 page, which isn’t ideal. I changed my template’s 404 page to do a search for what the viewer was really after, and redirect them there. If it can’t find an exact match, it’ll perform a search with keywords extracted from the URL. If it finds a single result, it’ll redirect, otherwise it’ll put up a few results as suggestions on the 404 page.

It also works as a nice search shortcut. Try it: http://atastypixel.com/blog/wordpress 404 redirect

It’s all done in the 404 handler in the WordPress theme (wp-content/themes/themename/404.php). It should probably be a plugin, and maybe I’ll make it one some time. For now, put this code above the get_header() in your 404.php:

<?php

global $wp_the_query;

// Construct search term

$search = preg_replace(array(“@[_-]@”, “@\[email protected]),

array(” “, “”),

urldecode(basename($_SERVER[“REQUEST_URI”])));

// Search for posts with exact name, redirect if one found

$posts = $wp_the_query->query(array(“name” => $search));

if ( count($posts) == 1 ) {

wp_redirect(get_permalink($posts[0]->ID), 301);

exit();

}

// Do a general search, redirect if exactly one result

$posts = $wp_the_query->query(array(“s” => $search));

if ( count($posts) == 1 ) {

wp_redirect(get_permalink($posts[0]->ID), 301);

exit();

}

?>

And then if you want to list some suggestions as well, use this code to do so, somewhere after the above code:

<?php if (count($posts) > 0) : ?>

Or, try one of these links:

<p>

<?php foreach ( $posts as $post ) : ?>

<a href=“<?php echo get_permalink($post->ID); ?>”><?php echo $post->post_title; ?></a><br/>

<?php endforeach; ?>

</p>

<?php endif; ?>

, , . Bookmark the permalink. Both comments and trackbacks are currently closed.