Бесплатный экспресс-аудит сайта Безопасность, SEO, AI-поиск и 152-ФЗ — отчёт с баллами за минуту Проверить сайт
Обсудить проект

Редиректы на сайте: .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 на странице.