Иногда возникает необходимость в переносе сайта, обслуживаемого веб-сервером Apache, на новый домен. Снизить издержки на такой перенос помогает настройка перенаправления HTTP-запросов к старому домену на новый. Перенаправление может быть выполнено с помощью модуля Rewrite, настройка которого производится либо в конфигурационном файле веб-сервера httpd.conf, либо в файле децентрализованных настроек .htaccess. В этой статье рассматривается второй вариант, поскольку он универсальнее и может быть использован, например, абонентами услуг виртуального хостинга.

Помимо основной задачи перенаправления запросов на новый домен удобно заодно решить дополнительные задачи, относящиеся к поисковой оптимизации. Это - исключение из поступающих запросов префикса www. и преобразование схемы HTTP в HTTPS.

Найденное решение

Решение, описанное в этой статье:

  • перенаправляет HTTP-запросы с домена old.com и всех его поддоменов на домен new.com и соответствующие его поддомены;

  • исключает префикс www. путём перенаправления HTTP-запросов с доменов www.*.new.com на *.new.com;

  • может использоваться для преобразования HTTP-запросов в HTTPS-запросы.

Первые две задачи решаются следующими директивами файла .htaccess, который находится в корневом каталоге веб-сайта. Директивы сгруппированы в две условные секции - проверки (validator) и перенаправления (redirector), начало которых отмечено соответствующими комментариями:

# HTTP-request validator.
RewriteCond "%{HTTP_HOST}" "!^www\." [NC]
RewriteCond "%{HTTP_HOST}" "^(?:.+\.)?new\.com(?::\d+)?$"
RewriteRule "^" "-" [L]
# HTTP-request redirector.
RewriteCond "%{HTTP_HOST}" "^(?:www\.)?(.+\.)?(?:new\.com|old\.com)(:\d+)?$"
RewriteRule "^" "%{REQUEST_SCHEME}://%1new.com%2%{REQUEST_URI}" [R=301,L]

Если дополнительно требуется модифицировать схему HTTP в HTTPS, то в секцию проверки надо добавить строку:

RewriteCond "%{HTTPS}" "on"

а правило в секции перенаправления изменить так:

RewriteRule "^" "https://%1new.com%2%{REQUEST_URI}" [R=301,L]

Необходимые условия

Чтобы правила перенаправления, указанные в файле .htaccess, заработали:

  • на веб-сервере должно быть разрешено использование файлов .htaccess - настраивается директивой AllowOverride в секции <Directory> конфигурации сервера httpd.conf;

  • модуль Rewrite должен быть подключён - в файле httpd.conf не должно быть признака комментария в строке LoadModule rewrite_module modules/mod_rewrite.so;

  • модуль Rewrite должен быть активирован - директивой RewriteEngine On в .htaccess или в секции httpd.conf:

<IfModule rewrite>
	RewriteEngine On
</IfModule>

Пояснения к решению

Директивы RewriteRule

Для описания перенаправления в файлах .htaccess используются директивы RewriteRule, которые работают в контексте каталога (это важно!), в котором расположен файл .htaccess, и содержат два обязательных параметра:

  • шаблон сопоставления (регулярное выражение), которое применяется к пути HTTP-запроса относительно текущего каталога;

  • строка подстановки, которая используется в случае соответствия пути HTTP-запроса шаблону сопоставления. Третий, необязательный, параметр содержит флаги, задающие особенности выполнения директивы. Если в файле присутствует несколько директив RewriteRule, то они просматриваются и выполняются последовательно, сверху вниз по тексту файла.

То, к чему применяется шаблон сопоставления, легче пояснить на примере. Допустим, компонента пути (path в терминах RFC 3986) HTTP-запроса выглядит так: /blog/20231008/index.html. Если файл .htaccess находится в корне сайта, то шаблон будет применяться к строке blog/20231008/index.html, а если в каталоге /blog, то к строке 20231008/index.html.

В компоненте пути отсутствует информация о доменах, поэтому её анализ не может помочь в решении задачи перенаправления, рассматриваемой в этой статье. Это приводит к необходимости использования дополнительных условий, реализуемых директивой RewriteCond, которая будет рассмотрена ниже.

