
Редиректы и канонический адрес сайта в Nginx
Настраиваем HTTP→HTTPS, www и перенос URL в Nginx без циклов и потери пути: return, проверка Location, временные и постоянные коды.
Содержание
Одна страница может открываться по http://example.com, https://example.com и https://www.example.com. Если все варианты отдают содержимое, поисковая система и внешние ссылки видят несколько адресов одного материала. Выберите один канонический вариант, а остальные направьте к нему за один переход.
В примерах основным будет https://example.com. Сначала настроим временный редирект и проверим заголовок Location, путь и параметры. Постоянный код следует включать после проверки: браузеры и поисковые роботы могут надолго запомнить ошибочный адрес.
Выберите канонический вариант
До конфигурации зафиксируйте решение:
- HTTPS обязателен для основного сайта;
- используется домен с
wwwили без него; - сохраняются ли путь и query string;
- какие старые URL должны вести на новые;
- где заканчивается TLS — на этом Nginx или на внешнем proxy.
DNS каждого исходного имени должен вести в вашу инфраструктуру, а TLS-сертификат — покрывать имена, которые принимают HTTPS. Редирект не исправляет отсутствующий сертификат: браузер устанавливает TLS до получения HTTP-ответа.
Перенаправьте HTTP на HTTPS
Оба HTTP-варианта можно принять одним блоком и сразу направить на окончательный HTTPS-адрес. Директива return формирует ответ 302 и заголовок Location без обработки файлов сайта:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 302 https://example.com$request_uri;
}
$request_uri сохраняет исходный путь и аргументы. Начните с 302, проверьте все варианты и только затем замените на выбранный постоянный код. Для большинства обычных GET-страниц применяется 301. Коды 307/308 сохраняют метод и тело запроса, что важно для API, но перенос POST-запросов нужно проектировать отдельно, а не полагаться только на код.
Окончательный HTTPS-блок не перенаправляет запрос, а обслуживает содержимое. Он должен иметь сертификат для example.com и каталог опубликованного сайта:
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
root /var/www/example/current;
index index.html;
}
Пути сертификата — пример структуры ACME-клиента. Используйте фактические файлы и не копируйте приватный ключ в репозиторий.
Отдельно обработайте HTTPS на www
Чтобы https://www.example.com смог вернуть редирект, Nginx должен успешно завершить TLS с сертификатом, включающим www:
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
return 302 https://example.com$request_uri;
}
После теста измените временный код. Не отправляйте все неизвестные хосты на $host: входной заголовок контролирует клиент. Для default server безопаснее отдельный отказ, если у проекта нет осознанной причины обслуживать посторонние имена.
Когда нужен rewrite
rewrite полезен для преобразования структуры пути, например строго известного старого раздела:
location /old-docs/ {
rewrite ^/old-docs/(.*)$ /docs/$1 permanent;
}
Если переносится одна страница без изменения остальных адресов, регулярное выражение не требуется. Точное location = с return явно связывает старый и новый путь:
location = /old-page {
return 302 /new-page;
}
Составьте таблицу старый → новый адрес. Массовое правило с (.*) может отправить разные материалы на нерелевантную страницу и скрыть настоящие 404. Для SEO ценен точный перенос смысла, а не сам факт редиректа.
Не создавайте цепочки и циклы
Плохая цепочка делает два перехода: http://www → https://www → https://без-www. Сразу направляйте каждый контролируемый вариант на финальный адрес.
За внешним балансировщиком внутренний Nginx может видеть HTTP. Если он принимает решение только по $scheme, получится цикл. Настройте внешний proxy, доверенные адреса и передачу исходного протокола как единую схему. Не доверяйте X-Forwarded-Proto от любого клиента.
Проверьте все исходные адреса
Сначала проверьте конфигурацию и примените её через reload. Затем запросите каждый исходный вариант без загрузки тела, а в последней команде попросите curl пройти всю цепочку не более чем за пять переходов:
sudo nginx -t
sudo systemctl reload nginx
curl -I http://example.com/docs/?ref=test
curl -I http://www.example.com/docs/?ref=test
curl -I https://www.example.com/docs/?ref=test
curl -IL --max-redirs 5 http://www.example.com/docs/?ref=test
Проверьте один переход, правильный Location, сохранение пути и отсутствие неожиданного переноса query. Затем проверьте реальную страницу на конечном адресе. Браузерный кеш не заменяет curl с видимыми заголовками.
Если тест не прошёл, верните сохранённый конфиг, выполните nginx -t и reload. Принципы структуры конфигурации разобраны в отдельном уроке, а выпуск HTTPS — в материале про домен, Nginx и HTTPS.
Контроль после публикации
Редиректы нужно проверить не только сразу после reload. Просмотрите access log через несколько часов: старые адреса не должны давать неожиданные 404 или повторяться цепочкой. Обновите canonical и внутренние ссылки в самом сайте, sitemap и внешних настройках — редирект страхует старые переходы, но не заменяет правильные конечные URL.
Если переносится большой раздел, сохраните таблицу соответствий и выборочно проверяйте популярные, вложенные и содержащие кириллицу адреса. Следите, чтобы параметры аналитики сохранялись только там, где они нужны, а чувствительные query-параметры не появлялись в публичных журналах или новом Location.
Первоисточники
- Nginx: ngx_http_rewrite_module — директивы return и rewrite, порядок выполнения.
- Nginx: converting rewrite rules — официальные примеры переноса правил.
Самопроверка
Проверьте, что материал усвоен
Ответьте на все вопросы. Результат сохранится только в этом браузере и будет учтён в статистике прочитанных материалов.
Разбираем коротко
Частые вопросы
Какой код использовать для проверки нового редиректа?
Сначала удобнее временный 302 или 307, потому что браузер и поисковые системы не закрепляют его как окончательный так же агрессивно. После проверки пути, query string и отсутствия циклов переходят к постоянному коду.
Сохраняет ли return 301 https://example.com$request_uri параметры запроса?
Да, переменная request_uri содержит исходный путь вместе с аргументами. Это удобно для перенаправления HTTP на HTTPS, если именно такое сохранение соответствует требуемой логике.
Почему возникает бесконечный цикл редиректов за reverse proxy?
Nginx может видеть внутреннее HTTP-соединение и снова отправлять клиента на HTTPS, хотя внешний прокси уже завершил TLS. Нужно согласовать доверенный заголовок протокола и правила между всеми прокси, а не отключать редирект случайным условием.


