在 Apache RewriteRule 指令中设置环境变量时,是什么原因导致变量名称以“REDIRECT_”为前缀?.htaccess per-dir context恢复变量名称为什么这些技术不足遗产@trowel的技术SetEnvIf为什么?

我正在尝试在.htaccess文件中使用RewriteRule规则的标志来设置Apache环境变量(用于PHP)。[E=VAR:VAL]

我已经发现这些变量在PHP中作为服务器变量而不是(这有一定的意义)访问。但是,我的问题是对于某些规则,标志按预期工作,我最终得到一个变量,但对于其他规则,我以变量或,等结尾$_SERVER$_ENV[E=VAR:VAL]$_SERVER['VAR']$_SERVER['REDIRECT_VAR']$_SERVER['REDIRECT_REDIRECT_VAR']

一个。是什么原因导致在 Apache 中使用标志设置的环境变量通过在变量名称前面附加“REDIRECT_”来重命名?[E=VAR:VAL]

B.我能做些什么来确保我最终得到一个名称不变的环境变量,这样我就可以在PHP中访问它,而不必诉诸于检查变量名称的变体,其中有一个以上的“REDIRECT_”实例?$_SERVER['VAR']

找到部分解决方案。如果需要,将以下内容添加到重写规则的开头,可以在每个重定向上重新创建原始的ENV:VAR(以及将REDIRECT_VAR版本保留在那里):

RewriteCond %{ENV:REDIRECT_VAR} !^$
RewriteRule .* - [E=VAR:%{ENV:REDIRECT_VAR}]

答案 1

这种行为是不幸的,甚至没有记录在案。

.htaccess per-dir context

以下是在每个目录(每个目录)上下文中似乎发生的情况:.htaccess

假设 Apache 处理包含重写指令的文件。.htaccess

  1. Apache使用所有标准的CGI / Apache变量填充其环境变量映射

  2. 重写开始

  3. 环境变量在指令中设置RewriteRule

  4. 当 Apache 停止处理指令(由于标志或规则集的末尾)并且 URL 已被 a 更改时,Apache 将重新启动请求处理。RewriteRuleLRewriteRule

    如果您不熟悉此部分,请参阅 L 标志文档

    因此,规则集可以从一开始就再次运行。最常见的情况是,如果其中一个规则导致重定向(内部或外部)导致请求过程重新开始,则会发生这种情况。
  5. 根据我所能观察到的情况,我相信当#4发生时,#1被重复,那么在指令中设置的环境变量被附加并添加到环境vars映射中(不一定按该顺序,而是由该组合组成的最终结果)。RewriteRuleREDIRECT_

    此步骤是清除所选变量名称的位置,稍后我将解释为什么这如此重要且不方便。

恢复变量名称

当我最初遇到这个问题时,我正在做一些类似的事情(简化):.htaccess

RewriteCond %{HTTP_HOST} (.+)\.projects\.

RewriteRule (.*) subdomains/%1/docroot/$1

RewriteRule (.+/docroot)/ - [L,E=EFFECTIVE_DOCUMENT_ROOT:$1]

如果我在第一个中设置环境变量,Apache将重新启动重写过程并在变量前面附加(上面的步骤#4和5),因此我将失去通过我分配的名称访问它的权限。RewriteRuleREDIRECT_

在这种情况下,第一个更改 URL,因此在处理两个之后,Apache 重新启动该过程并再次处理该过程。第二次,由于指令的原因,第一次被跳过,但第二次匹配,设置环境变量(再次),并且重要的是,不会更改URL。因此,请求/重写过程不会重新开始,并且我选择的变量名称保持不变。在这种情况下,我实际上同时拥有 和 .如果我在第一个上使用标志,我只有.RewriteRuleRewriteRule.htaccessRewriteRuleRewriteCondRewriteRuleREDIRECT_EFFECTIVE_DOCUMENT_ROOTEFFECTIVE_DOCUMENT_ROOTLRewriteRuleEFFECTIVE_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.phpsub/B.phpMAJOR

但是,如果您在 中有任何重写指令(即使只是 或 ),您的变量就会消失。这是因为一旦将 URL 重写为 ,就会被处理,如果它包含任何重写指令,则不会再次处理 中的重写指令。如果你有一个 after 被处理(例如,如果你从第一个中省略了标志),你仍然会有它,但这些指令不会再次运行来设置你选择的变量名称。docroot/sub/.htaccessRewriteEngine OffRewriteEngine OnMAJORsub/B.phpdocroot/sub/.htaccessdocroot/.htaccessREDIRECT_MAJORdocroot/.htaccessLRewriteRule

遗产

因此,假设您要:

  1. 在目录树的特定级别的指令中设置环境变量(如RewriteRuledocroot/.htaccess)

  2. 在更深层次的脚本中提供它们

  3. 使它们与指定的名称一起使用

  4. 能够在更深入的嵌套文件中重写指令.htaccess

一种可能的解决方案是在更深层次的嵌套文件中使用指令。这允许您在嵌套程度较低的文件中重新运行重写指令,并使用上面概述的技术使用所选名称设置变量。但是,请注意,这会增加复杂性,因为您必须更小心地在嵌套程度较低的文件中制作重写指令,以便在从嵌套深度较深的目录再次运行时它们不会引起问题。我相信Apache为更深的嵌套目录去除了per-dir前缀,并在该值上嵌套程度较低的文件中运行重写指令。RewriteOptions inherit.htaccess

@trowel的技术

据我所知,支持在标志的值组件中使用类似构造(例如)似乎没有记录%{ENV:REDIRECT_VAR}RewriteRuleE[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_

目前,这对我来说似乎是一个很大的麻烦,因为没有解释为什么这比单独使用指定的名字更好。缺乏文档只会加剧我对此的怀疑。

能够在重写规则中分配环境变量是有用的,或者至少会很有用。但是,这种更名的行为大大降低了实用性。这篇文章的复杂性说明了这种行为以及必须跳过以试图克服它的箍是多么疯狂。


答案 2

我根本没有测试过这个问题,我知道它没有解决A点或B点,但是在PHP文档中的注释中有一些关于这个问题的描述,以及一些使用访问这些变量的可能解决方案:$_SERVER['VAR']

http://www.php.net/manual/en/reserved.variables.php#79811

编辑 - 对所提供问题的更多回答:

一个:如果环境变量涉及重定向,Apache 会重命名这些变量。例如,如果您有以下规则:

RewriteRule ^index.php - [E=VAR1:'hello',E=VAR2:'world']

然后,您可以使用 和 访问 VAR1 和 VAR2。但是,如果您像这样重定向页面:$_SERVER['VAR1']$_SERVER['VAR2']

RewriteRule ^index.php index2.php [E=VAR1:'hello',E=VAR2:'world']

然后你必须使用 等。$_SERVER['REDIRECT_VAR1']

乙:解决此问题的最佳方法是处理您有兴趣使用PHP的变量。创建一个遍历数组并查找所需项的函数。您甚至可以使用如下函数:$_SERVER

function myGetEnv($key) {
    $prefix = "REDIRECT_";
    if(array_key_exists($key, $_SERVER))
        return $_SERVER[$key];
    foreach($_SERVER as $k=>$v) {
        if(substr($k, 0, strlen($prefix)) == $prefix) {
            if(substr($k, -(strlen($key))) == $key)
                return $v;
        }
    }
    return null;
}

推荐