Если в шаблоне сопоставления имеются группы с захватом (регулярные выражения, заключённые в круглые скобки), то в случае совпадения фрагменты пути, к которому применён шаблон, будут доступны через номерные переменные: $1, $2, ..., $9, а полное совпадение - через переменную $0. Например, директива

RewriteRule "c(.+)f(.+)i" "x$2y$1z.$0"

будучи применённой к компоненте пути ABcDEfGHiJK, произведёт её замену на xGHyDEz.cDEfGHi.

То, как будет использован результат подстановки, зависит от того, на что он похож с точки зрения модуля Rewrite: на путь в файловой системе, на компоненту пути URL или на полный URL. В этой статье директива в секции перенаправления генерирует полный URL. А в секции проверки использован простейший вариант директивы RewriteRule:

RewriteRule "^" "-" [L]

В этой директиве шаблон сопоставления "^" соответствует любой строке, поскольку символ ^ (крышка) в регулярном выражении означает начало строки. У каждой строки есть начало, а большего и не требуется. Строка подстановки "-", содержащая единственный дефис, означает, что строку, которая удовлетворяет шаблону, надо оставить в неизменном виде. Флаг [L] требует игнорировать (не применять) директивы RewriteRule, которые могут присутствовать ниже в файле .htaccess.

Практическая польза от такой директивы может быть получена только в том случае, если она предваряется дополнительными условиями, которые задаются директивами RewriteCond.

Директивы RewriteCond

Дополнительные условия, помимо рассмотренного основного условия соответствия шаблону, которые должны выполняться для активации директивы RewriteRule, описываются предшествующими ей директивами RewriteCond. Каждая директива RewriteCond содержит два обязательных параметра:

  • проверяемую строку;

  • шаблон сопоставления (регулярное выражение), которое применяется к проверяемой строке. Третий, необязательный, параметр, содержит флаги, задающие особенности выполнения директивы.

Проверяемая строка может формироваться из значений переменных, определённых в контексте веб-сервера Apache. В этой статье анализируются значение переменной HTTP_HOST, которая содержит компоненту имени и номера порта сервера (authority в терминах RFC 3986) и переменной HTTPS, которая содержит значение on для защищённого протокола и off при открытой передаче данных.

Результат директивы считается истинным, если шаблон, применённый к проверяемой строке, даёт совпадение, и не начинается с восклицательного знака. Восклицательный знак работает как логическое НЕ, поэтому результат будет истинным при отсутствии совпадения. Флаг [NC] требует считать строчные и прописные буквы одинаковыми. Результаты последовательных директив RewriteCond соединяются между собой и следующей за ними директивой RewriteRule логическим И, если не указан флаг [OR]. В последнем случае они соединяются логическим ИЛИ. Последовательность директив RewriteCond обрабатывается по правилу сокращённого вычисления выражений, то есть проверки прерываются, когда становится известным результат логического выражения, заданного этой последовательностью. По этой причине в некоторых случаях порядок следования директив имеет значение.

Если в последнем вычисленном шаблоне сопоставления имеются группы с захватом (регулярные выражения, заключённые в круглые скобки), то соответствующие этим группам фрагменты проверяемой строки в случае совпадения с шаблоном будут доступны через номерные переменные: %1, %2, ..., %9. В отличие от переменных, которые инициализируются директивой RewriteRule, их признаком является знак процента, а не знак доллара.

Рассмотрим следующую директиву:

RewriteCond "%{HTTP_HOST}" "^(?:www\.)?(.+\.)?(?:new\.com|old\.com)(:\d+)?$"

В качестве проверяемой строки в ней выступает значение переменной HTTP_HOST. Регулярное выражение шаблона имеет привязки к началу и концу строки (^$), две группы без захвата (в которых после открывающей скобки идёт вопросительный знак с двоеточием) и две группы с захватом. Первая группа с захватом инициализирует переменную %1 значением поддоменов, завершающимся точкой, а вторая - переменную %2 значением номера порта сервера, предваряемым двоеточием. Вопросительный знак после закрывающей скобки допускает отсутствие в проверяемой строке соответствующей группы, в случае которого значение соответствующей номерной переменной будут пустой строкой.

Cекции проверки и перенаправления

