A Better 404

NOTICE: This article is deprecated. While the article is still valid it no longer accurately describes the current 404-handler used on this website. Please also read the later article, A Better 404 - Redux, which contains updated information.

After reading Ian Lloyd’s The Perfect 404, I’ve revamped my “Not Found” error page to be a little bit more useful because, as Lloyd writes, “a user-friendly website will give you a helping hand.” Previously my 404 page simply stated that an error had occured and offered links to the home-page and archives. I knew I could do better than that!

The simplest change I made was to remove the “404” error code. After all, while you and I understand what this means, my next-door neighbour wouldn’t. To Joe the Plumber this error code is simply redundant information.

I wanted to try to explain why the reader had ended up on the 404 page. So my body text begins with something like:

Sorry, but /[url_fragment]/ doesn’t exist on the Urban Mainframe at this time.

It seemed polite to explain that the error could either be mine (I might have changed a URL or removed a resource) or it could be that a referring link is incorrect. So I needed to determine whether or not there was a referring link and, if there was, I could then display an appropriate message.

The link at http://some-domain.com/bad_referrer/ is incorrect or /[url_fragment]/ has been moved, renamed or deleted.

A snippet of PHP code in my WordPress’ 404 template serviced this requirement:
echo "<p>The link at <em><a href=\"" . $_SERVER['HTTP_REFERER'] . "\">" . $_SERVER['HTTP_REFERER'] . "</a></em> is incorrect or <em>" . $_SERVER['REQUEST_URI'] . "</em> has been moved, renamed or deleted.</p>";

Then I present the reader with a couple of options by providing links to the home-page, the archives and, via Matt Mullenweg’s Random Redirect WordPress plug-in, a link to a random page on the website - just for a bit of fun.

The next bit’s really neat — using a (slightly modified) script I found, WordPress breaks the erroneous URL into its component parts (ditching the domain fragment) and uses those parts as keywords for a full-text search of all my published pages and posts. It then presents the reader with a list of page titles that might be what they were looking for in the first place. Granted, it’s a long shot, but they just might get lucky and, even if they don’t, it exposes them to a selection of my content that they might otherwise have missed.

Updated, 4th November 2008: You may not have full-text indexing configured in your WordPress database. In order for the full-text search to work you’ll first need to issue the following command to MySQL:

ALTER TABLE `wp_posts` ADD FULLTEXT `post_related` (`post_name` ,`post_content`);

To draw the reader’s attention to even more content, I also display a list of my most recent posts with another PHP code snippet:

<h3>Recent Posts</h3>
<ol style="margin-left: 40px;">
<?php $postslist = get_posts('numberposts=5');
foreach ($postslist as $post) :
setup_postdata($post); ?>
<li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
<?php endforeach; ?>

The options end with a link to my contact form so that the reader can send me an email if further help is required.

Bonus: While I was putting this together, I tried various combinations of URL in order to test the error handler and I discovered something rather neat in the process. If I enter a URL that doesn’t directly match one that’s valid on the Urban Mainframe but that’s similar to another that’s unique, then WordPress automatically redirects to the other rather than respond with a 404. For example, if I enter the non-existant URL http://blog.urbanmainframe.com/insight/ then WordPress automatically redirects to http://blog.urbanmainframe.com/2008/08/insight/ — that’s pretty cool!

It goes without saying that I want as few visitors as possible to see my 404 page but, for those unfortunate souls who do arrive there, I hope that the options available will help them on their way and hopefully keep them on my website longer than they would otherwise have stayed.

What do you think? Is there anything I can do to improve the page further? What’s on your 404 page? Let me know in the comments.

