При переносе сайта, изменении структуры каталога или переименовании страниц обычно настраиваются постоянные 301-редиректы со старых URL на новые. На первый взгляд задача простая: указать старый адрес и конечную страницу.
На практике нередко получается такая цепочка:
http://example.com/old-page/
→ https://example.com/old-page/
→ https://www.example.com/old-page/
→ https://www.example.com/new-page/
Технически пользователь всё равно попадёт на нужную страницу. Но сервер выполнит три последовательных перенаправления вместо одного.
Правильный результат должен выглядеть так:
http://example.com/old-page/
→ https://www.example.com/new-page/
То есть любой вариант старого адреса должен сразу вести на окончательный канонический URL: с нужным протоколом, доменом, путём и завершающим слешем.
Google рекомендует направлять старые URL непосредственно на конечные страницы и избегать цепочек редиректов. Дополнительные переходы увеличивают задержку для пользователя и создают лишнюю работу для поискового робота.
Почему появляется лишний переход с HTTP на HTTPS
Типичная конфигурация .htaccess содержит отдельное правило, которое переводит весь сайт на HTTPS:
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://www.example.com%{REQUEST_URI} [R=301,END,NE]
Ниже разработчик добавляет редирект старой страницы:
RewriteRule ^old-page/?$ https://www.example.com/new-page/ [R=301,END,NE]
Проблема заключается в порядке выполнения правил.
Когда сервер получает запрос:
http://example.com/old-page/
первым срабатывает общее правило HTTPS:
http://example.com/old-page/
→ https://www.example.com/old-page/
После этого браузер делает второй запрос. Только при его обработке Apache доходит до правила старой страницы:
https://www.example.com/old-page/
→ https://www.example.com/new-page/
В результате получается цепочка из двух редиректов.
Основной принцип: частные правила должны находиться выше общих
Правильный порядок обработки:
- Редиректы конкретных старых страниц.
- Массовые редиректы разделов.
- Приведение домена и протокола к каноническому виду.
- Внутренние правила CMS или фреймворка.
Рабочий пример .htaccess:
RewriteEngine On
# 1. Старые URL сразу отправляем на конечные HTTPS-адреса
RewriteRule ^old-page/?$ https://www.example.com/new-page/ [R=301,END,NE]
RewriteRule ^catalog/old-category/?$ https://www.example.com/catalog/new-category/ [R=301,END,NE]
RewriteRule ^services/old-service/?$ https://www.example.com/services/new-service/ [R=301,END,NE]
# 2. Для остальных страниц одновременно исправляем протокол и домен
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} !^www\.example\.com$ [NC]
RewriteRule ^ https://www.example.com%{REQUEST_URI} [R=301,END,NE]
# 3. Ниже располагаются внутренние правила сайта или CMS
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]
Теперь запросы:
http://example.com/old-page/
http://www.example.com/old-page/
https://example.com/old-page/
https://www.example.com/old-page/
будут сразу перенаправлены на:
https://www.example.com/new-page/
без промежуточного открытия старого URL по HTTPS.
Флаг END полностью останавливает дальнейшую обработку mod_rewrite в Apache 2.4. Для старых конфигураций вместо него можно использовать L, хотя современные серверы должны работать на поддерживаемой версии Apache. Официальная документация Apache отдельно отмечает особенности обработки правил в .htaccess: в таком контексте начальный слеш пути удаляется, поэтому правило должно начинаться с ^old-page, а не с ^/old-page.
Как одновременно исправить HTTP и домен
Не стоит создавать два отдельных правила:
# Неоптимальный вариант
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://example.com%{REQUEST_URI} [R=301,END]
RewriteCond %{HTTP_HOST} !^www\.example\.com$ [NC]
RewriteRule ^ https://www.example.com%{REQUEST_URI} [R=301,END]
Для запроса http://example.com/page/ такая конфигурация может создать два перехода:
http://example.com/page/
→ https://example.com/page/
→ https://www.example.com/page/
Протокол и домен лучше исправлять одним ответом:
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} !^www\.example\.com$ [NC]
RewriteRule ^ https://www.example.com%{REQUEST_URI} [R=301,END,NE]
Если каноническим считается домен без www, правило будет выглядеть так:
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} !^example\.com$ [NC]
RewriteRule ^ https://example.com%{REQUEST_URI} [R=301,END,NE]
В целевом адресе лучше явно указывать канонический домен, а не формировать его напрямую из %{HTTP_HOST}. Это снижает риск некорректных перенаправлений при подменённом заголовке Host и делает результат предсказуемым.
Где размещать правила в WordPress, 1С-Битрикс и других CMS
Пользовательские редиректы нужно размещать раньше внутренних правил маршрутизации CMS.
Для WordPress — выше блока:
# BEGIN WordPress
Для 1С-Битрикс — выше правил, которые отправляют запросы в urlrewrite.php, index.php или другой фронт-контроллер.
Общая структура должна быть такой:
RewriteEngine On
# Персональные 301-редиректы
RewriteRule ^old-page/?$ https://www.example.com/new-page/ [R=301,END,NE]
# Канонический протокол и домен
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} !^www\.example\.com$ [NC]
RewriteRule ^ https://www.example.com%{REQUEST_URI} [R=301,END,NE]
# Правила CMS
# ...
Не следует помещать собственные правила внутрь автоматически генерируемого блока CMS: система может перезаписать его при обновлении настроек постоянных ссылок или конфигурации сайта.
Нужно ли сохранять UTM-метки и другие GET-параметры
В большинстве случаев Apache сохраняет исходную строку параметров автоматически, если в целевом URL не указана новая строка запроса.
Правило:
RewriteRule ^old-page/?$ https://www.example.com/new-page/ [R=301,END,NE]
перенаправит:
http://example.com/old-page/?utm_source=yandex&utm_campaign=brand
на:
https://www.example.com/new-page/?utm_source=yandex&utm_campaign=brand
Важно: регулярное выражение в RewriteRule сопоставляется только с путём URL. GET-параметры в него не входят. Для проверки параметров применяется %{QUERY_STRING}.
Если параметры нужно удалить, используется флаг QSD:
RewriteRule ^old-search/?$ https://www.example.com/search/ [R=301,END,QSD,NE]
Результат:
/old-search/?query=test&page=3
→ /search/
Если новый URL уже содержит собственные параметры, а исходные нужно добавить к ним, применяется QSA:
RewriteRule ^old-page/?$ https://www.example.com/new-page/?source=legacy [R=301,END,QSA,NE]
Apache указывает, что без QSA новая строка запроса заменяет старую, а QSD полностью удаляет исходные параметры.
Redirect, RedirectMatch или RewriteRule
В Apache существует два основных механизма внешних перенаправлений:
RedirectиRedirectMatchиз модуляmod_alias;RewriteRuleиз модуляmod_rewrite.
Когда подходит Redirect
Для простого переноса раздела можно использовать:
Redirect permanent /old-section/ https://www.example.com/new-section/
При запросе:
/old-section/product-1/
Apache добавит оставшуюся часть пути к новому адресу:
https://www.example.com/new-section/product-1/
Директива Redirect работает как сопоставление префикса, а не как точное совпадение всей строки. Это нужно учитывать, чтобы случайно не перенаправить вложенные страницы.
Для точного совпадения подходит RedirectMatch:
RedirectMatch permanent ^/old-page/?$ https://www.example.com/new-page/
Apache рекомендует использовать Redirect для простых перенаправлений, а mod_rewrite — когда нужны условия, регулярные выражения или сложная логика.
Почему не стоит без необходимости смешивать оба механизма
В .htaccess директивы Redirect обрабатываются раньше RewriteRule, независимо от визуального порядка строк. В серверной конфигурации порядок взаимодействия модулей отличается.
Из-за этого смешивание mod_alias и mod_rewrite в одной области может создавать неочевидное поведение. Официальная документация Apache рекомендует выбирать один механизм для одной задачи.
Если в .htaccess уже есть сложная канонизация HTTP, HTTPS и www, проще и предсказуемее реализовать пользовательские редиректы через RewriteRule.
Почему абсолютный конечный URL надёжнее относительного
Для внешнего 301-редиректа желательно указывать полный конечный адрес:
RewriteRule ^old-page/?$ https://www.example.com/new-page/ [R=301,END,NE]
а не относительный путь:
RewriteRule ^old-page/?$ /new-page/ [R=301,END]
При относительной цели Apache формирует полный адрес на основе текущего протокола и домена. Если исходный запрос пришёл по HTTP или на неканонический хост, сервер может вернуть промежуточный адрес:
http://example.com/old-page/
→ http://example.com/new-page/
→ https://www.example.com/new-page/
Полный HTTPS-адрес сразу фиксирует все компоненты конечного URL:
- протокол;
- домен;
- путь;
- завершающий слеш;
- при необходимости — параметры.
Не забывайте про варианты со слешем
Правило:
RewriteRule ^old-page$ https://www.example.com/new-page/ [R=301,END,NE]
обработает только:
/old-page
но не обработает:
/old-page/
Чтобы учесть оба варианта, используется необязательный слеш:
RewriteRule ^old-page/?$ https://www.example.com/new-page/ [R=301,END,NE]
Это также помогает избежать промежуточного редиректа, который Apache или CMS может автоматически выполнить для добавления завершающего слеша.
Особенно внимательно нужно проверять URL, совпадающие с реальными каталогами на сервере. Модуль mod_dir может самостоятельно добавлять слеш к адресу директории, создавая дополнительный переход.
Более правильная альтернатива: конфигурация Apache VirtualHost
Если есть доступ к конфигурации сервера, правила лучше размещать не в .htaccess, а в конфигурации виртуального хоста.
Apache прямо рекомендует использовать основной конфигурационный файл вместо .htaccess, когда имеется административный доступ. .htaccess считывается при каждом запросе и усложняет порядок обработки правил.
Для обычного перевода сайта на HTTPS рекомендуется отдельный виртуальный хост на порту 80:
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
Redirect permanent / https://www.example.com/
</VirtualHost>
Но при наличии старых адресов недостаточно поставить только общий Redirect. Иначе запрос старой страницы сначала перейдёт на её HTTPS-версию, а уже затем — на новый URL.
Специальные редиректы должны выполняться раньше общего правила или подключаться из единой карты перенаправлений.
Для большого сайта можно использовать RewriteMap: таблицу соответствий между старыми и новыми адресами. RewriteMap объявляется только в серверной конфигурации или VirtualHost и не может быть объявлен непосредственно в .htaccess.
Пример файла соответствий:
/old-page/ https://www.example.com/new-page/
/old-category/ https://www.example.com/catalog/new-category/
/old-service/ https://www.example.com/services/new-service/
Такой подход удобнее сотен отдельных регулярных выражений и позволяет хранить редиректы централизованно.
Настройка редиректов в Nginx
В Nginx для простых внешних перенаправлений лучше использовать директиву return 301, а не сложные конструкции с rewrite.
Пример отдельного HTTP-сервера:
server {
listen 80;
server_name example.com www.example.com;
location = /old-page {
return 301 https://www.example.com/new-page/;
}
location = /old-page/ {
return 301 https://www.example.com/new-page/;
}
location / {
return 301 https://www.example.com$request_uri;
}
}
Здесь два точных старых адреса сразу переходят на новую страницу. Все остальные запросы отправляются на тот же путь по HTTPS.
Для неканонического HTTPS-домена, например https://example.com, аналогичные специальные правила должны находиться и в соответствующем серверном блоке. При большом количестве адресов их лучше вынести в общую подключаемую конфигурацию или карту.
Nginx обрабатывает директивы уровня server до поиска подходящего location, поэтому бездумно размещать общий return 301 прямо на уровне server нельзя: он может сработать раньше специальных правил. Официальная документация описывает порядок выполнения return и rewrite и рекомендует для перенаправления доменов использовать отдельные серверные блоки.
Редиректы на уровне CDN или reverse proxy
Если сайт работает через Cloudflare, балансировщик или другой reverse proxy, перенаправление можно выполнять до обращения к основному серверу.
Преимущества такого подхода:
- редирект выполняется ближе к пользователю;
- запрос не доходит до PHP, CMS и origin-сервера;
- можно сразу сопоставить старые HTTP и HTTPS URL;
- большие списки редиректов можно загружать централизованно.
В Cloudflare для этого предусмотрены Single Redirects и Bulk Redirects. Single Redirects подходят для динамических и шаблонных правил, а Bulk Redirects — для больших статических таблиц соответствий. Bulk Redirect выполняется до отправки запроса на origin-сервер. Если в исходном URL не указывать протокол, правило может применяться одновременно к HTTP и HTTPS, а целью должен быть полный конечный HTTPS-адрес.
Например:
Источник: example.com/old-page/
Цель: https://www.example.com/new-page/
Код: 301
Такой редирект может сразу обработать:
http://example.com/old-page/
https://example.com/old-page/
без промежуточного обращения к .htaccess.
Важно не дублировать противоречащие правила одновременно на CDN и origin-сервере. Несогласованные настройки HTTPS могут привести не только к цепочке, но и к циклическому перенаправлению. Например, Cloudflare предупреждает о циклах при несовместимых настройках SSL и автоматического HTTPS на стороне origin.
Можно ли делать редиректы средствами CMS
Многие CMS, фреймворки и SEO-модули позволяют хранить перенаправления в административной панели или базе данных.
Такой вариант удобен, когда:
- редиректы регулярно добавляют контент-менеджеры;
- новый адрес зависит от объекта в базе данных;
- нужна статистика переходов;
- правила должны управляться без доступа к серверу.
Но у приложения есть существенный недостаток: оно обрабатывает запрос позже Nginx, Apache, CDN или балансировщика.
Если reverse proxy сначала выполняет:
http://example.com/old-page/
→ https://example.com/old-page/
и только затем передаёт запрос в CMS, приложение уже не сможет убрать первый переход.
Чтобы гарантировать один редирект, правило должно находиться на самом раннем уровне, который одновременно:
- видит исходный URL;
- знает окончательный адрес назначения.
Поэтому массовые миграционные редиректы обычно лучше размещать на уровне CDN, Nginx или Apache. CMS подходит для редиректов, логика которых действительно зависит от приложения.
Как проверить цепочку редиректов
Для проверки первого ответа сервера можно использовать curl:
curl -I http://example.com/old-page/
Правильный ответ:
HTTP/1.1 301 Moved Permanently
Location: https://www.example.com/new-page/
В заголовке Location сразу должен находиться окончательный URL.
Затем проверяем конечную страницу:
curl -I https://www.example.com/new-page/
Ожидаемый результат:
HTTP/2 200
Полную цепочку можно посмотреть командой:
curl -IL http://example.com/old-page/
А количество переходов и фактический конечный адрес — так:
curl -sS -L -o /dev/null \
-w 'Final URL: %{url_effective}\nRedirects: %{num_redirects}\nStatus: %{http_code}\n' \
http://example.com/old-page/
Для одной корректно настроенной старой страницы результат должен быть примерно таким:
Final URL: https://www.example.com/new-page/
Redirects: 1
Status: 200
Проверять нужно минимум четыре варианта:
http://example.com/old-page/
http://www.example.com/old-page/
https://example.com/old-page/
https://www.example.com/old-page/
Если сайт раньше использовал адреса без завершающего слеша, дополнительно проверяются оба варианта пути.
Частые ошибки
Общее правило HTTPS находится выше специальных редиректов
Результат:
HTTP старой страницы
→ HTTPS старой страницы
→ HTTPS новой страницы
Решение: поднять специальные правила выше.
В цели указан HTTP
RewriteRule ^old-page/?$ http://www.example.com/new-page/ [R=301,END]
Результат:
старый URL
→ HTTP нового URL
→ HTTPS нового URL
Решение: всегда указывать окончательный HTTPS-адрес.
Протокол и домен исправляются разными правилами
Результат:
HTTP без www
→ HTTPS без www
→ HTTPS с www
Решение: объединить проверки через [OR] и выполнить один редирект.
Редирект ведёт на промежуточную страницу
/old-product/
→ /temporary-product/
/temporary-product/
→ /catalog/product/
Решение: обновить исходное правило и сразу указать актуальную конечную страницу.
Не учтён завершающий слеш
Результат:
/old-page
→ /old-page/
→ /new-page/
Решение: сопоставлять оба варианта через /?$.
Правило находится после маршрутизации CMS
Запрос сначала попадает в PHP или обрабатывается другими правилами, из-за чего результат становится зависимым от внутренней логики приложения.
Решение: размещать внешние 301-редиректы перед фронт-контроллером CMS.
Редиректы одновременно настроены в нескольких местах
Например:
- Cloudflare;
- Nginx;
.htaccess;- CMS;
- SEO-плагин.
Решение: определить один основной уровень перенаправлений и удалить дублирующие либо конфликтующие правила.
Какой способ выбрать
| Ситуация | Рекомендуемый вариант |
|---|---|
| Обычный хостинг без доступа к серверу | .htaccess и RewriteRule |
| Есть доступ к Apache | VirtualHost, Redirect или RewriteMap |
| Сервер работает на Nginx | return 301, точные location или карта |
| Сайт работает через Cloudflare | Single Redirects или Bulk Redirects |
| Редиректы зависят от данных CMS | Маршрутизатор приложения |
| Сотни и тысячи URL после миграции | Таблица перенаправлений на сервере или CDN |
Итоговая схема правильной настройки
Чтобы не создавать промежуточные HTTP URL в цепочке:
- Определите единственный канонический формат адресов.
- Направляйте каждый старый URL сразу на окончательный HTTPS-адрес.
- Размещайте частные правила выше общей канонизации.
- Объединяйте переход на HTTPS и смену домена в одно правило.
- Учитывайте варианты с
www, безwww, со слешем и без него. - Не смешивайте без необходимости
RedirectиRewriteRule. - Не дублируйте правила одновременно в CDN, веб-сервере и CMS.
- Проверяйте каждый адрес через
curl, а не только визуально в браузере.
Хороший 301-редирект — это один ответ сервера, после которого пользователь и поисковый робот сразу оказываются на актуальной странице.
Команда NJ Soft проводит технический аудит сайтов, проверяет цепочки перенаправлений и настраивает безопасные миграции URL для Apache, Nginx, 1С-Битрикс, WordPress и кастомных веб-приложений.




