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
As anubhava said… Try changing
RewriteRule ^ "/cloud-cgi/error_handler.php" [R=404,L]
toRewriteRule ^ - [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.
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 404ErrorDocument
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:
(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: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 theREDIRECT_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:
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: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):
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.
You can set an env var in the same
RewriteRule
directive. For example:The above sets the env var
MYVAR
to the valueMessage
. 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 theerror_handler.php
and just set a short code or flag here.)