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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3050444/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 08:32:19  来源:igfitidea点击:

When setting environment variables in Apache RewriteRule directives, what causes the variable name to be prefixed with "REDIRECT_"?

phpapachemod-rewriteenvironment-variablesnaming

提问by trowel

I am trying to set Apache environment variables (for use in PHP) with the [E=VAR:VAL]flag on RewriteRule rules in an .htaccess file.

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

I have already discovered the variables are accessed in PHP as server variables $_SERVERrather than $_ENV(which makes a certain amount of sense). However, my problem is for some rules the [E=VAR:VAL]flag works as expected and I end up with a variable $_SERVER['VAR']but for other rules I end with a variable $_SERVER['REDIRECT_VAR']or $_SERVER['REDIRECT_REDIRECT_VAR'], etc

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

A. What causes an environment variable set in Apache using the [E=VAR:VAL]flag to get renamed by having "REDIRECT_" prepended to the variable name?

A. 是什么导致使用[E=VAR:VAL]标志在 Apache 中设置的环境变量通过在变量名称前加上“REDIRECT_”而被重命名?

B. What can I do to make sure I end up with an Environment Variable with an unchanged name so I can access it in PHP as $_SERVER['VAR']without having to resort to checking for variations of the variable name having one of more instances of "REDIRECT_" prepended to it?

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

Partial solution found. Adding the following to the start of the rewrite rules recreates the original ENV:VAR on each redirect (as well as leaving the REDIRECT_VAR versions there) if they're needed:

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

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

回答by JMM

This behavior is unfortunate and doesn't even appear to be documented.

这种行为很不幸,甚至似乎没有记录。

.htaccess per-dir context

.htaccess 每个目录的上下文

Here's what appears to happen in .htaccessper-directory (per-dir) context:

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

Assume that Apache processes an .htaccessfile that includes rewrite directives.

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

  1. Apache populates its environment variable map with all of the standard CGI / Apache variables

  2. Rewriting begins

  3. Environment variables are set in RewriteRuledirectives

  4. When Apache stops processing the RewriteRuledirectives (because of an Lflag or the end of the ruleset) and the URL has been changed by a RewriteRule, Apache restarts request processing.

    If you're not familiar with this part, see the Lflag documentation:

    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.
  5. From what I can observe, I believe that when #4 happens, #1 is repeated, then the environment variables that were set in RewriteRuledirectives are prepended with REDIRECT_and added to the environment vars map (not necessarily in that order, but the end result consisting of that combination).

    This step is where the chosen variable names are wiped out, and in a moment I will explain why that is so important and inconvenient.

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

  2. 改写开始

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

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

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

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

    这一步是删除所选变量名的地方,稍后我将解释为什么这如此重要和不方便。

Restoring variable names

恢复变量名

When I originally ran into this issue, I was doing something like the following in .htaccess(simplified):

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

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

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

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

