skip to Main Content

I currently have a multi-site virtual host, and I have set up some universal error documents that can be reset by each domain. I also have a blacklisting rewrite, but I’m trying to set a custom error condition if a IP is blocked, and then show a custom error page.

<Virtualhost *:80>
    ServerName catchall.host
    ServerAlias *
    VirtualDocumentRoot /var/www/hosts/%0
    Alias /cloud-cgi /var/www/cloud-cgi
    SetEnv DIRECT_ACCESS no
    HttpProtocolOptions Unsafe

    RewriteEngine On
    RewriteMap access txt:/var/www/blacklist.txt
    RewriteCond ${access:%{REMOTE_ADDR}} deny [NC]
    RewriteRule ^ "/cloud-cgi/error_handler.php" [R=404,L]

    RemoteIPHeader CF-Connecting-IP
    CustomLog /var/www/catchall_access.log combined
    ErrorLog /var/www/catchall_error.log
</Virtualhost>

In /var/www/ and /var/www/hosts/ there is a htaccess setting the ErrorDocuments

ErrorDocument 400 /cloud-cgi/error_handler.php
ErrorDocument 401 /cloud-cgi/error_handler.php
ErrorDocument 403 /cloud-cgi/error_handler.php
ErrorDocument 404 /cloud-cgi/error_handler.php
ErrorDocument 500 /cloud-cgi/error_handler.php

In the vhost you can see the blacklist, where if the RewriteCond is met, tries to redirect the browser to /cloud-cgi/error_handler.php with HTTP code 404.

For some reason, when I try this, instead of getting to the custom ErrorDocument, I get the default Apache one. How can I trigger one of those HTTP errors, and redirect to the ErrorDocument. It would also be really nice if I could set a environment variable with a value so the handler can set a custom message. I’ve attempted to set the error document from the vhost definition as well, but without luck.

Am I missing something?

2

Answers


  1. As anubhava said… Try changing RewriteRule ^ "/cloud-cgi/error_handler.php" [R=404,L] to RewriteRule ^ - [R=404,L]

    The rewrite rule is making it so that the errorhandler file doesn’t appear to exist. So, because apache cannot access the errorhandler file, it shows the default one. And you should also be seeing a notice in the default error page, saying something like Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.

    Login or Signup to reply.
  2. RewriteRule ^ "/cloud-cgi/error_handler.php" [R=404,L]
    

    …tries to redirect the browser to /cloud-cgi/error_handler.php with HTTP code 404

    When you specify an HTTP status code with the R flag outside of the 3xx range then the substitution string is ignored (no substitution occurs). It is preferable to simply include a hyphen (-) as the substitution string in this case to explicitly show that a substitution is not intended.

    When you specify R=404, Apache triggers a 404 response (via an internal subrequest) and will call the custom 404 ErrorDocument as defined.

    However, the defined error document itself needs to be accessible. In the directives you posted, a request for /cloud-cgi/error_handler.php itself would also trigger a 404 response when the users IP address is on the blacklist, so the custom error document cannot be displayed. Apache falls back to the Apache default (and complains that either the custom error document triggered a 404 or even a 500 response, depending on where you are using these directives).

    To make it accessible, you could either:

    Include a specific exception

    Include an exception in your rule, to exclude requests for the specific error document. For example:

     RewriteRule !^/cloud-cgi/error_handler.php - [R=404]
    

    (The L flag is not required when specifying a non-3xx response code; it is implied.)

    Include a generic exception

    OR, more generically, prevent the rule being triggered when an error response has already been triggered. This can be achieved by examining the REDIRECT_STATUS environment variable, which is set to the HTTP response code after the error document is triggered. For example:

    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteRule ^ - [R=404]
    

    In the above we are simply checking that the REDIRECT_STATUS env var is empty. (Aside: This technique does not work on LiteSpeed servers, since the REDIRECT_STATUS env var is not updated during the request to the error state.)

    Allow public access to the error document

    But it’s possible you have other directives that might also need to trigger an error document…

    OR, use a mod_authz_core to permit public access. For example:

    <Files error_handler.php>
    Require all granted
    </Files>
    

    Prevent further rewrite processing when in an error state

    OR, create an earlier exception and prevent further rewrite processing (in the current context) when REDIRECT_STATUS is set. However, this depends what other directives you have and now the order of the directives is important. For example, before your existing rule in the vHost config:

    RewriteCond %{ENV:REDIRECT_STATUS} !^$
    RewriteRule ^ - [END]
    

    Internal rewrite

    Alternatively, you internally rewrite the request to the custom error document. But you must now set the 404 response code in your PHP script (which ideally you need to do anyway in case the error document is requested directly – unlikely but not impossible):

    RewriteRule ^ /cloud-cgi/error_handler.php [L]
    

    some universal error documents that can be reset by each domain

    The problem here is that if they are reset by each domain then the specific exception included above will no longer be valid. You could have a specific subdirectory for all error documents and simply permit unrestricted access to that directory.

    It would also be really nice if I could set a environment variable with a value so the handler can set a custom message.

    You can set an env var in the same RewriteRule directive. For example:

    RewriteRule ^ - [E=MYVAR:Message,R=404]
    

    The above sets the env var MYVAR to the value Message. If your message text contains spaces then surround the entire flags argument in double-quotes. (Although I would leave your specific error text up to the error_handler.php and just set a short code or flag here.)

    RewriteRule ^ - "[E=MYVAR:Custom message with spaces,R=404]"
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search