Day: June 11, 2007

Apache

Useful custom 403 and 404 error pages with PHP

While this is certainly nothing new, it seems to be too often overlooked. Apache allows an ErrorDocument Directive in the configuration that will point at a custom document. Using this can have some benefits to the user and to the site administrator.

While Apache allows for error documents located at a remote URL (ie anything starting with http://) this causes Apache to send a redirect to the browser, even if the document resides on the same server. This is not a good idea, as the documentation points out.

This has several implications, the most important being that the client will not receive the original error status code, but instead will receive a redirect status code. This in turn can confuse web robots and other clients which try to determine if a URL is valid using the status code. In addition, if you use a remote URL in an ErrorDocument 401, the client will not know to prompt the user for a password since it will not receive the 401 status code. Therefore, if you use an ErrorDocument 401 directive then it must refer to a local document.

Using a local document for handling errors, however, gives you the ability to override the default Apache messages, which are often replaced by the browser with their own, internal error messages (MSIE, I’m talking about you.) Besides giving you the ability to match the error page to your site, you can use some simple PHP to make it more informative for both the end user and the site admin. Instead of just saying “File so-and-so doesn’t exist, sorry” you can make a page that allows the user to send a message to the admin. If you wish, you can have the page automatically mail the information, although that can quickly lead to hundreds of emails as users mis-type urls, spiders follow old links, and scripts search your LAMP site for IIS vulnerabilities. Trust me on that one, it’s a bad idea that won’t outlive the weekend.

With that in mind here a couple samples that you can build from.

Sample 403 error page:

<?php
print "<html>
<head>
<title>Sample 403 Error Document</title>
</head>
<body>"
$server = $_SERVER['SERVER_NAME'];
$uri = $_SERVER['REQUEST_URI'];
$bad_link = $server.$uri;
// Note that the referer cannot be completely trusted
// as some agents either do not set a referer or allow
// the user to modify the referer at will. It is, however,
// often useful for troubleshooting.
$referer = $_SERVER['HTTP_REFERER'];
$remote = $_SERVER['REMOTE_ADDR'];
print "<h1>403: Forbidden</h1>
<p> </p>";
if ($uri == '/403/403.php') { 
	print "<p>>You have reached the custom 403 error page for mysite.com. Was it everything you were hoping for?</p>";
}
else if (substr($uri, -1, 1) == '/') {
    print "<p>Sorry, this directory cannot be browsed.</p>
    <p>If you received this message by clicking on a link on this website, please <a href=\"mailto:webmaster@mysite.com?subject=403: Bad Directory Link&body=$bad_link from $referer\">report it to the webmaster</a>.</p>";
}
else {
    print "<p>You have attempted to access a resource ($uri) for which you do not have the proper authorization or which is not available from your location.</p>
    <p>If you received this message by clicking on a link on this website, please <a href=\"mailto:webmaster@mysite.com?subject=403 Error&body=$bad_link from $referer reached by $remote\">report it to the webmaster</a>.</p>";
}
print "</body>
</html>
";
?>

Sample 404 error page:

<?php
print "<html>
<head>
<title>Sample 403 Error Document</title>
</head>
<body>"
$server = $_SERVER['SERVER_NAME'];
$uri = $_SERVER['REQUEST_URI'];
$bad_link = $server.$uri;
// Note that the referer cannot be completely trusted
// as some agents either do not set a referer or allow
// the user to modify the referer at will. It is, however,
// often useful for troubleshooting.
$referer = $_SERVER['HTTP_REFERER'];
print "<h1>404: File Not Found</h1>
<p> </p>";
if ($uri == '/404/404.php') {
    print "<p>You have reached the custom 404 error page for mysite.com. Was it everything you were hoping for?</p>";
}
else {
    print "<p>Sorry, that file ($uri) does not seem to exist.</p>
    <p>If you received this message by clicking on a link on this website, please <a href=\"mailto:webmaster@mysite.com?subject=Bad Link&body=$bad_link from $referer\">report it to the webmaster</a>.</p>";
}
print "</body>
</html>
";
?>

Of course you would make sure the styles, links, mailtos, site name, etc are right for your site, but this gives you an idea.

Technorati Tags: , ,