If I were to set the environment variable in the first RewriteRule, Apache would restart the rewriting process and prepend the variable with REDIRECT_(steps #4 & 5 above), thus I'd lose access to it via the name I assigned.

如果我要在第一个中设置环境变量RewriteRule,Apache 将重新启动重写过程并在变量前面加上REDIRECT_(上面的第 4 步和第 5 步),因此我将无法通过我分配的名称访问它。

In this case, the first RewriteRulechanges the URL, so after both RewriteRules are processed, Apache restarts the procedure and processes the .htaccessagain. The second time, the first RewriteRuleis skipped because of the RewriteConddirective, but the second RewriteRulematches, sets the environment variable (again), and, importantly, doesn't change the URL. So the request / rewriting process does not start over, and the variable name I chose sticks. In this case I actually have both REDIRECT_EFFECTIVE_DOCUMENT_ROOTand EFFECTIVE_DOCUMENT_ROOT. If I were to use an Lflag on the first RewriteRule, I'd only have EFFECTIVE_DOCUMENT_ROOT.

在这种情况下,第一个RewriteRule更改了 URL,因此在RewriteRule处理完两个s后,Apache 重新启动该过程并.htaccess再次处理 s 。第二次,第一个RewriteRule因为RewriteCond指令被跳过,但第二个RewriteRule匹配,设置环境变量(再次),重要的是,不改变 URL。所以请求/重写过程不会重新开始,我选择的变量名仍然存在。在这种情况下,我实际上同时拥有REDIRECT_EFFECTIVE_DOCUMENT_ROOTEFFECTIVE_DOCUMENT_ROOT。如果我L在第一个上使用标志RewriteRule,我只会有EFFECTIVE_DOCUMENT_ROOT.

@trowel's partial solution works similarly: the rewrite directives are processed again, the renamed variable is assigned to the original name again, and if the URL does not change, the process is over and the assigned variable name sticks.

@trowel 的部分解决方案的工作原理类似:再次处理重写指令,再次将重命名的变量分配给原始名称,如果 URL 没有更改,则该过程结束并保留分配的变量名称。

Why those techniques are inadequate

为什么这些技术是不够的

Both of those techniques suffer from a major flaw: when the rewrite rules in the .htaccessfile where you set environment variables rewrite the URL to a more deeply nested directory that has an .htaccessfile that does any rewriting, your assigned variable name is wiped out again.

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

Say you have a directory layout like this:

假设你有一个这样的目录布局:

docroot/
        .htaccess
        A.php
        B.php
        sub/
                .htaccess
                A.php
                B.php

And a docroot/.htaccesslike this:

docroot/.htaccess这样的:

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

RewriteRule .* - [E=MAJOR:flaw]

So you request /A.php, and it's rewritten to sub/B.php. You still have your MAJORvariable.

所以你请求/A.php,它被重写为sub/B.php。你仍然有你的MAJOR变量。

However, if you have any rewrite directives in docroot/sub/.htaccess(even just RewriteEngine Offor RewriteEngine On), your MAJORvariable disappears. That's because once the URL is rewritten to sub/B.php, docroot/sub/.htaccessis processed, and if it contains any rewrite directives, rewrite directives in docroot/.htaccessare not processed again. If you had a REDIRECT_MAJORafter docroot/.htaccesswas processed (e.g. if you omit the Lflag from the first RewriteRule), you'll still have it, but those directives won't run again to set your chosen variable name.

但是,如果您在docroot/sub/.htaccess(甚至只是RewriteEngine OffRewriteEngine On)中有任何重写指令,您的MAJOR变量就会消失。那是因为一旦 URL 被重写为sub/B.phpdocroot/sub/.htaccess就会被处理,如果它包含任何重写指令,docroot/.htaccess则不会再次处理 中的重写指令。如果您有一个REDIRECT_MAJORafterdocroot/.htaccess被处理(例如,如果您L从 first省略标志RewriteRule),您仍然会拥有它,但这些指令不会再次运行以设置您选择的变量名称。

Inheritance

遗产

So, say you want to:

所以,假设你想:

  1. set environment variables in RewriteRuledirectives at a particular level of the directory tree (like docroot/.htaccess)

  2. have them available in scripts at deeper levels

  3. have them available with the assigned names

  4. be able to have rewrite directives in more deeply nested .htaccessfiles

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

  2. 让它们在更深层次的脚本中可用

  3. 让它们以指定的名称可用

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

A possible solution is to use RewriteOptions inheritdirectives in the more deeply nested .htaccessfiles. That allows you to re-run the rewrite directives in less deeply nested files and use the techniques outlined above to set the variables with the chosen names. However, note that this increases complexity because you have to be more careful crafting the rewrite directives in the less deeply nested files so that they don't cause problems when run again from the more deeply nested directories. I believe Apache strips the per-dir prefix for the more deeply nested directory and runs the rewrite directives in the less deeply nested files on that value.

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

@trowel's technique

@trowel 的技术

As far as I can see, support for using a construct like %{ENV:REDIRECT_VAR}in the value component of a RewriteRuleEflag (e.g. [E=VAR:%{ENV:REDIRECT_VAR}]) does not appear to be documented:

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

VAL may contain backreferences ($N or %N) which will be expanded.

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

It does appear to work, but if you want to avoid relying on something undocumented (please correct me if I'm wrong about that), it can easily be done this way instead:

它似乎确实有效,但是如果您想避免依赖未记录的内容(如果我错了,请纠正我),可以通过以下方式轻松完成:

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

SetEnvIf

设置环境

I don't recommend relying on this, because it doesn't seem to be consistent with the documented behavior(see below), but this (in docroot/.htaccess, with Apache 2.2.20) works for me:

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

SetEnvIf REDIRECT_VAR (.+) VAR=

Only those environment variables defined by earlier SetEnvIf[NoCase] directives are available for testing in this manner.

只有那些由较早的 SetEnvIf[NoCase] 指令定义的环境变量可用于以这种方式进行测试。

Why?

为什么?

I don't know what the rationale for prefixing these names with REDIRECT_is -- not surprising, since it doesn't appear to be mentioned in the Apache documentation sections for mod_rewrite directives, RewriteRuleflags, or environment variables.

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

At the moment it seems like a big nuisance to me, in the absence of an explanation for why it's better than leaving the assigned names alone. The lack of documentation only contributes to my skepticism about it.

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

Being able to assign environment variables in rewrite rules is useful, or at least, it would be. But the usefulness is greatly diminished by this name-changing behavior. The complexity of this post illustrates how nuts this behavior and the hoops that have to be jumped through to try to overcome it are.

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

回答by thetaiko

I haven't tested this at all and I know it doesn't address points A or B, but there is some description of this issue in the comments in PHP documentation and some possible solutions for accessing these variables using $_SERVER['VAR']:

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

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

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

EDIT- some more responses to the question offered:

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

A:The environment variables are renamed by Apache if they are involved in a redirect. For example, if you have the following rule:

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

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

Then you may access VAR1 and VAR2 using $_SERVER['VAR1']and $_SERVER['VAR2']. However, if you redirect the page like so:

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

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

Then you must use $_SERVER['REDIRECT_VAR1'], etc.

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

B:The best way to overcome this issue is to process the variables that you're interested in using PHP. Create a function that runs through the $_SERVERarray and finds the items that you need. You might even use a function like this:

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;
}

回答by Bramus

As I don't want to change any of my code (nor can I change code of the libraries used), I went with the following approach: whilst bootstrapping my application – e.g. in my index.php– I rework the $_ENVsuperglobal so that variables prefixed with REDIRECT_are rewritten to their normal intended name:

由于我不想更改我的任何代码也不能更改所用库的代码),我采用了以下方法:在引导我的应用程序时——例如在我的应用程序中index.php——我修改了$_ENV超全局变量,以便前缀REDIRECT_为改写为他们正常的预期名称:

// Fix ENV vars getting prepended with `REDIRECT_` by Apache
foreach ($_ENV as $key => $value) {
    if (substr($key, 0, 9) === 'REDIRECT_') {
        $_ENV[str_replace('REDIRECT_', '', $key)] = $value;
        putenv(str_replace('REDIRECT_', '', $key) . '=' . $value);
    }
}

Not only do we directly set it in $_ENV, but we also store it using putenv(). This way existing code and libraries – which might use getenv()– can work fine.

我们不仅直接将其设置在 中$_ENV,而且还使用putenv(). 这样现有的代码和库(可能会使用getenv())可以正常工作。



On a sidenote: if you're extracting headers – like HTTP_AUTHORIZATION– in your code, you need to do the same kind of manipulation on $_SERVER:

旁注:如果您HTTP_AUTHORIZATION在代码中提取标头(例如),则需要对 进行相同类型的操作$_SERVER

foreach ($_SERVER as $key => $value) {
    if (substr($key, 0, 9) === 'REDIRECT_') {
        $_SERVER[str_replace('REDIRECT_', '', $key)] = $value;
    }
}