1

I'm having the following situation, I'm developing an API. I rewrite all traffic to one PHP script named: route.php, using mod_rewrite like:

1: RewriteEngine On
2: RewriteCond %{REQUEST_FILENAME} -f [OR]
3: RewriteCond %{REQUEST_FILENAME} -d
4: RewriteRule ^.* route.php [L]

Other files should not be accessible, that's why I am using a whitelist for accessing route.php only. Therefor I use this:

order allow,deny
<FilesMatch "route\.php">
    allow from all
</FilesMatch>

I would like to send all 1xx, 2xx (except 200), 4xx, and if possible 5xx HTTP status codes to a PHP script (let's say error.php?code=404 that shows the dynamic error page for that statuscode. In this case I probably have to allow access to error.php also in the FilesMatch part.

I found partly what I want, described in this article, but I can't implement or manage to get it working the way I described above.

My purpose is for the error.php to show a JSON output (dynamically based on the statuscode) like {'statusCode':'404','status':'Not Found'} including all common (security) HTTP headers I use.

Bob Ortiz
  • 442
  • 4
  • 21

1 Answers1

1

This question didn't get much attention and I managed to answer the question myself. So hereby my answer. This is possible in the following way. Any improvement is welcome.

Below should be in the .htaccess file:

order allow,deny
deny from all
<FilesMatch "^route\.php$">
    # replace 0.0.0.0 with IP that is allowed to access the Matched files
    allow from 0.0.0.0
</FilesMatch>

ErrorDocument 400     /error.php?code=400
ErrorDocument 401     /error.php?code=401
ErrorDocument 403     /error.php?code=403
ErrorDocument 404     /error.php?code=404
ErrorDocument 500     /error.php?code=500

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteRule ^route.php/(.*)$ index.php [L]
</IfModule>
<IfModule !mod_rewrite.c>
    ErrorDocument 403 /error.php?code=403
</IfModule>

The following should be in the error.php file:

if (!function_exists('http_response_code_text'))
{
    function http_response_code_text($code = 403)
    {
        $text = 'Forbidden';
        switch ($code)
        {
            case 400: $text = 'Bad Request'; break;
            case 401: $text = 'Unauthorized'; break;
            case 403: $text = 'Forbidden'; break;
            case 404: $text = 'Not Found'; break;
            case 500: $text = 'Internal Server Error'; break;
            default:
                $text = 'Forbidden';
            break;
        }
        return $text;
    }
}

$code = 403;
if(isset($_GET['code']{0}))
{
    $code = (int) $_GET['code'];
}

$text = http_response_code_text($code);
echo json_encode(array('httpStatusCode' => $code, 'httpStatusText' => $text));

This doesn't seem to work for all HTTP error codes, like 414 "Request-URI Too Long" and always falls back to 403 Forbidden.

Bob Ortiz
  • 442
  • 4
  • 21
  • There are several error codes that you simply can't trap in "userland" code (such as 414 and most _types_ of 500 - which is just a generic error state). You don't necessarily need to explicitly pass the status code to your script. The `$_SERVER['REDIRECT_STATUS']` should also contain this information (which is not set at all for direct requests, but "200" after a successful rewrite). The `ErrorDocument` directive in the `` container would seem to be superfluous? – MrWhite Jul 13 '16 at 22:31