Spec-Zone.ru › Apache HTTP Server

Перенаправление и переадресация с помощью 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API