web-dev-qa-db-ja.com

Apache RewriteRuleディレクティブで環境変数を設定する場合、変数名の先頭に「REDIRECT_」が付加される原因は何ですか?

.htaccessファイルのRewriteRuleルールで[E=VAR:VAL]フラグを使用して(PHPで使用するために)Apache環境変数を設定しようとしています。

変数はPHPサーバー変数として$_SERVERではなく$_ENV(ある程度の意味はあります)]でアクセスされることを既に発見しています。しかし、私の問題はいくつかのルールは[E=VAR:VAL]フラグが期待どおりに動作し、変数$_SERVER['VAR']で終わりますが、他のルールでは変数$_SERVER['REDIRECT_VAR']または$_SERVER['REDIRECT_REDIRECT_VAR']などで終わります

A. Apacheで[E=VAR:VAL]フラグを使用して設定された環境変数が、変数名の前に「REDIRECT_」を追加することで名前が変更される原因は何ですか?

B.環境変数の名前が変更されていないことを確認して、バリエーションのチェックに頼らずにPHP as $_SERVER['VAR']]でアクセスできるようにするためにできること「REDIRECT_」のインスタンスが1つ以上付加された変数名の

部分的な解決策が見つかりました。書き換えルールの先頭に次を追加すると、必要に応じて、各リダイレクトで元のENV:VARが再作成されます(REDIRECT_VARバージョンがそのまま残ります)。

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

この動作は残念であり、文書化されていないようです。

.htaccessのディレクトリごとのコンテキスト

.htaccessディレクトリごと(ディレクトリごと)のコンテキストで発生するように見えるものは次のとおりです。

Apacheが書き換えディレクティブを含む.htaccessファイルを処理すると仮定します。

  1. Apacheは、環境変数マップにすべての標準CGI/Apache変数を取り込みます

  2. 書き換えが始まります

  3. 環境変数はRewriteRuleディレクティブで設定されます

  4. ApacheがRewriteRuleディレクティブの処理を停止すると(Lフラグまたはルールセットの最後)、RewriteRuleによってURLが変更されると、Apacheは要求処理を再開します。

    この部分に慣れていない場合は、 Lフラグのドキュメント を参照してください。

    したがって、ルールセットは最初から再度実行される場合があります。最も一般的には、ルールの1つがリダイレクト(内部または外部)を引き起こし、要求プロセスを最初からやり直す場合に発生します。
  5. 私が観察できることから、#4が発生すると#1が繰り返され、RewriteRuleディレクティブで設定された環境変数にREDIRECT_が追加され、環境変数マップ(必ずしもその順序である必要はありませんが、最終結果はその組み合わせで構成されます)。

    このステップでは、選択した変数名が消去されます。すぐに、それが非常に重要で不便な理由を説明します。

変数名の復元

もともとこの問題に遭遇したとき、.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を変更するため、両方のRewriteRulesが処理された後、Apacheはプロシージャを再起動し、.htaccessを再度処理します。 2回目は、最初のRewriteRuleRewriteCondディレクティブのためにスキップされますが、2番目のRewriteRuleは一致し、環境変数を(再び)設定し、そして重要なことにURLを変更しません。そのため、リクエスト/書き換えプロセスは最初からやり直さず、私が選んだ変数名は固執します。この場合、実際にはREDIRECT_EFFECTIVE_DOCUMENT_ROOTEFFECTIVE_DOCUMENT_ROOTの両方があります。最初のLRewriteRuleフラグを使用する場合、EFFECTIVE_DOCUMENT_ROOTしかありません。

@trowelの部分的な解決策も同様に機能します。書き換えディレクティブが再度処理され、名前が変更された変数が元の名前に再び割り当てられます。URLが変更されない場合、プロセスは終了し、割り当てられた変数名が保持されます。

なぜこれらの技術が不十分なのか

これらの手法にはどちらも大きな欠陥があります。環境変数を設定する.htaccessファイルの書き換えルールが、書き換えを行う.htaccessファイルを持つ、より深くネストされたディレクトリにURLを書き換えると、割り当てられた変数名は再び消去されます。

