Always return a 404 when you decide to return a 403

Glen picture Glen · May 9, 2012 · Viewed 10.9k times · Source

There are several questions already posted here about returning a 404 instead of a 403 in special cases (e.g., .ht* files or a certain directory), but I can't figure out how to simply replace all 403 responses ("Okay, it exists, but you still have to find a way in") with 404s ("Sorry, never heard of it"). I'm hoping that there is a simple solution that won't require updating regexes or other bits of the .htaccess to match site changes, just a simple directive: "whenever you decide to return a 403, return a 404 instead" that applies to the whole site, regardless of configuration changes.

Then, if the top level .htaccess contains "Options -Indexes", and any given directory contains no index.html (or equiv), the bare directory URL will return 404, but if I ever add an index.html to that directory, the same bare directory URL will return the index.html, with no updates needed to any .htaccess file.

I don't even care if, in the event I ever password a directory, a bad password returns a 404 (because of the 404 -> 403 mapping). In that case, I'm not hiding anything by returning a 404, but it causes no harm either. If there's a way to UNDO the general 403->404 mapping for special cases (rather than DO it for special cases), though, that could be even more useful.

Of course, if I'm overlooking something, please set me straight.

EDIT: Drat. I was trying to write a good quality question here, but my description of the behavior of "Options -Indexes" in the second paragraph turns out to be wrong. Without that line a bare directory URL shows "index.html" if exists in the directory; otherwise, it reveals the contents of the directory. (That forwarding of /dir to /dir/index.html if index.html exists is the default setup of the Web host, unless I'm mistaken.) Adding the "Options -Indexes" line stops it airing my laundry in public (returning a 403, not 404, but still better than exposing the dir contents), but now the bare directory URL returns a 403 even if index.html exists.

I wish a bare dir URL "mysite.com/mydir" displayed /mydir/index.html if it existed and "404" if it didn't, but clearly there's more to it than just replacing the 403s with 404s.

Answer

Marcel picture Marcel · May 14, 2013

To complete one of the better answers here (as mentioned by @WebChemist and @JennyD), a good way to solve this is to return 404 Not Found from the document you use to handle 403 errors. Personally I do something like the following:

.htaccess in web root (relevant excerpt):

ErrorDocument 400 /http-errors.php
ErrorDocument 403 /http-errors.php
ErrorDocument 404 /http-errors.php

http-errors.php in web root (condensed working example):

<?php

$status = $_SERVER['REDIRECT_STATUS'];
// If it's a 403, just bump it up to a 404
if ( $status == 403 ) $status++;

$codes = array(
  400 => array( '400 Bad Request', 'The request cannot be fulfilled due to...' ),
  404 => array( '404 Not Found', 'The resource you requested was not found...' ),
  500 => array( '500 Internal Server Error', 'The request was unsuccessful...' )
);

$title = $codes[$status][0];
$message = $codes[$status][1];

header( $_SERVER['SERVER_PROTOCOL'] . ' ' . $title );
echo "<h1>$title</h1>\n<p>$message</p>";