Задачей секции проверки является прерывание просмотра директив RewriteRule для тех URL, которые не требуют перенаправления, то есть не начинаются с www. и не ведут на старый домен. Те URL, которые проверку не проходят, попадают в секцию перенаправления. Там директива RewriteCond выполняет формальную проверку на несоответствие требованиям (что уже и так известно) и (в этом главная её задача) инициализирует переменные %1 и %2 строками с поддоменами и номером порта сервера соответственно. Эти переменные используются в директиве RewriteRule для формирования полного URL, очищенного от префикса www. и ведущего на новый домен. Помимо номерных переменных, в этой директиве также используются переменные веб-сервера REQUEST_SCHEME (содержит схему запроса http или https) и REQUEST_URL (содержит компоненту пути HTTP-запроса). Перенаправление с возвратом клиенту в HTTP-ответе кода 301 обеспечивается флагом [R=301].

После такого внешнего перенаправления модифицированный URL снова попадает в обработку правилами .htaccess. Зацикливание предотвращается секцией проверки, которая обеспечивает защиту от перенаправления корректных HTTP-запросов. Поэтому важно, чтобы любой URL, попавший в секцию перенаправления, после модификации удовлетворял всем условиям RewriteCond секции проверки.

Тестирование и отладка

Проверять работу выполненных настроек лучше на локальном компьютере, физическом или виртуальном. Для направления запросов к доменам new.com и old.com на локальный сетевой интерфейс удобно модифицировать файл hosts, который находится в каталоге /etc, если используется операционная система Linux или Unix, или в каталоге %SystemRoot%\System32\drivers\etc, если используется Windows. Для этого в него надо добавить строки:

127.0.0.1 old.com site.old.com www.old.com www.site.old.com
127.0.0.1 new.com site.new.com www.new.com www.site.new.com
::1 old.com site.old.com www.old.com www.site.old.com
::1 new.com site.new.com www.new.com www.site.new.com

Если веб-сервер Apache уже настроен на обслуживание запросов к localhost:8080, то после модификации hosts к нему же должны попадать запросы http://old.com:8080, http://www.old.com:8080 и все остальные, соответствующие произведенной модификации. Изменения, вносимые в файл .htaccess, сразу вступают в силу, перезагрузка веб-сервера не требуется.

Модуль Rewrite реализует сложные, не всегда очевидные правила обработки, результат часто зависит от контекста и его "предположений". Чтобы увидеть производимые им действия, можно включить вывод отладочной информации в журнальный файл error.log. Для этого надо найти в конфигурационном файле Apace httpd.conf директиву LogLevel, устанавливающую степень детализации сведений в журнале, и дописать к ней настройку для модуля Rewrite. Например:

LogLevel warn rewrite:trace3

Для отладки регулярных выражений в шаблонах сопоставления RewriteCond и RewriteRule можно воспользоваться подстановкой значений захваченных групп в строку запроса целевого URL. Например:

RewriteCond "%{HTTP_HOST}" "^(?:www\.)?(.+\.)?old\.com(:\d+)?$"
RewriteRule "^" "http://TEST?gr1=%1&gr2=%2&scheme=%{REQUEST_SCHEME}&https=%{HTTPS}" [R,L]

Если в адресной строке браузера после этого набрать http://www.site.old.com:8080, то он сообщит о недоступности запрошенного веб-сайта, а в адресной строке появится текст: http://test/?gr1=site.&gr2=:8080&scheme=http&https=off. Для объяснения результата, возможно, придётся вспомнить, что поддомен www. попал в соответствие незахватываемой группе.

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

Комментарии (4)


  1. MAXH0
    11.10.2023 07:36
    +5

    Очередная статья из песочницы просто соответствующая НЫНЕШНЕМУ уровню Хабра?


  1. gr1mm3r
    11.10.2023 07:36
    +1

    Давай я поищу за тебя на Хабре
    https://habr.com/ru/search/?q=301 redirect&target_type=posts&order=relevance


  1. user18383
    11.10.2023 07:36
    +1

    Есть же CNAME в dns? Зачем использовать apache?


    1. qzq
      11.10.2023 07:36
      +3

      CNAME резолвит домен, а не перенаправляет хттп запрос, разные вещи для разных задач.