次のようなディレクトリレイアウトがあるとします。

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に書き換えディレクティブがある場合(RewriteEngine OffまたはRewriteEngine Onだけでも)、MAJOR変数は消えます。これは、URLがsub/B.phpに書き換えられると、docroot/sub/.htaccessが処理され、書き換えディレクティブが含まれている場合、docroot/.htaccessの書き換えディレクティブは再度処理されないためです。 REDIRECT_MAJORが処理された後にdocroot/.htaccessがあった場合(たとえば、最初のLからRewriteRuleフラグを省略した場合)、まだありますが、選択した変数名を設定するためにディレクティブが再び実行されることはありません。

継承

だから、あなたがしたいことを言う:

  1. ディレクトリツリーの特定のレベルでRewriteRuleディレクティブに環境変数を設定します(docroot/.htaccessなど)

  2. それらをより深いレベルのスクリプトで利用可能にする

  3. 割り当てられた名前で利用可能にする

  4. より深くネストされた.htaccessファイルに書き換えディレクティブを含めることができる

考えられる解決策は、より深くネストされたRewriteOptions inheritファイルで.htaccessディレクティブを使用することです。これにより、ネストの浅いファイルで書き換えディレクティブを再実行し、上記の手法を使用して、選択した名前で変数を設定できます。ただし、より深くネストされたディレクトリから再度実行したときに問題が発生しないように、より深くネストされていないファイルの書き換えディレクティブをより慎重に作成する必要があるため、これにより複雑さが増します。 Apacheは、より深くネストされたディレクトリのディレクトリごとのプレフィックスを削除し、その値でより深くネストされていないファイルで書き換えディレクティブを実行すると信じています。

@こてのテクニック

私が見る限り、RewriteRuleEフラグ(たとえば、%{ENV:REDIRECT_VAR})の値コンポーネントで[E=VAR:%{ENV:REDIRECT_VAR}]のような構造を使用するためのサポートはそうではないようです。 文書化

VALには、展開される後方参照($ Nまたは%N)が含まれる場合があります。

それは動作しているように見えますが、文書化されていない何かに依存することを避けたい場合(それについて間違っている場合は私を修正してください)、代わりにこの方法で簡単に行うことができます:

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

SetEnvIf

文書化された動作 (以下を参照)と一貫していないように見えるため、これに依存することはお勧めしませんが、これは(docroot/.htaccessで、Apache 2.2.20で)私のために働く:

SetEnvIf REDIRECT_VAR (.+) VAR=$1

この方法でテストできるのは、以前のSetEnvIf [NoCase]ディレクティブで定義された環境変数のみです。

どうして?

これらの名前の前にREDIRECT_を付ける理由はわかりません。 mod_rewrite directives のApacheドキュメントセクションで言及されていないので、驚くことではありません。 RewriteRule flags 、または 環境変数

現時点では、割り当てられた名前をそのままにしておくよりも良い理由についての説明がないので、私には大きな迷惑のようです。ドキュメントの欠如は、それについての私の懐疑にのみ貢献します。

書き換えルールで環境変数を割り当てることができると便利です。または、少なくともそうするでしょう。しかし、この名前を変更する動作により、有用性は大幅に低下します。この投稿の複雑さは、この振る舞いとそれを乗り越えようとする際に乗り越えなければならないフープがいかに重要であるかを示しています。

72
JMM

私はこれをまったくテストしておらず、ポイントAまたはBに対応していないことを知っていますが、PHPドキュメントおよびアクセスするためのいくつかの可能なソリューションのコメントにこの問題の説明があります$_SERVER['VAR']を使用したこれらの変数:

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

[〜#〜] edit [〜#〜]-提示された質問に対するいくつかの回答:

A:環境変数は、リダイレクトに関係している場合、Apacheによって名前が変更されます。たとえば、次のルールがある場合:

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

その後、$_SERVER['VAR1']$_SERVER['VAR2']を使用してVAR1とVAR2にアクセスできます。ただし、次のようにページをリダイレクトする場合:

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

次に、$_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;
}
8
thetaiko

コードを変更したくないので(使用するライブラリのコードを変更することもできません)、次のアプローチを取りました。 _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);
    }
}
_

_$_ENV_に直接設定するだけでなく、putenv()を使用して保存します。このようにして、既存のコードとライブラリ(getenv()を使用する場合があります)は正常に機能します。


補足:コード内でヘッダーを抽出する場合(_HTTP_AUTHORIZATION_など)、_$_SERVER_に対して同じ種類の操作を行う必要があります。

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