How to serve 403 and 404 error pages in Drupal code
Most of the time, 403 and 404 errors don't need to be dished out manually. Users will get a 404 when they hit a URL that doesn't correspond to an actual file or page, and will get a 403 when they are denied access through various access control utilities in Drupal. However, on occasion you'll need to explicitly send someone to a 404 or 403 error page, and in that case, drupal_not_found and drupal_access_denied are your friends. With them, you don't have to worry about manually settings headers or redirecting to the error page that's been defined in the error handling configuration.
Serving a 403 page with drupal_access_denied()
Let's take a look at an example of drupal_access_denied from the SimpleNews module:
// If non-admin is trying to edit someone else's subscription, access denied.
if ($user->uid && $user->uid != $subscription->uid && !user_access('administer simplenews subscriptions')) {
drupal_access_denied();
return;
}
In this example, a little logic is need to determine if a user has access. First, it checks to make sure the user is logged in, then insures that if the user editing the subscription doesn't have a particular permission, they get booted with a drupal_access_denied().
Note that the drupal_access_denied function will only print the 403 page, but will not stop the script from continuing. So, you need to make sure that the functionality you're preventing access to doesn't get run by using a return after the call.
Serving a 404 page with drupal_not_found()
Next, let's look at an example of drupal_not_found. This example comes from the Masquerade module:
$new_user = user_load(array('uid' => $uid));
if (!$new_user->uid) {
return drupal_not_found();
}
The Masquerade module is checking to make sure that a user exists, before allowing access to a page specific to the user. If the user doesn't exist, then the page also doesn't exist, therefore the current user should get a 404. Notice that this snippet uses a slightly different syntax for the return. Instead of returning on a separate line, return is used on the function itself. drupal_not_found will not return any value, but the syntax still works and is slightly more elegant.