Submitted by Nik on
Currently there are three options for creating error pages in the Drupal system, that I know of. I’m going to show here which I think is the best, for reasons of usability, performance and general webmaster sanity. At the foot of this article, there’s some free code too!
The options:
* Drupal’s built-in support for error pages (@ /admin/settings/error-reporting)
- Search404.module (404’s only, of course)
- Customerror.module (404 & 403)
h2. Drupal’s build in error page support
Drupal provides, out of the box, two fields in the Error Reporting configuration screen. These fields can be set to any internal Drupal path. Usually, they will be set to point the user to a page created specifically for the purpose.
The downside to this is that these will now be nodes in the system, and as such they will show up in popular content lists, site searches and the like. This is clearly not desirable.
Update: I have been made aware of an outstanding issue in Drupal core with error pages. This issue means that a user without “access content” permissions cannot access 403 error pages that are created as nodes. This is true in Drupal 5.x and even 6.1, and is another weak point for this mechanism.
h2. Search404 module
Until very recently I was using search404 but I became less than pleased with the results. To start with, I thought I was aiding usability, but as it transpires… not really. The real killer for me is that search404 often gives me empty search result sets, because the path elements just don’t relate specifically enough to the content.
For instance, the node “/blog/my-drupal-article” will almost certainly contain all the words “my drupal article”, but may not contain the word “blog”, except in the path. This means the search doesn’t catch that article, so you get no results. Given that every 404 page the module generates incurs a DB query automatically, this query is effectively just trash, but cannot be disabled.
h2. Customerror module
Customerror module skirts round the issues of having nodes as error pages. The module makes error handling pages available as custom paths inside Drupal. These aren’t nodes, so we have no issues there.
The configuration screen offers up two textarea fields which will contain the page content to be rendered on each of the 403 and 404 page errors. The key to making this more special than just a plain text or html page is the availability of PHP processing for these fields whilst not requiring nodes for the task.
Ok, so what I’m doing here is recommending customerror as the best choice for this task. That said, let’s throw down some code and make this more useful.
To start, visit the standard Drupal error reporting page at “/admin/settings/error-reporting”. Here, set the default error page fields to “customerror/403” and “customerror/404” respectively, if you’re going to override both these pages.
Now, on the Custom Error module’s config page at “/admin/settings/customerror”, enable both checkboxes that say “Allow PHP code to be executed for 40x”. Now let’s look at handling the 404 error. I’ve added the following code for this site, in the “Description for 404” textarea, and a suitably snappy title in the other field: “404 Not Found Error: No content found at the requested URL”.
<p>Sorry, no content was found at the requested path - it's possible that you've requested this page in error.</p>
<p>Use the search form below, or go to the <a href="/">home page.</a></p>
<?php
// check that the search module exists and the user has permission to hit the form
if (module_exists('search') && user_access('search content')) {
// cool! - customerror doesn't trash the page request and the full path is available
$path = $_REQUEST['destination'];
// bin anything that's not alphanumeric and replace with spaces
$keys = strtolower(preg_replace('/[^a-zA-Z0-9-]+/', ' ', $path));
// retrieve the search form using the data we've pull from the request
// note that we can override the label for the search terms field here too
print drupal_get_form('search_form', NULL, $keys, 'node', 'Search terms');
}
?>
In the 403 error fields, we adopt a similar technique. I’ve used “403 Forbidden Error: Access to this page is denied” for the title. Here we display different content depending on whether or not the user is logged in. If you’re running a site with lots of members, you can uncomment the user login line towards the bottom and the login form will be rendered on the 403 page!
<?php global $user; ?>
<?php if ($user->uid): ?>
<p>Sorry <?php print $user->name; ?>, you don't have permission to view the page you've just tried to access.</p>
<p>If you feel that you have received this message in error, please
<a href="contact">contact us</a> with specific details so that we may review your access to this web site.</p>
<p>Thanks</p>
<?php else: ?>
<p>This page may be available to clients and registered users only. Please select from one of the other options available to you below.</p>
<ul>
<li><a href="/user/login?<?php print drupal_get_destination(); ?>">Login</a> to view this page</li>
<li>Use the <a href="search">search</a> facility</li>
<li>Go to the <a href="/">home page</a></li>
<li>Go to the <a href="sitemap">site map</a></li>
</ul>
<?php //print drupal_get_form('user_login'); ?>
<?php endif; ?>
Now we’ve got friendly, usable error pages that are helpful and don’t scare off visitors!
Updated 24th April 2008
Comments
Alex replied on Permalink
Great post, i've been using search404 up to now, but Customerror module seems worth checking out!
Thanks
Michael Prasuhn replied on Permalink
So I used modified versions of these snippets on a site I'm working on and they work great. One caveat though...
Since the customerror module uses some custom callbacks to execute its code, and those callbacks aren't in the menu system, breadcrumbs and navigation will possibly change on the 403 snippet. My client noticed that the although the URL was the same, all the highlighted menus and blocks went away. This one line snippet fixed all that:
<?php menu_set_active_item($_REQUEST['destination']); ?>
All it does is trick Drupal into thinking the the requested URL is the page it's on even when a different callback is really handling the request.
Nik replied on Permalink
Mike, that’s a cool trick indeed – I assume there’s no security implications (regarding permissions etc) in using that tweak?
Miichael Prasuhn replied on Permalink
I have found none. To the best of my knowledge it only affects the menu system, particularly highlighting of menu items in the active trail. And while the menu system does control access to many pages, it is still smart enough to do all access checks against $user, which is never changed.
Alan Brookland replied on Permalink
I added the call to the 404 snippet and it stopped any menus from appearing - I just now noticed you were adding it to the 403 snippet not 404 doh!
Broun replied on Permalink
What would i do with errors other than 404 and 403. I have other databases on my website and the codes are in drupal pages. How do i deal with errors of database not found when i issue sql or specific sql errors each having its own type of safe error page.
Thanks
Nik replied on Permalink
As far as I know, Drupal only handles responses for 403 and 404 errors. As such, I think the only way to process other errors is via Apache’s built-in handlers for those.
Malcolm replied on Permalink
Great post/information! Thank you...
Denver Prophit Jr replied on Permalink
http://drupal.org/project/customerror is not even ready for drupal 6.6 what am I to do?
Nik replied on Permalink
I’ve tried out the dev version of the Drupal 6 module, and it seems ok to use, as far as I can tell. Give it a whirl! :)
Bill replied on Permalink
I have implemented this in v6 and I lose all the menus and blocks... If I navigate to www.mysite.com/customerror/403 I get all my nav and blocks and the error page BUT if I let Drupal show the error page, the URL does not rewrite form the erroneous one and I lose all my nav and blocks... I tried that code above to no avail (I pasted it in the code blocks)... Any ideas?
Nik replied on Permalink
Bill, ya know, I’m not sure. I do believe however that there is a known issue (and indeed, one that I don’t think can be fixed) that involves there not being blocks etc available at certain page callbacks, such as the page you mention.
You might want to have a poke round in the issue queue for that module and maybe also that of Search404; I’m sure I’ve seen some reasoning behind it somewhere!
Nik replied on Permalink
Ha, well – I did some more rooting around. Have a look at this thread, which details improvements in the core handling of this issue and workarounds via theme code.
Also, JohnAlbin has released a module which fixes this issue for Drupal version 6. I’ve tested it with my customerror.module setup and everything seems functional & cool! Try this out: http://drupal.org/project/blocks404
[Edited: sorry, the Blocks404 module doesn’t work with Drupal 5]
Jeff Schuler replied on Permalink
Good analysis, Nik, thanks.
Having a db search query on all 404's was a major consideration for me, and something I decided to try, and evaluate.
Providing the search form without performing the search is probably a much better solution -- and an option that could maybe be included in Search 404.
A few other considerations:
Search 404 provides Advanced Settings on its config page where you can make a list of words to ignore -- it includes "and or the" by default, and I've added terms like "blog node content" that show up in my paths, but aren't appropriate for searches.
Another benefit, for me, is that Search 404 allows for easy inclusion of sidebar blocks on the 404/403 pages. (Yes, I've read #129762.) I think the performance hit is worth keeping the blocks there ~ especially since Search 404 apparently only displays cached blocks.
I guess I either want sidebar blocks display in CustomError, or an option to not perform the search in Search 404...
Nik replied on Permalink
Jeff, if you see the comment above, I have sourced a solution for the disappearing block regions in Drupal 5 & 6 for use in combination with customerror.module.
I do see the benefit to the extra bits provided by Search404 but there’s no way I’m taking the DB hit for that!
Maybe you’d also like to evaluate this new solution? Hope that helps!
Jeff Schuler replied on Permalink
Sorry Nik, I should've read the comments more thoroughly.
You say Blocks404 works for D5 and D6, but there's only a D6 version on the project page. Unless you know something I don't, I'll look into backporting when I have a chance...
Nik replied on Permalink
Jeff – two things, for both of which I must apologise! Firstly, the thread I read said the fix would work for Drupal 5 & 6, but you’re right, the module is only D6. Sorry.
Secondly, I didn’t answer your comment until just after I’d posted the above comments, so you didn’t miss them; they weren’t even there at the time! :)
Check out the thread in the comment I mentioned, I believe that has code fixes for previous versions – hope that helps.
Peter Christopher replied on Permalink
Whew, backbreaking. I finally got everything working according to your instructions with d6. I did some modifications to the 404 page. (I used customerror and 404blocks as suggested btw). But I still wasn't able to get the "bad url" to go into the google CSE results box. Ideally I wanted it to go in there with the results also already appearing. Maybe next time I can figure it out, or if anyone else knows how to do it please let me know. http://philippines-living.com/bar3 and http://central-america-forum.com/bar3 are URLs to nonexistent files on the two sites I serve off that d6 codebase if you want to take a look. Thanks for providing your description and tips here.
Nik replied on Permalink
To be fair Christopher, I’ve not tried (nor given any indication) of getting this module or any of the associated snippets/workarounds/modules to work in conjunction with a Google Custom Search Engine.
The info is provided as-is for just the stock Drupal 6 (and 5 where mentioned) – perhaps you’d have more luck with a CSE expert? Sorry I can’t be more helpful! That said, you could perhaps achieve your desired result with some JQuery – I don’t think it’s a total loss that you can’t get this working though, you’ve provided two search boxes after all!
smitty replied on Permalink
All this works great for me (Drupal 5.15). Thanks for this advice.
The only problem is that I get ugly html-errors because of duplicate Ids by using:
print drupal_get_form('search_form', NULL, $keys, 'node', 'Search terms');
in my 403/404 pages. Is there anything I can do to avoid this?
Nik replied on Permalink
Well, I checked in my own theme, where I don’t have this problem – it seems that in D5 I got round it by theming the search form to use a different ID every time. At least, that’s how it appears; I’m running D6 now and to be honest I don’t have a legacy copy of the theme any more, so it’s rather hard to check!
I’m certain that if you search around the Drupal site you can find something on the duplicate form ID issue though – I definitely found something there that helped me fix that! Sorry I can’t be more helpful…
Anonymous replied on Permalink
Thanks for the advice. I switched from Search404.
Anonymous replied on Permalink
Awesome tutorial, definitely saved me an hour or 2.
THANK YOU!
Jeff in Seattle replied on Permalink
Hi,
First, I am using Drupal 6.x and I have loaded into customerror module 6.x within /drupal/sites/all/modules, however I am not seeing Custom Error module’s config page at “/admin/settings/customerror”.
I am seeing "/admin/settings" instead.
What could be wrong with my setup?
Second, the two code samples in this document's section "Customerror module", where are they applied?
Nik replied on Permalink
Jeff, it sounds like you haven’t actually installed the module. Visit /admin/build/modules and check off the customerror module. Secondly, the application occurs at the page I directed you to. You shouldn’t have any problems once the module is correctly installed.
Alan Brookland replied on Permalink
Thanks for this I think it adds a lot to the useability of our site and it saved me some time figuring it out myself.
However I cam across a problem:
While logged out I went to /admin and got the 403 page in the admin theme (Garland), I entered correct username/password into logon box in the left hand column and got sent to 403 page again but now in the custom front end theme.
If I clicked on the log in link on the 403 page and entered correct username/password I was logged in and redirected to /admin.
I had copy'n'pasted the 403 snippet exactly as above and I fixed it by adding in the line:
<?php menu_set_active_item($_REQUEST['destination']); ?>
as suggested by Michael Prasuhn above (not that I thought it would help but I wanted that functionality but had mistakenly read his comment as applying to 404's not 403's and had just noticed and changed it).
Anonymous replied on Permalink
Thanks a lot. I've had the same experience with Search404. I will try Cutomerror, maybe adding the google code for 404 pages.
Cool article. Thanks
Jeff Schuler replied on Permalink
This is an excellent resource, Nik, and I'm generally using this approach, now, in lieu of Search 404.
I wonder if you might prepend appropriate links in your examples with <?php print $GLOBALS['base_path']; ?> so that they work on sites that aren't at the root dir of a domain... Might help folks that are copy-pasting.
Thanks again!
Nik replied on Permalink
You’ve a point there Jeff, but I shan’t bother because I think it’s niche and because you’ve both illustrated the problem and presented a solution here! :)
Also, I’m not sure that that’s the recommended Drupal way of getting that variable? There’s a $basepath variable, a basepath() function and in light of various other things, it’s still possible that either of these might not fix your particular use case(?)
Peter replied on Permalink
Many thanks for an excellent solution!
Davina replied on Permalink
thankyou, thankyou, thankyou!!
Mark replied on Permalink
I absolutely agree with you on this. Search404 is great conceptually, but produces a LOT of overhead, and this solution is an elegant alternative. It's funny that Search404 is catching on so much--it's even mentioned as a 'to do' in the 'SEOchecklist' module. I think in most cases it's a module that should just be skipped.
snorkers replied on Permalink
Any idea how to pre-populate just a search box (rather than the whole form)?
I blindly used:
print drupal_get_form('search_box', NULL, $keys, 'node', 'Search terms');
Which gave me the text: 'Search this site', a text input box and a 'Search' input button - which is dead easy to theme...
But I've no idea how the form API works, or what arguments I should really be passing. I've looked at api.drupal.org and it's left me none the wiser. Grateful for any help
Ian Hoar replied on Permalink
Wow thanks, this post and thread were very valuable. I finally have a witty 404, search, and my site frame.
Robert replied on Permalink
Great tutorial, thanks a lot
Anonymous replied on Permalink
Thank you, this post was really helpful.
Bas Vredeling replied on Permalink
I've created a sandbox Drupal module which was inspired by this post: http://drupal.org/sandbox/basvredeling/1422496
Nik replied on Permalink
That’s a real compliment, thanks Bas! :) I’ll be sure to check it out.
Wolfflow replied on Permalink
Many thanks. Great and simple post to implement successfully for Drupal newbei also.
Cheers
Add new comment