Редиректы на сайте: .htaccess, nginx и настройки Битрикса
Редирект — ответ сервера «страница переехала, иди туда». Вопрос обычно не в том, как написать правило, а где его разместить: в окружении Битрикса таких мест минимум четыре, и работают они по-разному.
Где прописывать
| Где | Когда уместно | Цена |
|---|---|---|
| Конфиг nginx | Склейка доменов, протокол, массовые правила | Самый быстрый: ответ отдаётся сразу |
.htaccess | Когда к конфигу нет доступа | Файл читается на каждый запрос |
| PHP на стороне сайта | Правила, зависящие от данных | Дорого: поднимается всё ядро продукта |
| Модуль переадресации в продукте | Точечные правила силами контент-менеджера | Удобно, но тоже через PHP |
Правило простое: чем ближе редирект ко входу, тем он дешевле. Склейку доменов и протокола держите в конфиге веб-сервера, а не в коде сайта — иначе каждый запрос по http поднимает PHP только затем, чтобы ответить «иди на https».
Важная особенность связки: nginx стоит спереди и отдаёт картинки, стили и скрипты сам, а PHP обрабатывает Apache. Поэтому правило в .htaccess не сработает для того, что nginx отдаёт без участия Apache.
Склейка адресов
С www на без www
Apache:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]
nginx — отдельным блоком, без условий внутри основного:
server {
listen 80;
listen 443 ssl;
server_name www.site.ru;
return 301 https://site.ru$request_uri;
}
С без www на www
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L]
С http на https
Apache:
RewriteCond %{HTTPS} off
RewriteCond %{REQUEST_URI} !^/\.well-known/
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
За прокси переменная HTTPS может не выставляться — тогда условие пишут по заголовку:
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
Исключение для /.well-known/ обязательно: по этому пути проверяется домен при выпуске и продлении сертификата. Закроете его редиректом — сертификат перестанет продлеваться.
Протокол и домен одним правилом
Чтобы не делать цепочку из двух редиректов, оба условия сводят в одно правило:
server {
listen 80;
server_name site.ru www.site.ru;
return 301 https://site.ru$request_uri;
}
server {
listen 443 ssl;
server_name www.site.ru;
return 301 https://site.ru$request_uri;
}
Слеш в конце адреса
Добавить слеш
nginx — только для адресов без точки, чтобы не трогать файлы:
rewrite ^([^.]*[^/])$ $1/ permanent;
Apache:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*[^/])$ /$1/ [R=301,L]
Убрать слеш
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [R=301,L]
Выберите один вариант и держитесь его: адреса со слешем и без — это разные страницы, и метания между ними плодят дубли.
Переезд страниц и разделов
Одна страница на другую
Apache:
Redirect 301 /old-page/ /new-page/
nginx:
location = /old-page/ { return 301 /new-page/; }
Знак равенства важен: без него правило поймает и вложенные адреса.
Раздел целиком с сохранением пути
Чтобы /old/page-1/ ушёл на /new/page-1/, а не на общую страницу:
RedirectMatch 301 ^/old-section/(.*)$ /new-section/$1
rewrite ^/old-section/(.*)$ /new-section/$1 permanent;
Раздел на одну страницу
location /old-section/ { return 301 /new-page/; }
Так делают, когда раздел закрыт и заменять нечем. Но если страниц было много и они приносили трафик, лучше составить список соответствий: массовый редирект в одну точку поисковики считают мягким 404, и вес не передаётся.
Один файл, не трогая папку
RewriteRule ^catalog/old-file\.php$ /catalog/new-file.php [R=301,L]
Смена расширения
RedirectMatch 301 ^(.*)\.html$ $1.php
Смена домена
Весь сайт на новый домен
server {
listen 443 ssl;
server_name old-site.ru www.old-site.ru;
return 301 https://new-site.ru$request_uri;
}
Apache:
RewriteCond %{HTTP_HOST} ^(www\.)?old-site\.ru$ [NC]
RewriteRule ^(.*)$ https://new-site.ru/$1 [R=301,L]
Всё, кроме административной части
Пригодится, пока на старом домене ещё работают: админка остаётся доступной, остальное переезжает.
RewriteCond %{REQUEST_URI} !^/bitrix/admin
RewriteCond %{REQUEST_URI} !^/bitrix/tools
RewriteRule ^(.*)$ https://new-site.ru/$1 [R=301,L]
Поддомен на основной домен
server {
listen 443 ssl;
server_name shop.site.ru;
return 301 https://site.ru$request_uri;
}
Кириллический домен
В правилах домен записывается в кодировке punycode, а не кириллицей:
RewriteCond %{HTTP_HOST} ^xn--80aswg\.xn--p1ai$ [NC]
RewriteRule ^(.*)$ https://site.ru/$1 [R=301,L]
Адреса с параметрами
Конкретное значение параметра
RewriteCond %{QUERY_STRING} ^id=10$
RewriteRule ^catalog\.php$ /catalog/tovar-10/? [R=301,L]
Знак вопроса в конце цели отбрасывает исходные параметры — без него они приклеятся к новому адресу.
Любое значение параметра
RewriteCond %{QUERY_STRING} ^id=([0-9]+)$
RewriteRule ^catalog\.php$ /catalog/tovar-%1/? [R=301,L]
Убрать все параметры
RewriteCond %{QUERY_STRING} .
RewriteRule ^(.*)$ /$1? [R=301,L]
nginx — обратите внимание, что $uri не содержит параметров, в отличие от $request_uri:
if ($args) { return 301 $uri; }
Так чистят адреса от служебных меток. С рекламными метками так делать не нужно: они нужны аналитике, и для них есть Clean-param в robots.txt.
index.php в корне
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /index\.php\ HTTP/
RewriteRule ^index\.php$ / [R=301,L]
Условие по THE_REQUEST обязательно: без него правило сработает на внутреннем переписывании адреса и уведёт сайт в бесконечный редирект.
Особые случаи
Картинки на отдельный домен
RewriteRule ^upload/(.*)\.(jpg|jpeg|png|webp)$ https://static.site.ru/upload/$1.$2 [R=301,L]
Техническое обслуживание
На время работ нужен не 301 и даже не 302, а ответ 503 с указанием, когда возвращаться — тогда поисковик не выбросит страницы из индекса:
location / {
return 503;
}
error_page 503 /maintenance.html;
Разные адреса для мобильных
RewriteCond %{HTTP_USER_AGENT} (android|iphone|ipad) [NC]
RewriteRule ^$ https://m.site.ru/ [R=302,L]
Отдельная мобильная версия — вчерашний день: адаптивная вёрстка проще в поддержке и не плодит дубли. Правило приведено для сайтов, где такая версия ещё осталась, и редирект здесь намеренно временный.
Редиректы внутри Битрикса
Кроме конфигов, перенаправления на сайте могут появляться ещё в трёх местах — и когда редирект «не работает», виноват обычно конфликт между ними:
- Модуль переадресации в административной части: список правил, который ведут контент-менеджеры.
- Файл
urlrewrite.phpв корне: правила ЧПУ. Он не делает редиректов, но определяет, какой скрипт обработает адрес. - Код сайта: обработчики событий в
init.php, вызовыLocalRedirect()в компонентах и шаблонах.
Если страница неожиданно перенаправляется, ищите по порядку: конфиг nginx, .htaccess, модуль переадресации, код. Поиск по проекту строки LocalRedirect закрывает последний пункт за минуту.
Частые ошибки
- Цепочка редиректов. http → www → https → страница: четыре ответа вместо одного. Сводите в одно правило.
- Редирект на 404. Перенаправление на несуществующую страницу хуже честного 404: поисковик дольше считает старый адрес живым.
- Весь старый сайт на главную. Считается мягким 404, вес страниц не передаётся. Нужен список соответствий.
- 302 вместо 301 при постоянном переезде. Старый адрес остаётся в выдаче, новый не набирает вес.
- Бесконечный редирект. Правило перенаправляет на адрес, который снова подпадает под это же правило. Отсюда ошибка «слишком много перенаправлений».
- Закрытый
/.well-known/. Перестаёт продлеваться сертификат — и через три месяца сайт встречает посетителей предупреждением браузера. - Правила в
.htaccessтам, где запрос не доходит до Apache. Для статики нужен nginx.
Как проверять
Не в браузере: он кеширует постоянные редиректы, и вы будете видеть вчерашнюю картину. Смотрите ответ сервера:
curl -sI https://site.ru/old-page/ | head -5
В ответе нужны код 301 и заголовок Location с правильным адресом. Чтобы увидеть всю цепочку целиком и убедиться, что она из одного шага:
curl -sIL https://site.ru/old-page/ | grep -E "HTTP/|Location"
После массового переезда проверьте список самых посещаемых старых адресов, а не два-три наугад: выгрузку можно взять из Вебмастера или из аналитики.
Частые вопросы
Какой редирект использовать при переезде страницы — 301 или 302?
При постоянном переезде 301: он передаёт вес старого адреса новому и заменяет адрес в выдаче. 302 означает «временно» и оставляет в индексе прежний адрес — его используют для технических случаев. На время работ правильнее отдавать 503, а не редирект.
Работает ли .htaccess, если на сервере nginx?
Зависит от связки. В окружении Битрикса nginx стоит спереди, а PHP обрабатывает Apache, который .htaccess читает. Но запросы, которые nginx отдаёт сам — картинки, стили, скрипты, — до Apache не доходят, и правила для них не сработают.
Сколько редиректов подряд допустимо?
Чем меньше, тем лучше: каждая цепочка — лишние запросы и потеря веса. Две склейки подряд (http на https и www на без www) нужно объединять в одно правило, которое сразу ведёт на конечный адрес.
Почему после правила сайт выдаёт «слишком много перенаправлений»?
Правило перенаправляет на адрес, который снова подпадает под него же. Частый случай — редирект index.php без условия по THE_REQUEST: внутреннее переписывание адреса попадает под правило и зацикливает его.
Как перенаправить домен в зоне .рф?
В правилах домен записывается в кодировке punycode, то есть видом xn--80aswg.xn--p1ai, а не кириллицей. Кириллическая запись в условии не сработает.
Куда смотреть, если страница перенаправляется, а правила нигде нет?
В Битриксе редирект может приходить из четырёх мест: конфиг nginx, .htaccess, модуль переадресации в административной части и код сайта. Последний ищется поиском по проекту строки LocalRedirect.
Нужно ли закрывать параметры редиректом, чтобы убрать дубли?
Не обязательно и часто вредно: рекламные метки нужны аналитике. Для склейки адресов с параметрами есть директива Clean-param в robots.txt и canonical на странице.