#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
заголовка (поэтому перенаправления нет).