在Apache RewriteRule指令中设置环境变量时,是什么导致变量名称以"REDIRECT_"作为前缀?

tro*_*wel 70 php apache mod-rewrite naming environment-variables

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

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

A.什么原因导致Apache中设置的环境变量使用[E=VAR:VAL]标志通过将"REDIRECT_"添加到变量名前面来重命名?

B.我可以做些什么来确保我最终得到一个名称不变的环境变量,这样我就可以在PHP中访问它,$_SERVER['VAR']而无需检查变量名称的变体,其中有一个或多个"REDIRECT_"实例被预先添加对吗?

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

RewriteCond %{ENV:REDIRECT_VAR} !^$
RewriteRule .* - [E=VAR:%{ENV:REDIRECT_VAR}]
Run Code Online (Sandbox Code Playgroud)

JMM*_*JMM 73

这种行为很不幸,甚至没有记录.

.htaccess per-dir context

这是在.htaccess每个目录(per-dir)上下文中出现的情况:

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

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

  2. 重写开始

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

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

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

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

    这一步是所选变量名称被删除的地方,稍后我会解释为什么这么重要且不方便.

恢复变量名称

当我最初遇到这个问题时,我在.htaccess(简化)中做了类似下面的事情:

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

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

RewriteRule (.+/docroot)/ - [L,E=EFFECTIVE_DOCUMENT_ROOT:$1]
Run Code Online (Sandbox Code Playgroud)

如果我要在第一个中设置环境变量RewriteRule,Apache将重新启动重写过程并在变量前面添加REDIRECT_(上面的步骤#4和5),因此我将通过我指定的名称失去对它的访问权限.

在这种情况下,第一个RewriteRule更改URL,因此在RewriteRule处理完两个s 后,Apache重新启动该过程并.htaccess再次处理.第二次,RewriteRule由于RewriteCond指令而跳过第一次,但是第二次RewriteRule匹配,再次设置环境变量,并且重要的是,不会更改URL.因此请求/重写过程不会重新开始,我选择的变量名称会粘在上面.在这种情况下,我实际上有两个REDIRECT_EFFECTIVE_DOCUMENT_ROOTEFFECTIVE_DOCUMENT_ROOT.如果我L在第一个使用旗帜RewriteRule,我只会EFFECTIVE_DOCUMENT_ROOT.

@trowel的部分解决方案的工作方式类似:再次处理重写指令,重命名的变量再次分配给原始名称,如果URL没有更改,则流程结束,指定的变量名称会粘住.

为什么这些技术不合适

这两种技术都存在一个主要缺陷:当.htaccess您设置环境变量的文件中的重写规则将URL重写为具有.htaccess任何重写文件的更深层嵌套目录时,您的指定变量名称将再次被删除.

假设您有这样的目录布局:

docroot/
        .htaccess
        A.php
        B.php
        sub/
                .htaccess
                A.php
                B.php
Run Code Online (Sandbox Code Playgroud)

docroot/.htaccess这样的:

RewriteRule ^A\.php sub/B.php [L]

RewriteRule .* - [E=MAJOR:flaw]
Run Code Online (Sandbox Code Playgroud)

所以你要求/A.php,它被重写sub/B.php.你还有MAJOR变量.

但是,如果你有任何重写指令docroot/sub/.htaccess(即使只是RewriteEngine OffRewriteEngine On),你的MAJOR变量就会消失.这是因为一旦URL被重写sub/B.php,docroot/sub/.htaccess被处理,并且如果它包含任何重写指令,docroot/.htaccess则不会再次处理重写指令.如果您处理了一个REDIRECT_MAJORafter docroot/.htaccess(例如,如果您省略了L第一个标记RewriteRule),您仍然可以使用它,但是这些指令不会再次运行来设置您选择的变量名称.

遗产

所以,说你想:

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

  2. 将它们以更深层次的脚本提供

  3. 让它们具有指定的名称

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

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

@trowel的技术

据我所知,支持使用像标志%{ENV:REDIRECT_VAR}的值组件RewriteRule E(例如[E=VAR:%{ENV:REDIRECT_VAR}])中的构造似乎没有记录:

VAL可能包含将被扩展的反向引用($ N或%N).

它看起来确实有效,但是如果你想避免依赖未记录的东西(如果我错了,请纠正我),它可以很容易地用这种方式完成:

RewriteCond %{ENV:REDIRECT_VAR} (.+)
RewriteRule .* - [E=VAR:%1]
Run Code Online (Sandbox Code Playgroud)

SetEnvIf之后

我不建议依赖于此,因为它似乎与记录的行为不一致(见下文),但是(在docroot/.htaccessApache 2.2.20中)对我有用:

SetEnvIf REDIRECT_VAR (.+) VAR=$1
Run Code Online (Sandbox Code Playgroud)

只有早期SetEnvIf [NoCase]指令定义的环境变量才能以这种方式进行测试.

为什么?

我不知道为这些名称添加前缀的理由REDIRECT_是什么 - 这并不奇怪,因为它似乎没有在Apache文档部分中提到mod_rewrite指令,RewriteRule标志环境变量.

目前这对我来说似乎是一个很大的麻烦,因为没有解释为什么它比单独留下指定名称更好.缺乏文件只会导致我对此持怀疑态度.

能够在重写规则中分配环境变量是有用的,或者至少是有用的.但是这种改变名称的行为大大减少了它的用处.这篇文章的复杂性说明了这种行为的坚果以及必须通过试图克服它的箍.

  • FWIW,这里有一些Apache的文档,明确谈到"REDIRECT_"前缀:http://httpd.apache.org/docs/2.0/custom-error.html.我同情它是多么恼人,但是 - 我自己被它咬了一口. (2认同)

the*_*iko 8

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

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

编辑 - 对提出的问题做出更多回应:

答:如果Apache涉及重定向,则Apache会重命名环境变量.例如,如果您有以下规则:

RewriteRule ^index.php - [E=VAR1:'hello',E=VAR2:'world']
Run Code Online (Sandbox Code Playgroud)

然后你可以使用$_SERVER['VAR1']和访问VAR1和VAR2 $_SERVER['VAR2'].但是,如果您重定向页面,如下所示:

RewriteRule ^index.php index2.php [E=VAR1:'hello',E=VAR2:'world']
Run Code Online (Sandbox Code Playgroud)

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

B:克服这个问题的最好方法是使用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;
}
Run Code Online (Sandbox Code Playgroud)