Почему этот флаг [L] не может предотвратить применение следующего правила перезаписи?


Я думал, что флаг " L " должен был предотвратить применение последующих правил. Однако в этом примере:

RewriteRule foo bar [L]
RewriteRule bar qux

Я получаю http://mysite/foo переписанный как http://mysite/qux. Я ожидал http://mysite/bar. Чего мне здесь не хватает?

2 2

2 ответа:

Поведение флага [L] довольно плохо описано в документах Apache. Чтобы понять, как это работает, нам сначала нужно знать, как Apache обрабатывает RewriteRules. Возьмем простой пример

RewriteRule ^something /somethingelse #1
RewriteRule ^somewhere /somewhereelse #2 
RewriteRule ^someplace /anotherplace  #3

В этой ситуации с несколькими правилами и без флагов [L], если мы запросим /something, Apache перепишет это /somethingelse (согласно #1), затем попробуйте правила #2 и #3. После того, как все правила обработаны, он проверяет, совпадает ли URL, вышедший из RewriteRules, с URL, который заниматься. Если это не так, Apache снова начинает обрабатывать все правила, пока input= = = output (или максимальное число перенаправлений не будет выполнено, чтобы предотвратить бесконечные циклы).

Теперь, если мы изменим Правило #1 и добавим к нему [L], и мы снова запросим /something, Apache перепишет его в /somethingelse (согласно #1), а затем прекратит обработку правил, т. е. он не будет обрабатывать #2 и #3. Но затем, поскольку URL-адрес, который вышел, не совпадает с URL-адресом, который вошел (это суть здесь), обработка перезапускает , и правила #2 и #3 будут обработаны в любом случае (и #1 тоже, но больше ничего не делает).

В вашем примере, если вы хотите предотвратить перенаправление /bar на /qux, когда он был переписан первым RewriteRule, вы можете использовать

RewriteRule foo bar [L]
RewriteCond %{THE_REQUEST} ^GET\ /bar
RewriteRule bar qux

Который перепишет /bar на /qux только , если пользователь специально запросил /bar, а не если URL был переписан с /foo на /bar сначала.

Фокус здесь в том, что %{THE_REQUEST} содержит точный заголовок HTTP, который был использован для запроса, и не изменяется при перезаписи URL, поэтому с помощью этой переменной вы всегда можете проверить, для чего исходил запрос (в отличие от %{REQUEST_URI}, который меняется при каждой перезаписи).

Из mod_rewrite введение :

Обязательно настройте уровень журнала mod_rewrite на один из уровней трассировки уровни, использующие директивуLogLevel . Он незаменим в проблемы отладки с конфигурацией mod_rewrite, так как она расскажет вы точно знаете, как обрабатывается каждое правило.

Для включения трассировки mod_rewrite в Apache 2.4 можно использовать

LogLevel info mod_rewrite:trace3

Однако в Apache 2.2 (который является текущим фаворитом для производства) вы придется использовать

RewriteLog "/var/log/apache2/rewrite.log"
RewriteLogLevel 3

Ваша конфигурация

RewriteRule foo bar [L]
RewriteRule bar qux

Проблематично, потому что он должен быть запущен только один раз, чтобы работать так, как вы хотите. Флаг L в 2.2 не делает того, что вы думаете, когда правило используется в Каталоге или .контекст htaccess.

Если вы используете RewriteRule в любом из них .файлы htaccess или в разделы, важно иметь некоторое понимание как обрабатываются правила. Упрощенная форма этого заключается в том, что когда-то правила были обработаны, то переписанный запрос передается обратно механизм синтаксического анализа URL, чтобы сделать то, что он может с ним. Вполне возможно, что поскольку переписанный запрос обрабатывается, то .файл htaccess или раздел может быть обнаружен снова, и таким образом набор правил может быть запущен опять с самого начала. Чаще всего это произойдет, если один из правила вызывают перенаправление-либо внутреннее , либо внешнее, - вызывающее запросите процесс, чтобы начать все сначала.

Однако перемещение этих правил в конфигурацию службы или виртуальный хост контекст действительно работает. (Проверено на моем локальном сервере.)

В общем случае вы хотите написать правила, так что не имеет значения, сколько раз они обрабатываются. Если вы абсолютно не можете этого сделать, мне все еще не нравится использовать RewriteCond %{THE_REQUEST} ^GET\ /bar, чтобы избежать зацикливания или даже RewriteCond %{THE_REQUEST} ^[^ ]+ /bar, но у меня нет предложения на данный момент для Apache 2.2.

Apache 2.4 имеет флагEND как раз для этой ситуации.