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