Удалите расширение из 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]
Но это также приводит к циклу перенаправления.
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
.
Проблема в том, что при использовании 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-адресу параметр, который вы могли бы использовать.)
Распространенный способ предотвратить цикл перенаправления - проверить переменную сервера 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
шаблон - который более эффективен (так как это проверяется первым).
Всегда предпочтительнее иметь любые внешние перенаправления перед внутренней переписью.