问题描述
我目前有一个多站点的虚拟主机,我设置了一些通用的错误文档,每个域都可以重置。我也有一个黑名单重写,但如果 IP 被阻止,我尝试设置自定义错误条件,然后显示自定义错误页面。
<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>
在 /var/www/
和 /var/www/hosts/
中有一个 htaccess 设置 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
在 vhost 中,您可以看到黑名单,如果满足 RewriteCond,则尝试将浏览器重定向到 /cloud-cgi/error_handler.PHP
,HTTP 代码为 404。
出于某种原因,当我尝试此操作时,我没有访问自定义的 ErrorDocument,而是获得了默认的 Apache 文档。如何触发这些 HTTP 错误之一,并重定向到 ErrorDocument。如果我可以设置一个带有值的环境变量以便处理程序可以设置自定义消息,那也将非常好。我也尝试从 vhost 定义中设置错误文档,但没有运气。
我错过了什么吗?
解决方法
RewriteRule ^ "/cloud-cgi/error_handler.php" [R=404,L]
...尝试使用 HTTP 代码 404 将浏览器重定向到 /cloud-cgi/error_handler.php
当您使用 3xx 范围之外的 R
标志指定 HTTP 状态代码时,替换 字符串将被忽略(不发生替换)。在这种情况下,最好简单地包含一个连字符 (-
) 作为替换字符串,以明确表明不打算进行替换。
当您指定 R=404
时,Apache 会触发 404 响应(通过内部子请求)并将调用自定义的 404 ErrorDocument
定义。
但是,定义的错误文档本身需要是可访问的。在您发布的指令中,当用户 IP 地址在黑名单中时,对 /cloud-cgi/error_handler.php
本身的请求也会触发 404 响应,因此无法显示自定义错误文档。 Apache 回退到 Apache 默认值(并且抱怨自定义错误文档触发了 404 甚至 500 响应,具体取决于您在何处使用这些指令)。
要使其可访问,您可以:
包括特定的例外
在您的规则中包含一个例外,以排除对特定错误文档的请求。例如:
RewriteRule !^/cloud-cgi/error_handler.php - [R=404]
(指定非 3xx 响应代码时不需要 L
标志;它是隐含的。)
包括通用异常
或者,更笼统地说,防止在错误响应已经触发时触发规则。这可以通过检查 REDIRECT_STATUS
环境变量来实现,该变量在触发错误文档后设置为 HTTP 响应代码。例如:
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^ - [R=404]
在上面我们只是检查 REDIRECT_STATUS
环境变量是否为空。 (旁白:此技术不适用于 LiteSpeed 服务器,因为 REDIRECT_STATUS
环境变量在请求到错误状态期间未更新。)
允许公共访问错误文档
但是您可能还有其他指令也可能需要触发错误文档...
或者,使用 mod_authz_core 允许公共访问。例如:
<Files error_handler.php>
Require all granted
</Files>
在错误状态下防止进一步的重写处理
或者,当设置 REDIRECT_STATUS
时,创建一个较早的异常并防止进一步的重写处理(在当前上下文中)。但是,这取决于您拥有哪些其他指令,现在指令的顺序很重要。例如,before vHost 配置中的现有规则:
RewriteCond %{ENV:REDIRECT_STATUS} !^$
RewriteRule ^ - [END]
内部重写
或者,您在内部将请求重写为自定义错误文档。但是您现在必须在您的 PHP 脚本中设置 404 响应代码(理想情况下您需要这样做,以防直接请求错误文档 - 不太可能但并非不可能):
RewriteRule ^ /cloud-cgi/error_handler.php [L]
一些可以被各个域重置的通用错误文档
这里的问题是,如果它们被每个域重置,那么上面包含的特定异常将不再有效。您可以为所有错误文档设置一个特定的子目录,并允许对该目录进行不受限制的访问。
如果我可以设置一个带有值的环境变量以便处理程序可以设置自定义消息,那就太好了。
您可以在同一个 RewriteRule
指令中设置环境变量。例如:
RewriteRule ^ - [E=MYVAR:Message,R=404]
以上将环境变量 MYVAR
设置为值 Message
。如果您的消息文本包含空格,则用双引号将整个 flags 参数括起来。 (尽管我会将您的具体错误文本保留在 error_handler.php
中,并在此处设置一个短代码或标志。)
RewriteRule ^ - "[E=MYVAR:Custom message with spaces,R=404]"
,
正如 anubhava 所说...尝试将 RewriteRule ^ "/cloud-cgi/error_handler.php" [R=404,L]
更改为 RewriteRule ^ - [R=404,L]
重写规则使得错误处理程序文件似乎不存在。因此,由于 apache 无法访问 errorhandler 文件,因此它显示了默认值。并且您还应该在默认错误页面中看到一条通知,内容类似于此外,在尝试使用 ErrorDocument 处理请求时遇到了 404 Not Found 错误。