Удалите расширение из URL-адреса с помощью перезаписи, не приводя к циклу перенаправления


На сервере у меня есть файл (в файловой системе) с именем page.html, к которому я хочу получить доступ как site.com/page, Поэтому, если кто-то перейдет к site.com/page.html, он должен 301 перенаправить на site.com/page

Я видел правила перезаписи, которые будут обрабатывать перезапись /page -> /page.html внутренне, но заставляя его 301 перенаправлять /page.html -> /page также вызывает цикл перенаправления для меня.

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

Я также пробовал использовать ENV следующим образом:

RewriteRule ^page$ /page.html [L,E=END:1]
RewriteCond %{ENV:END} !1
RewriteRule ^page.html$ /page [R=301,L]

Но это также приводит к циклу перенаправления.

Author: MrWhite, 2014-08-29

3 answers

Я задал этот тот же вопрос о стековом потоке. Чтобы заставить его работать должным образом, вы должны использовать переменные среды:

RewriteRule ^page$ /page.html [L,E=LOOP:1]
RewriteCond %{ENV:REDIRECT_LOOP} !1
RewriteRule ^page.html$ /page [R=301,L]

Это связано с тем, что mod_rewrite выполняет несколько проходов через ваши правила. Во время первого прохода он устанавливает переменную среды. Во время второго прохода он добавляет к переменной префикс REDIRECT_, поэтому вы должны прочитать его как REDIRECT_LOOP.

 3
Author: Stephen Ostermiller, 2014-08-29 13:38:36

Проблема в том, что при использовании mod_rewrite в файле .htaccess или разделе каждый успешный перезаписывающий файл - даже внутренний - вызывает внутренний перезапуск запроса, и, таким образом, весь набор правил перезаписи должен быть переработан.

Таким образом, происходит то, что, когда пользователь посещает /page, ваш внутренний перезаписывающий файл совпадает и переписывает URL-адрес на /page.html. Но это заставляет Apache перезапустить обработку запросов и снова запустить ваш набор правил, в результате чего внешнее правило перезаписи для соответствия и запуска 301 перенаправления обратно на /page.

Быстрый и грязный (но эффективный!) исправление заключается в том, чтобы ваше внутреннее правило перезаписи добавляло фиктивный параметр, такой как redirect=no, к URL-адресу и проверяло этот параметр во внешнем правиле перезаписи. Вот пример, основанный на этом ответе, который я написал для аналогичного вопроса о переполнении стека:

RewriteEngine On
RewriteBase /

# Externally rewrite page.html -> page, unless query includes redirect=no:
RewriteCond %{QUERY_STRING} !(^|&)redirect=no(&|$) 
RewriteRule ^(page)\.html$ /$1  [NS,R=301,L] 

# Internally rewrite page -> page.html, add redirect=no to query:
RewriteRule ^(page)$ $1.html?redirect=no [NS,QSA]

(Конечно, не стесняйтесь заменять redirect=no чем-то другим, если это противоречит фактическому URL-адресу параметр, который вы могли бы использовать.)

 3
Author: Ilmari Karonen, 2017-05-23 12:37:06

Распространенный способ предотвратить цикл перенаправления - проверить переменную сервера THE_REQUEST, которая содержит начальный заголовок запроса, а не URL-путь переписанного URL-адреса (который RewriteRule шаблон совпадает с), который, естественно, обновляется по мере перезаписи URL-адреса.

THE_REQUEST значение не меняется, так как URL-адрес внутренне переписан и содержит строку вида:

GET /page.html HTTP/1.1

Так что это можно было бы переписать как:

# Redirect direct requests for /page.html to the canonical URL
RewriteCond %{THE_REQUEST} \.html
RewriteRule ^(page)\.html$ /$1 [R=301,L]

# Rewrite the "pretty" URL to the actual filesystem path
RewriteRule ^(page)$ $1.html [L]

В этом случай, условие THE_REQUEST просто гарантирует, что .html присутствует в первоначально запрошенном URL-адресе. Проверка на page.html оставлена на усмотрение RewriteRule шаблон - который более эффективен (так как это проверяется первым).

Всегда предпочтительнее иметь любые внешние перенаправления перед внутренней переписью.

 0
Author: MrWhite, 2016-08-30 12:24:14