Ответ 1
Такое поведение является неудачным и даже не документируется.
.htaccess per-dir context
Вот что происходит в контексте .htaccess
per-directory (per-dir):
Предположим, что Apache обрабатывает файл .htaccess
, который включает в себя директивы rewrite.
-
Apache заполняет карту переменных среды со всеми стандартными переменными CGI/Apache
-
Переписывание начинается
-
Переменные среды задаются в директивах
RewriteRule
-
Когда Apache перестает обрабатывать директивы
RewriteRule
(из-за флагаL
или конца набора правил), а URL-адрес был изменен наRewriteRule
, Apache перезапускает обработку запроса.Если вы не знакомы с этой частью, см. Документацию
L
:таким образом, набор правил может снова запускаться с самого начала. Чаще всего это произойдет, если одно из правил вызывает перенаправление - внутреннее или внешнее - заставляя процесс запроса начать работу.
-
Из того, что я могу наблюдать, я считаю, что когда # 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-адрес, поэтому после обработки обоих RewriteRule
Apache перезапускает процедуру и снова обрабатывает .htaccess
. Во второй раз первый RewriteRule
пропускается из-за директивы RewriteCond
, но второй RewriteRule
соответствует, устанавливает переменную среды (снова) и, что важно, не изменяет URL. Таким образом, процесс запроса/перезаписи не начинается, а имя переменной я выбрал. В этом случае у меня есть как REDIRECT_EFFECTIVE_DOCUMENT_ROOT
, так и EFFECTIVE_DOCUMENT_ROOT
. Если бы я использовал флаг L
в первом RewriteRule
, у меня был бы только EFFECTIVE_DOCUMENT_ROOT
.
@trowel работает аналогично: директивы rewrite обрабатываются снова, переименованная переменная снова присваивается исходному имени, и если URL-адрес не изменяется, процесс завершается, и имя назначенной переменной сохраняется.
Почему эти методы неадекватны
Оба этих метода страдают от серьезного недостатка: когда правила перезаписи в файле .htaccess
, где вы устанавливаете переменные среды, переписываете URL-адрес в более глубоко вложенный каталог с файлом .htaccess
, который выполняет любую переписывание, ваш имя назначенной переменной снова уничтожается.
Предположим, что у вас есть такой макет каталога:
docroot/
.htaccess
A.php
B.php
sub/
.htaccess
A.php
B.php
И a docroot/.htaccess
следующим образом:
RewriteRule ^A\.php sub/B.php [L]
RewriteRule .* - [E=MAJOR:flaw]
Итак, вы запрашиваете /A.php
и переписываете его на sub/B.php
. У вас все еще есть переменная MAJOR
.
Однако, если у вас есть какие-либо директивы rewrite в docroot/sub/.htaccess
(даже просто RewriteEngine Off
или RewriteEngine On
), ваша переменная MAJOR
исчезает. Это потому, что, как только URL-адрес переписан на sub/B.php
, обрабатывается docroot/sub/.htaccess
, и если он содержит какие-либо директивы rewrite, переписывать директивы в docroot/.htaccess
не обрабатываются снова. Если у вас был REDIRECT_MAJOR
после обработки docroot/.htaccess
(например, если вы опускаете флаг L
из первого RewriteRule
), вы все равно будете иметь его, но эти директивы не будут выполняться снова, чтобы установить выбранный вами имя переменной.
Наследование
Итак, скажите, что вы хотите:
-
задайте переменные окружения в директивах
RewriteRule
на определенном уровне дерева каталогов (например,docroot/.htaccess
) -
иметь их доступными в сценариях на более глубоких уровнях
-
иметь их доступными с назначенными именами
-
иметь возможность переписывать директивы в более глубоко вложенных файлах
.htaccess
Возможным решением является использование директив RewriteOptions inherit
в более глубоко вложенных файлах .htaccess
. Это позволяет повторно запускать директивы rewrite в менее глубоко вложенных файлах и использовать описанные выше методы для установки переменных с выбранными именами. Однако обратите внимание, что это увеличивает сложность, потому что вам нужно быть более осторожным при разработке директив перезаписи в менее глубоко вложенных файлах, чтобы они не вызывали проблем при повторном запуске из более глубоко вложенных каталогов. Я считаю, Apache разбивает предикс per-dir для более глубоко вложенного каталога и запускает директивы rewrite в менее глубоко вложенных файлах по этому значению.
@технология
Насколько я понимаю, поддержка использования такой конструкции, как %{ENV:REDIRECT_VAR}
в компоненте значения RewriteRule
E
(например, [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_
- не удивительно, так как оно не упоминается в разделах документации Apache для директивы mod_rewrite, RewriteRule
flags или переменные окружения.
В настоящий момент это кажется большой неприятностью для меня, в отсутствие объяснения, почему это лучше, чем оставить назначенные имена в одиночку. Отсутствие документации только способствует моему скептицизму.
Возможность присвоить переменные среды в правилах перезаписи полезна или, по крайней мере, будет. Но полезность значительно уменьшена этим изменением названия. Сложность этого сообщения иллюстрирует, как гайки этого поведения и обручи, которые нужно перепрыгнуть, чтобы попытаться преодолеть это.