这种行为是不幸的,甚至似乎没有被记录下来。
.htaccess 每个目录上下文
这似乎发生在.htaccess
每个目录(per-dir)上下文:
假设 Apache 处理.htaccess
包含重写指令的文件。
-
Apache 使用所有标准 CGI / Apache 变量填充其环境变量映射
-
重写开始
-
环境变量设置在RewriteRule
指令
-
当 Apache 停止处理RewriteRule
指令(因为L
标志或规则集末尾)并且 URL 已被更改RewriteRule
,Apache 重新启动请求处理。
如果您不熟悉这部分,请参阅L标记文档:
thus the ruleset may be run again from the start. Most commonly this will happen if one of the rules causes a redirect - either internal or external - causing the request process to start over.
-
据我观察,我相信当#4发生时,#1会重复,然后设置的环境变量RewriteRule
指令前缀为REDIRECT_
并添加到环境变量映射中(不一定按该顺序,而是由该组合组成的最终结果)。
这一步是删除所选变量名称的地方,稍后我将解释为什么这如此重要且不方便。
恢复变量名
当我最初遇到这个问题时,我正在做类似以下的事情.htaccess
(简化):
RewriteCond %{HTTP_HOST} (.+)\.projects\.
RewriteRule (.*) subdomains/%1/docroot/$1
RewriteRule (.+/docroot)/ - [L,E=EFFECTIVE_DOCUMENT_ROOT:$1]
如果我要先设置环境变量RewriteRule
,Apache 将重新启动重写过程并在变量前面加上REDIRECT_
(上面的步骤#4和5),因此我将无法通过我分配的名称访问它。
在这种情况下,第一个RewriteRule
更改 URL,因此在两者之后RewriteRule
s 被处理后,Apache 重新启动该过程并处理.htaccess
再次。第二次,第一次RewriteRule
被跳过是因为RewriteCond
指令,但第二个RewriteRule
匹配,设置环境变量(再次),并且,重要的是,不更改 URL。因此请求/重写过程不会重新开始,并且我选择的变量名称会保留。在这种情况下,我实际上两者都有REDIRECT_EFFECTIVE_DOCUMENT_ROOT
and EFFECTIVE_DOCUMENT_ROOT
。如果我要使用L
第一个标志RewriteRule
,我只有EFFECTIVE_DOCUMENT_ROOT
.
@trowel 的部分解决方案的工作原理类似:再次处理重写指令,将重命名的变量再次分配给原始名称,如果 URL 没有更改,则该过程结束,分配的变量名称将保留。
为什么这些技术不够充分
这两种技术都存在一个重大缺陷:当重写规则时.htaccess
您设置环境变量的文件将 URL 重写为更深层嵌套的目录,该目录具有.htaccess
如果文件进行任何重写,您分配的变量名称将再次被擦除。
假设您有这样的目录布局:
docroot/
.htaccess
A.php
B.php
sub/
.htaccess
A.php
B.php
And a docroot/.htaccess
像这样:
RewriteRule ^A\.php sub/B.php [L]
RewriteRule .* - [E=MAJOR:flaw]
所以你要求/A.php
,并且它被重写为sub/B.php
。你还有你的MAJOR
多变的。
但是,如果您有任何重写指令docroot/sub/.htaccess
(即使只是RewriteEngine Off
or RewriteEngine On
), your MAJOR
变量消失。这是因为一旦 URL 被重写为sub/B.php
, docroot/sub/.htaccess
被处理,如果它包含任何重写指令,则重写指令docroot/.htaccess
不会再次被处理。如果你有一个REDIRECT_MAJOR
after docroot/.htaccess
已处理(例如,如果您省略L
从一开始的标志RewriteRule
),您仍然会拥有它,但这些指令不会再次运行来设置您选择的变量名称。
遗产
那么,假设您想要:
-
设置环境变量RewriteRule
目录树特定级别的指令(例如docroot/.htaccess
)
-
让它们在更深层次的脚本中可用
-
让它们具有指定的名称
-
能够更深层地嵌套重写指令.htaccess
files
一个可能的解决方案是使用RewriteOptions inherit
更深层嵌套的指令.htaccess
文件。这允许您在嵌套深度较低的文件中重新运行重写指令,并使用上面概述的技术来设置具有所选名称的变量。但是,请注意,这会增加复杂性,因为您必须更加小心地在嵌套较浅的文件中编写重写指令,以便它们在从嵌套较深的目录再次运行时不会导致问题。我相信 Apache 会去除嵌套较深的目录的 per-dir 前缀,并在嵌套较浅的文件中根据该值运行重写指令。
@trowel的技术
据我所知,支持使用像%{ENV:REDIRECT_VAR}
在 a 的值分量中RewriteRule
E
标志(例如[E=VAR:%{ENV:REDIRECT_VAR}]
)似乎不是记录在案:
VAL 可能包含将被扩展的反向引用($N 或 %N)。
它似乎确实有效,但如果您想避免依赖未记录的内容(如果我错了,请纠正我),可以轻松地通过以下方式完成:
RewriteCond %{ENV:REDIRECT_VAR} (.+)
RewriteRule .* - [E=VAR:%1]
SetEnvIf
我不建议依赖这个,因为它似乎与记录的行为(见下文),但是这个(在docroot/.htaccess
,使用 Apache 2.2.20)对我有用:
SetEnvIf REDIRECT_VAR (.+) VAR=$1
只有那些由早期 SetEnvIf[NoCase] 指令定义的环境变量才可用于以这种方式进行测试。
Why?
我不知道给这些名字加上前缀的理由是什么REDIRECT_
是——并不奇怪,因为 Apache 文档部分中似乎没有提到它mod_rewrite 指令, RewriteRule flags, or 环境变量.
目前,这对我来说似乎是一个很大的麻烦,因为没有解释为什么它比保留分配的名称更好。缺乏文档只会加剧我对此的怀疑。
能够在重写规则中分配环境变量是有用的,或者至少是有用的。但这种改名行为大大削弱了实用性。这篇文章的复杂性说明了这种行为是多么疯狂,以及为了克服它而必须克服的困难。