Перенаправление и переадресация с помощью mod_rewrite
Этот документ дополняет mod_rewrite справочную документацию. В нём описывается, как использовать mod_rewrite для перенаправления и переадресации запросов. Здесь приведено множество примеров распространённых способов применения mod_rewrite, включая подробное описание работы каждого из них.
От старого к новому (внутреннее перенаправление)
- Описание:
-
Предположим, мы недавно переименовали страницу
foo.htmlвbar.htmlи теперь хотим сохранить старый URL для обратной совместимости. При этом мы хотим, чтобы пользователи старого URL даже не заметили, что страница была переименована, то есть адрес в их браузере не должен измениться. - Решение:
-
Мы внутренне переадресуем старый URL на новый с помощью следующего правила:
RewriteEngine on RewriteRule "^/foo\.html$" "/bar.html" [PT]
Переадресация со старого URL на новый (внешнее перенаправление)
- Описание:
-
Предположим, мы снова недавно переименовали страницу
foo.htmlвbar.htmlи теперь хотим сохранить старый URL для обратной совместимости. Но на этот раз мы хотим сообщить пользователям старого URL о новом адресе, то есть поле Location в их браузерах тоже должно измениться. - Решение:
-
Мы принудительно перенаправляем запрос HTTP на новый URL, в результате чего адрес в браузере изменится и пользователь увидит новый адрес:
RewriteEngine on RewriteRule "^/foo\.html$" "bar.html" [R]
- Обсуждение
-
В этом примере, в отличие от приведённого выше внутреннего примера, можно просто использовать директиву Redirect. В предыдущем примере применялся mod_rewrite, чтобы скрыть перенаправление от клиента:
Redirect "/foo.html" "/bar.html"
Ресурс перемещён на другой сервер
- Описание:
-
Если ресурс перемещён на другой сервер, вы можете некоторое время поддерживать работу URL на старом сервере, пока пользователи обновляют свои закладки.
- Решение:
-
Для перенаправления этих URL на новый сервер можно использовать
mod_rewrite, но можно также рассмотреть директиву Redirect или RedirectMatch.#With mod_rewrite RewriteEngine on RewriteRule "^/docs/(.+)" "http://new.example.com/docs/$1" [R,L]
#With RedirectMatch RedirectMatch "^/docs/(.*)" "http://new.example.com/docs/$1"
#With Redirect Redirect "/docs/" "http://new.example.com/docs/"
От статического к динамическому
- Описание:
-
Как преобразовать статическую страницу
foo.htmlв динамический вариантfoo.cgiнезаметно для браузера и пользователя? - Решение:
-
Достаточно переписать URL на CGI-скрипт и принудительно назначить обработчиком cgi-script, чтобы он выполнялся как CGI-программа. Таким образом, запрос к
/~quux/foo.htmlвнутренне приводит к запуску/~quux/foo.cgi.RewriteEngine on RewriteBase "/~quux/" RewriteRule "^foo\.html$" "foo.cgi" [H=cgi-script]
Обратная совместимость при изменении расширения файла
- Описание:
-
Как обеспечить обратную совместимость URL (сохранить их виртуальную работоспособность) после перехода с
document.YYYYнаdocument.XXXX, например после преобразования набора файлов.htmlв.php? - Решение:
-
Мы заменяем имя на его базовую часть и проверяем, существует ли файл с новым расширением. Если он существует, используем это имя, иначе возвращаем URL к исходному виду.
# backward compatibility ruleset for # rewriting document.html to document.php # when and only when document.php exists <Directory "/var/www/htdocs"> RewriteEngine on RewriteBase "/var/www/htdocs" RewriteCond "$1.php" -f RewriteCond "$1.html" !-f RewriteRule "^(.*).html$" "$1.php" </Directory> - Обсуждение
-
В этом примере используется часто упускаемая из виду особенность mod_rewrite: порядок выполнения набора правил. В частности, mod_rewrite вычисляет левую часть RewriteRule до вычисления директив RewriteCond. Поэтому к моменту вычисления директив RewriteCond значение $1 уже определено. Это позволяет проверить наличие исходного (
document.html) и целевого (document.php) файлов, используя одно и то же базовое имя.Этот набор правил предназначен для использования в контексте каталога (в блоке <Directory> или в файле .htaccess), чтобы проверки
-fвыполнялись для правильного пути к каталогу. Возможно, потребуется задать директивуRewriteBase, чтобы указать базовый каталог, с которым ведётся работа.
Канонические имена хостов
- Описание:
- Цель этого правила — принудительно использовать определённое имя хоста вместо других имён, по которым можно обратиться к тому же сайту. Например, если нужно использовать www.example.com вместо example.com, можно применить один из вариантов следующей конфигурации.
- Решение:
-
Лучше всего решить эту задачу вообще без mod_rewrite, используя директиву
Redirectв виртуальном хосте для неканонического имени (или имён) хоста.<VirtualHost *:80> ServerName undesired.example.com ServerAlias example.com notthis.example.com Redirect "/" "http://www.example.com/" </VirtualHost> <VirtualHost *:80> ServerName www.example.com </VirtualHost>
В качестве альтернативы это можно сделать с помощью директивы
<If>:<If "%{HTTP_HOST} != 'www.example.com'"> Redirect "/" "http://www.example.com/" </If>Например, чтобы перенаправить часть сайта на HTTPS, можно сделать следующее:
<If "%{SERVER_PROTOCOL} != 'HTTPS'"> Redirect "/admin/" "https://www.example.com/admin/" </If>Если по какой-либо причине вы всё же хотите использовать
mod_rewrite— например, если это необходимо для работы с большим набором правил RewriteRule, — можно воспользоваться одним из приведённых ниже вариантов.Для сайтов, работающих на порту, отличном от 80:
RewriteCond "%{HTTP_HOST}" "!^www\.example\.com" [NC] RewriteCond "%{HTTP_HOST}" "!^$" RewriteCond "%{SERVER_PORT}" "!^80$" RewriteRule "^/?(.*)" "http://www.example.com:%{SERVER_PORT}/$1" [L,R,NE]А для сайта, работающего на порту 80:
RewriteCond "%{HTTP_HOST}" "!^www\.example\.com" [NC] RewriteCond "%{HTTP_HOST}" "!^$" RewriteRule "^/?(.*)" "http://www.example.com/$1" [L,R,NE]Если нужно сделать это универсально для всех доменных имён, то есть перенаправлять example.com на www.example.com для любого значения example.com, можно использовать следующий вариант:
RewriteCond "%{HTTP_HOST}" "!^www\." [NC] RewriteCond "%{HTTP_HOST}" "!^$" RewriteRule "^/?(.*)" "http://www.%{HTTP_HOST}/$1" [L,R,NE]Эти наборы правил будут работать как в основном файле конфигурации сервера, так и в файле
.htaccess, размещённом вDocumentRootсервера.
Поиск страниц в нескольких каталогах
- Описание:
-
Определённый ресурс может находиться в одном из нескольких мест, и при запросе ресурса мы хотим искать его во всех этих местах. Возможно, мы недавно изменили структуру каталогов, распределив содержимое по нескольким расположениям.
- Решение:
-
Следующий набор правил ищет ресурс в двух каталогах, а если не находит его ни в одном из них, пытается отдать его из запрошенного расположения.
RewriteEngine on # first try to find it in dir1/... # ...and if found stop and be happy: RewriteCond "%{DOCUMENT_ROOT}/dir1/%{REQUEST_URI}" -f RewriteRule "^(.+)" "%{DOCUMENT_ROOT}/dir1/$1" [L] # second try to find it in dir2/... # ...and if found stop and be happy: RewriteCond "%{DOCUMENT_ROOT}/dir2/%{REQUEST_URI}" -f RewriteRule "^(.+)" "%{DOCUMENT_ROOT}/dir2/$1" [L] # else go on for other Alias or ScriptAlias directives, # etc. RewriteRule "^" "-" [PT]
Перенаправление на географически распределённые серверы
- Описание:
-
У нас есть несколько зеркал сайта, и мы хотим перенаправлять пользователей на зеркало, расположенное в их стране.
- Решение:
-
По имени хоста клиента, отправившего запрос, мы определяем, из какой страны он обращается. Если не удаётся определить страну по IP-адресу, используется сервер по умолчанию.
Для создания списка используемых серверов мы воспользуемся директивой
RewriteMap.HostnameLookups on RewriteEngine on RewriteMap multiplex "txt:/path/to/map.mirrors" RewriteCond "%{REMOTE_HOST}" "([a-z]+)$" [NC] RewriteRule "^/(.*)$" "${multiplex:%1|http://www.example.com/}$1" [R,L]## map.mirrors -- Multiplexing Map de http://www.example.de/ uk http://www.example.uk/ com http://www.example.com/ ##EOF##
- Обсуждение
- Этот набор правил предполагает, что
HostNameLookupsзаданon, что может значительно снизить производительность.Директива
RewriteCondизвлекает последнюю часть имени хоста клиента, отправившего запрос, — код страны. Следующее правило RewriteRule использует это значение, чтобы найти соответствующий сервер-зеркало в файле карты.
Содержимое, зависящее от браузера
- Описание:
-
Мы хотим предоставлять разное содержимое в зависимости от браузера или user-agent, отправившего запрос.
- Решение:
-
Нам нужно определить по HTTP-заголовку "User-Agent", какое содержимое отдавать. Следующая конфигурация работает так: если HTTP-заголовок "User-Agent" содержит "Mozilla/3", страница
foo.htmlпереписывается вfoo.NS.html, после чего переписывание прекращается. Если браузер — "Lynx" или "Mozilla" версии 1 или 2, URL становитсяfoo.20.html. Все остальные браузеры получают страницуfoo.32.html. Для этого используется следующий набор правил:RewriteCond "%{HTTP_USER_AGENT}" "^Mozilla/3.*" RewriteRule "^foo\.html$" "foo.NS.html" [L] RewriteCond "%{HTTP_USER_AGENT}" "^Lynx/" [OR] RewriteCond "%{HTTP_USER_AGENT}" "^Mozilla/[12]" RewriteRule "^foo\.html$" "foo.20.html" [L] RewriteRule "^foo\.html$" "foo.32.html" [L]
Канонические URL
- Описание:
-
На некоторых веб-серверах у одного ресурса может быть несколько URL. Обычно есть канонические URL (которые действительно используются и распространяются) и URL, служащие лишь короткими адресами, внутренними адресами и т. п. Независимо от того, какой URL пользователь указал в запросе, в адресной строке браузера в итоге должен отображаться канонический адрес.
- Решение:
-
Мы выполняем внешнее перенаправление HTTP для всех неканонических URL, чтобы исправить адрес в браузере и во всех последующих запросах. В приведённом ниже наборе правил
/puppiesи/caninesзаменяются на канонический/dogs.RewriteRule "^/(puppies|canines)/(.*)" "/dogs/$2" [R]
- Обсуждение:
- Эту задачу лучше решать с помощью директив Redirect или RedirectMatch:
RedirectMatch "^/(puppies|canines)/(.*)" "/dogs/$2"
Перемещённый DocumentRoot
- Описание:
-
Обычно
DocumentRootвеб-сервера напрямую соответствует URL "/". Однако зачастую эти данные не имеют первостепенного значения. Например, вы можете захотеть, чтобы при первом посещении сайта пользователь попадал в определённый подкаталог/about/. Этого можно добиться с помощью следующего набора правил: - Решение:
-
Мы перенаправляем URL
/на/about/:RewriteEngine on RewriteRule "^/$" "/about/" [R]
Обратите внимание, что эту задачу также можно решить с помощью директивы
RedirectMatch:RedirectMatch "^/$" "http://example.com/about/"
Также обратите внимание, что пример переписывает только корневой URL. То есть он переписывает запрос к
http://example.com/, но не запрос кhttp://example.com/page.html. Если вы действительно изменили корневой каталог документов, то есть всё содержимое находится в этом подкаталоге, гораздо лучше просто изменить директивуDocumentRootили переместить всё содержимое на один уровень выше, а не переписывать URL.
Резервный ресурс
- Описание:
- Вы хотите, чтобы один ресурс (например, определённый файл вроде index.php) обрабатывал все запросы к конкретному каталогу, кроме тех, которые должны обращаться к существующему ресурсу, например изображению или файлу CSS.
- Решение:
-
Начиная с версии 2.2.16 для этого следует использовать директиву
FallbackResource:<Directory "/var/www/my_blog"> FallbackResource "index.php" </Directory>
Однако в более ранних версиях Apache или если ваши требования сложнее, можно использовать вариант следующего набора правил переписывания, чтобы добиться того же результата:
<Directory "/var/www/my_blog"> RewriteBase "/my_blog" RewriteCond "/var/www/my_blog/%{REQUEST_FILENAME}" !-f RewriteCond "/var/www/my_blog/%{REQUEST_FILENAME}" !-d RewriteRule "^" "index.php" [PT] </Directory>Если же вы хотите передавать запрошенный URI в качестве аргумента строки запроса в index.php, замените это правило RewriteRule следующим:
RewriteRule "(.*)" "index.php?$1" [PT,QSA]
Обратите внимание, что эти наборы правил можно использовать как в файле
.htaccess, так и в блоке <Directory>.
Переписывание строки запроса
- Описание:
- Вы хотите извлечь определённое значение из строки запроса и заменить его либо включить в другой компонент URL.
- Решения:
-
Во многих решениях этого раздела используется одно и то же условие, которое помещает найденное значение в обратную ссылку %2. В %1 находится начало строки запроса (вплоть до интересующего ключа), а в %3 — оставшаяся часть. Это условие немного сложнее обычного ради гибкости и во избежание появления двойного '&&' в подстановках.
- Это решение удаляет совпавшие ключ и значение:
# Remove mykey=??? RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$" RewriteRule "(.*)" "$1?%1%3" - Это решение использует извлечённое значение в подстановке URL, отбрасывая остальную исходную строку запроса с помощью добавления '?':
# Copy from query string to PATH_INFO RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$" RewriteRule "(.*)" "$1/products/%2/?" [PT] - Это решение проверяет извлечённое значение в последующем условии:
# Capture the value of mykey in the query string RewriteCond "%{QUERY_STRING}" "(.*(?:^|&))mykey=([^&]*)&?(.*)&?$" RewriteCond "%2" !=not-so-secret-value RewriteRule "(.*)" - [F] - Это решение показывает обратный вариант предыдущих: компоненты пути (возможно, PATH_INFO) копируются из URL в строку запроса.
# The desired URL might be /products/kitchen-sink, and the script expects # /path?products=kitchen-sink. RewriteRule "^/?path/([^/]+)/([^/]+)" "/path?$1=$2" [PT]
- Это решение удаляет совпавшие ключ и значение:
© 2018 The Apache Software Foundation
Licensed under the Apache License, Version 2.0.
https://httpd.apache.org/docs/2.4/en/rewrite/remapping.html