Перенаправление [R = 301] завершает следующие итерации в .htaccess?

#regex #apache #.htaccess #mod-rewrite

#регулярное выражение #apache #.htaccess #модификация-перезапись

Вопрос:

У меня есть этот простой .htaccess:

 RewriteEngine On

RewriteBase /

RewriteRule ^foo3$ foo2
RewriteRule ^foo2$ foo1
RewriteRule ^foo1$ index.php
  

Когда я ввожу URL https://localhost/foo3 , он следует за перезаписанной «цепочкой» и заканчивается в index.php. Все в порядке.

Но для этого .htaccess:

 RewriteEngine On

RewriteBase /

RewriteRule ^foo3$ foo2 [R=301]
RewriteRule ^foo2$ foo1
RewriteRule ^foo1$ index.php
  

я ожидал, что это каскадно снизится до index.php в любом случае (игнорируя перенаправление), но браузер сначала перенаправляет 301 на https://localhost/foo2 (браузер отражает этот URL), а затем .htaccess «продолжается» оттуда до index.php.

R=301 Действует ли как [END] директива в этой ситуации каким-либо образом?

Ответ №1:

R=301 Действует ли как [END] директива в этой ситуации каким-либо образом?

«Нет» END не действует, но L (последнее) действует. Пожалуйста, поймите, что END поведение отличается от L .

Следовательно, Apache отправляет 301 в браузер, и браузер выполняет полное перенаправление, затем он снова отправляет /foo2 на веб-сервер, а затем выполняются 2-е и 3-е правила.


Обновить:

Благодаря комментарию от @MrWhite ниже, я выполнил этот тест, сохраняя журналы перезаписи trace4 .

Настройте, чтобы включить ведение журнала перезаписи:

 LogLevel info rewrite:trace4
  

И вот дополнительные журналы, которые пришли в мои журналы ошибок Apache. Пожалуйста, обратите внимание, что мое локальное имя хоста — localhost а мое DocumentRoot /www/root .

Шаг: Применяя правило № 1, URI является /foo3 :

 strip per-dir prefix: /www/root/foo3 -> foo3
applying pattern '^foo3$' to uri 'foo3'
rewrite 'foo3' -> 'foo2'
add per-dir prefix: foo2 -> /www/root/foo2
explicitly forcing redirect with http://localhost/www/root/foo2
  

Шаг: Применяя правило № 2, URI является http://localhost/www/root/foo2 (обратите внимание, что ваше RewriteBase / еще не вступило в силу):

 applying pattern '^foo2$' to uri 'http://localhost/www/root/foo2'
  

Шаг: Применяя правило # 3, URI является http://localhost/www/root/foo2 :

 applying pattern '^foo1$' to uri 'http://localhost/www/root/foo2'
  

Шаг: Применяем внешнее перенаправление 301 путем удаления DocumentRoot вашего RewriteBase значения:

 trying to replace prefix /www/root/ with /
add subst prefix: foo2 -> foo2
escaping http://localhost/foo2 for redirect
redirect to http://localhost/foo2 [REDIRECT/301]
  

Шаг: Применяя правило № 1, URI является /foo2 :

 strip per-dir prefix: /www/root/foo2 -> foo2
applying pattern '^foo3$' to uri 'foo2'
  

Шаг: Применяя правило № 2, URI является /foo2 :

 strip per-dir prefix: /www/root/foo2 -> foo2
applying pattern '^foo2$' to uri 'foo2'
rewrite 'foo2' -> 'foo1'
  

Шаг: Применяя правило # 3, URI является /foo1 :

 add per-dir prefix: foo1 -> /www/root/foo1
strip per-dir prefix: /www/root/foo1 -> foo1
applying pattern '^foo1$' to uri 'foo1'
rewrite 'foo1' -> 'index.php'
  

Шаг: Внутренняя перезапись в /index.php :

 add per-dir prefix: index.php -> /www/root/index.php
trying to replace prefix /www/root/ with /
add subst prefix: index.php -> /index.php
internal redirect with /index.php [INTERNAL REDIRECT]
  

Шаг: Применяя правило № 1, URI является /index.php :

 strip per-dir prefix: /www/root/index.php -> index.php
applying pattern '^foo3$' to uri 'index.php'
  

Шаг: Применяя правило № 2, URI является /index.php :

 strip per-dir prefix: /www/root/index.php -> index.php
applying pattern '^foo2$' to uri 'index.php'
  

Шаг: Применяя правило # 3, URI является /index.php :

 strip per-dir prefix: /www/root/index.php -> index.php
applying pattern '^foo1$' to uri 'index.php'
  

Шаг: Остановить обработку правила, URI является /index.php :

 pass through /www/root/index.php
  

Я добавил подробное протоколирование трассировки и мой step комментарий выше, чтобы показать, как L возникает побочный эффект, при котором обработка правил не полностью прекращается. Однако тот факт, что mod_rewrite движки пытаются применить оставшиеся правила к URI, который недействителен, т. е. http://localhost/www/root/foo2 эти правила не будут выполняться, и результатом будет полное перенаправление.

Комментарии:

1. Но почему он отправляет 301? Почему оно не обрабатывает 2-е и 3-е правила на первой итерации, «перезаписывая» первое правило (как в первом примере)?

2. Если я правильно понимаю выполнение .ht , когда обнаруживается L флаг и происходит перезапись, .ht перезапускается с самого начала с этой перезаписью в качестве «запрошенного uri». Если это правильно, то при вычислении R=301 (плюс неявное L ) правила больше не обрабатываются, и вторая итерация должна начинаться с foo2 в качестве входных данных. На этой итерации будет применяться 2-е правило, за которым следует 1-е правило (нет L во 2-м правиле). Мы достигли EOF и произошла перезапись, поэтому третья итерация начинается с index.php в качестве входных данных. Перезаписей не происходит, поэтому index.php должен быть ресурс для получения. Это точно?

3. Вы действительно хорошо сформулировали это, это абсолютно правильно.

4. Да, это правильно. Поскольку полное перенаправление с R является результатом первого запуска, apache должен отправить этот статус обратно в браузер. Когда браузер отправляет следующий запрос с новым URL, т.е. /foo2 он снова обрабатывает .htaccess сверху.

5. «Когда вы используете R флаг, тогда L это неявно» — это не совсем верно. L Флаг подразумевается только для кодов ответа, отличных от 3xx. В приведенном выше случае обработка продолжается , за исключением того, что следующее правило просто не соответствует, потому что R=301 флаг заставляет механизм перезаписи создавать абсолютный URL (своего рода), который передается следующему правилу. Удалите начальную привязку ( ^ ) во 2-м правиле, и оно действительно совпадает, что в конечном итоге приводит к немного искаженному ответу… он внутренне перезаписывает в index.php , но отправляет код ответа 301 без Location заголовка (поэтому перенаправления нет).