这种行为是不幸的,甚至没有被记录在案。
.htaccess每个目录上下文
这是.htaccess
每个目录(每个目录)上下文中似乎发生的情况:
假设Apache处理.htaccess
包含重写指令的文件。
-
Apache使用所有标准CGI / Apache变量填充其环境变量映射
-
重写开始
-
环境变量在RewriteRule
指令
中设置
-
当Apache停止处理RewriteRule
指令(由于L
标志或规则集的结尾)并且URL已被更改时RewriteRule
,Apache将重新启动请求处理。
如果您不熟悉此部分,请参阅L
标志文档:
因此,规则集可以从头开始再次运行。如果其中一个规则导致重定向(内部或外部)导致请求过程重新开始,则通常会发生这种情况。
-
从我的观察中,我相信当发生#4时,重复#1,然后在RewriteRule
指令中设置的环境变量将被REDIRECT_
添加到环境var映射之前并添加到环境var映射中(不一定按此顺序,但是最终结果包括的组合)。
这是清除所选变量名的步骤,稍后我将解释为什么它如此重要和不便。
恢复变量名
当我最初遇到此问题时,我在.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
处理完两个后,Apache重新启动该过程并.htaccess
再次处理。第二次,RewriteRule
由于该RewriteCond
指令而跳过了第一次,但是第二次RewriteRule
匹配,再次设置了环境变量,并且重要的是,它没有更改URL。因此,请求/重写过程不会重新开始,因此我选择的变量名将保持不变。在这种情况下,我实际上同时拥有REDIRECT_EFFECTIVE_DOCUMENT_ROOT
和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
和docroot/.htaccess
这样的:
RewriteRule ^A\.php sub/B.php [L]
RewriteRule .* - [E=MAJOR:flaw]
因此,您可以请求/A.php
,并将其重写为sub/B.php
。您仍然有MAJOR
变量。
但是,如果您在中有任何重写指令docroot/sub/.htaccess
(甚至justRewriteEngine Off
或RewriteEngine On
),MAJOR
变量都会消失。这是因为一旦将URL重写为sub/B.php
,docroot/sub/.htaccess
便会对其进行处理,并且如果其中包含任何重写指令,docroot/.htaccess
则不会再次处理其中的重写指令。如果您有一个REDIRECT_MAJOR
after后docroot/.htaccess
处理(例如,如果省略了L
first中的标志RewriteRule
),您仍然会拥有它,但是这些指令不会再次运行以设置您选择的变量名。
遗产
因此,说您想:
-
RewriteRule
在目录树的特定级别的指令中
设置环境变量(例如docroot/.htaccess
)
-
在更深层次的脚本中提供它们
-
使它们具有分配的名称
-
能够在更深层嵌套的.htaccess
文件中
具有重写指令
一种可能的解决方案是RewriteOptions inherit
在嵌套更深的.htaccess
文件中使用指令。这样,您就可以在嵌套程度较低的文件中重新运行rewrite指令,并使用上面概述的技术来设置具有所选名称的变量。但是,请注意,这会增加复杂性,因为您必须更加谨慎地在嵌套程度较低的文件中编写重写指令,以使它们在从嵌套程度更高的目录中再次运行时不会引起问题。我相信Apache会为嵌套深度更深的目录除去per-dir前缀,并在嵌套深度较小的文件中对该值运行rewrite指令。
@trowel的技术
据我所知,似乎并没有证明支持%{ENV:REDIRECT_VAR}
在RewriteRule
E
标志的值组件(例如[E=VAR:%{ENV:REDIRECT_VAR}]
)中使用类似的构造:
VAL可能包含将被扩展的反向引用($ N或%N)。
它确实可以工作,但是如果您要避免依赖未记录的内容(如果我对此有误,请纠正我),可以这样轻松地完成它:
RewriteCond %{ENV:REDIRECT_VAR} (.+)
RewriteRule .* - [E=VAR:%1]
设置环境
我不建议依赖此方法,因为它似乎与所记录的行为不一致(请参见下文),但是(在docroot/.htaccess
Apache 2.2.20中,此方法)对我有用:
SetEnvIf REDIRECT_VAR (.+) VAR=$1
只有早期的SetEnvIf [NoCase]指令定义的那些环境变量才能以这种方式进行测试。
为什么?
我不知道为这些名称加上前缀的理由REDIRECT_
是什么-不足为奇,因为在Apache文档部分中似乎没有提及mod_rewrite指令,RewriteRule
标志或环境变量。
目前,对于我来说,这似乎是一个很大的麻烦,因为没有解释为什么它比单独分配名称更好的解释。缺乏文档只会加剧我对此的怀疑。
能够在重写规则中分配环境变量很有用,或者至少是有用的。但是,这种改名行为大大降低了其实用性。这篇文章的复杂性说明了这种行为以及为克服它而必须克服的困难。