12 Reader Comments for “A Better 404”

  1. What a good idea. Mine are probably the typical Apache bland-o-rama 404’s. If I have time tomorrow, I think I’ll try this out myself. Thanks mate.

  2. Aye that’s me Kevin - making the Web a better place, it’s my mission in life. ;-)

  3. great idea this could be a easier resolution to be fair…i think i may have to give this a whirl thanks

  4. Glad to be of service Suzanne.

  5. You’re on to something, but I’d improve the request_uri part like so:

    The link at ” . $_SERVER[‘HTTP_REFERER’] . “ is incorrect or ” . $_SERVER[‘REQUEST_URI’] . “ has been moved, renamed or deleted.
    <?php } else {

    echo “” . $_SERVER[‘REQUEST_URI’];
    has been moved, renamed or deleted.

  6. Thanks for your comment James. I have in fact already got a conditional on the referrer variable. If there is a referrer then the output text is written accordingly. If there is no referrer then no mention of it is made on the 404 page.

    Is that what you meant or have I misunderstood?

  7. Ouch my comment really did get mangled there, I’ll put it up on my own blog with some other things I’ve run into.

    I meant to put a bit of code in that built upon your code, where if there is a referrer, then that output text is written, and if there isn’t, then you get a message where it says: “/requested/uri has been moved, etc. Please try again or try a search.” That should help people coming in from bookmarks or locationbar history.

    Wouldn’t it also be handy if the request_uri could be used to dish up some possibly related posts?

  8. But that’s exactly what does happen James!

    If there is no referrer then the message simply says that the page might never have existed or it’s been moved, deleted, etc.

    If there is a referrer then that referrer is listed along with a message saying that the referring link is incorrect.

    There’s also a little magic that goes into the “Possible Match(es)” results. If there is no referrer then the requested URI is broken down into it’s component parts and everything after the domain name is then fed into the search engine.

    If there is a referrer and that referrer is a search engine, then the query that was submitted to the external search engine is then fed into mine to automatically produce a local search. The results appear as “Possible Match(es)”.

    I’d like this page to be as helpful and informative as possible and I think I’m getting there. However, I’ll await your post with interest so as I can properly understand what you’re suggesting. If it’s good, I’ll do my utmost to implement it here.

    Thanks for your contribution.

    P.S. I’m sorry your original comment got mangled. It seems that WordPress’ comment handler doesn’t like code snippets! :-(

  9. I must apologise, I hadn’t actually taken the time to look at your 404 page itself! It does indeed do everything it should, and I was wondering if you would consider sharing your code which breaks down the URI for the search engine. Me, and I’m sure other readers too, would be very grateful.

    Second try at the code: —  —  —  —  — -
    // When there is a refer
    if ($_SERVER[‘HTTP_REFERER’]) { ?)
    (p)The link at (em)(a href="” . $_SERVER[‘HTTP_REFERER’] . “")” . $_SERVER[‘HTTP_REFERER’] . “(/a)(/em) is incorrect or (em)” . $_SERVER[‘REQUEST_URI’] . “(/em) has been moved, renamed or deleted.(/p)
    (?php }
    // When there’s no refer
    else {
    echo “(p)(em)” . $_SERVER[‘REQUEST_URI’];
    (/em) has been moved, renamed or deleted.(/p)
    (?php } ?) —  —  —  — -
    All tags changed to have (’s and )’s…for posting-comment-purposes. I also seperated the html from the php as much as made sense (supposed to be better and not kill kittens or something - it made sense when the dev people told me).

    I suppose you have something like this on your page, instead of the code you have in the article.

    Other things which are sometimes needed are a: — -
    # Customized error messages.
    ErrorDocument 404 /index.php?error=404 — -
    In your .htaccess

    And a proper http-status message in your 404.php in your wordpress theme folder: — (?php ob_start(); ?)
    (?php header(“HTTP/1.1 404 Not Found”); ?) — Hope that works!

  10. Thanks for that James. I will revisit this soon with a new article that includes all the 404 code that I’m currently using. I also feel a possible mod_rewrite article coming on - there’s a few custom rewrites on this site to handle the edge cases and to deal with some of the URIs from the old website which still get a lot of traffic.

    Thanks again for everything you’ve contributed to this thread. I appreciate your feedback.

    Happy New Year!!! :-)

  11. Happy New Year!

    I’ll link to you then for that magic, saves me typing!

  12. (Fix: I left an error in my code here, the correct code can be found here: http://james.gameover.com/index.php/2009/improve-your-wordpress-the-404-error-page/ )

    Also, it’s nice to cut the http-referer-as-link-text up in chunks like so: chunk_split($_SERVER[‘HTTP_REFERER’], 25). This makes lines wrap properly.

Leave a Comment