Короткий ответ: как сделать редирект в Nginx с 80 на 443 для нескольких доменов
Для нескольких доменов с одинаковой политикой перехода используйте один HTTP server-блок. Перечислите доверенные имена в server_name и задайте общий ответ return 301 https://$host$request_uri;. Так путь, параметры запроса и исходный домен сохранятся при переходе на HTTPS.
HTTPS-виртуальные хосты на порту 443 оставьте отдельными. В них задаются сертификат, root, proxy_pass, заголовки и настройки конкретного сайта. Такой подход убирает дублирование редиректа на порту 80, но сохраняет правильный выбор сертификата и приложения для каждого имени.
Сервер для такой схемы можно разместить на VDS или VPS. Например, облачный сервер Timeweb Cloud подходит для размещения нескольких сайтов, reverse proxy и других сервисов на одной инфраструктуре.
Рекомендуемая схема конфигурации
Начните с отдельного виртуального хоста для неизвестных значений Host. Он должен быть default_server на IPv4 и IPv6 и возвращать ошибку. Запросы с корректными именами передайте в общий HTTP-блок.
# Неизвестные Host на IPv4 и IPv6
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 400;
}
# Общий редирект для обслуживаемых имен
server {
listen 80;
listen [::]:80;
server_name example.org www.example.org example.net *.example.org;
return 301 https://$host$request_uri;
}
Имя _ в первом блоке выступает как обычное условное имя. Оно не превращает блок в универсальный wildcard. Роль обработчика неизвестных запросов дает параметр default_server.
Переменная $host содержит нормализованное имя запроса, а $request_uri сохраняет исходный URI вместе с query string. Запрос к http://example.org/docs/page?mode=test получит заголовок Location: https://example.org/docs/page?mode=test.
Для IPv6 нужна строка listen [::]:80;. Если DNS публикует запись AAAA, но Nginx слушает только IPv4, часть клиентов попадет на другой сервер или получит ошибку соединения. Записи A и AAAA должны указывать на тот узел, где загружена эта конфигурация.
Почему HTTPS-виртуальные хосты все равно остаются отдельными
HTTP-редирект работает после приема обычного HTTP-запроса. На порту 443 Nginx сначала участвует в TLS handshake, выбирает сертификат по SNI, а затем обрабатывает HTTP-запрос и его заголовок Host.
| Порт | Задача | Типичные директивы |
|---|---|---|
80 | Принять HTTP и перенаправить клиента | listen, server_name, return |
443 | Завершить TLS и обслужить сайт | ssl_certificate, root, proxy_pass |
Объединение всех доменов в одном блоке на 443 часто приводит к неправильному сертификату или попаданию запроса в чужое приложение. Один HTTPS-блок можно использовать для нескольких имен, если у них совпадают сертификат, document root, backend, заголовки и правила доступа.
Для разных сайтов безопаснее завести отдельные блоки на 443. HTTP-конфигурация отвечает за общую политику перехода, HTTPS-конфигурация описывает уникальную часть каждого сайта.
Как Nginx выбирает server_name для домена и поддомена
Nginx сначала учитывает адрес и порт из listen. Для обычного HTTP затем сопоставляется заголовок Host. Для HTTPS имя из SNI участвует в выборе TLS-параметров и сертификата, после чего HTTP-запрос сопоставляется с виртуальным хостом.
При одинаковом адресе и порте правила выбора имени имеют такой порядок:
- точное имя, например
example.org; - самый подходящий wildcard с префиксом, например
*.example.org; - самый подходящий wildcard с суффиксом, например
mail.*; - регулярное выражение в порядке его появления в конфигурации.
Если подходящего имени нет, запрос получает виртуальный хост по умолчанию для этого сочетания адреса и порта.
Точные имена для доменов с одинаковым редиректом
Когда несколько доменов должны сохранить свое имя и перейти на HTTPS, их можно записать в одном параметре server_name.
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com example.net;
return 301 https://$host$request_uri;
}
В этом примере example.com, www.example.com и example.net получают одинаковый код ответа, но каждый сохраняет собственный host. Такая запись касается только HTTP-блока. Она не объединяет сайты на 443 и не выдает сертификаты для перечисленных доменов.
Wildcard-поддомены и apex-домен
Корневое имя доменной зоны называют apex-доменом. Для зоны example.org это само имя example.org, а api.example.org и admin.example.org считаются поддоменами.
server {
listen 80;
listen [::]:80;
server_name example.org *.example.org;
return 301 https://$host$request_uri;
}
Запись *.example.org не заменяет example.org, поэтому apex нужно указывать отдельно. В Nginx wildcard с префиксом может сопоставляться с именами под этой зоной, включая вложенные имена. DNS-запись wildcard и правило server_name не создают друг друга: DNS должен отдельно направить нужные имена на сервер.
Wildcard в Nginx и wildcard-сертификат решают разные задачи. Сертификат *.example.org покрывает, например, api.example.org, но не покрывает example.org. Для api.dev.example.org нужен сертификат с подходящим SAN или другим wildcard-именем. Сертификат для *.example.org не покрывает домен example.net.
Конфликты server_name и default_server
Одинаковое точное имя нельзя без причины размещать в нескольких блоках с одним и тем же listen. При загрузке Nginx может сообщить о конфликтующем имени и проигнорировать одну из записей.
# Плохой пример: имя повторяется на одном порту
server {
listen 80 default_server;
server_name example.org;
}
server {
listen 80;
server_name example.org;
}
Пересекающиеся wildcard-правила требуют проверки приоритета. Например, запрос к api.example.org может попасть в более точный блок, чем ожидалось, если в конфигурации есть несколько подходящих шаблонов.
default_server назначается отдельно для каждого адреса и порта. Блок по умолчанию для 80 не становится блоком по умолчанию для 443. Назначьте default-хост явно на IPv4 и IPv6, а затем проверьте активную конфигурацию командами nginx -t и nginx -T.
Не отправляйте неизвестный host на https://$host$request_uri. Такой редирект позволяет клиенту использовать произвольное имя в целевом URL. Для неизвестных запросов ответ 400 или отдельная политика отказа безопаснее.
Единый HTTP server-блок для редиректа без дублирования
Один redirect-сервер подходит, когда всем перечисленным именам нужен переход на соответствующий HTTPS-адрес. Внутри него достаточно одного return. Это уменьшает число мест, где администратор может забыть обновить код ответа или сохранить query string.
Когда один redirect-сервер подходит для всех доменов
Общий блок выбирайте при четырех условиях:
- каждый домен должен сохранить собственный host после перехода;
- для всех имен нужен одинаковый код ответа и одинаковая политика HTTPS;
- на HTTP не нужно отдавать отдельную страницу или направлять часть путей в разные приложения;
- ACME-проверка либо корректно проходит через редирект, либо для нее подготовлен отдельный обработчик.
Для обычной схемы весь HTTP-трафик идет в один блок, а Nginx сразу возвращает заголовок Location. Backend на этом этапе не вызывается, поэтому приложение не тратит ресурсы на запросы, которые должны перейти на TLS.
Что проверить в списке имен
Соберите список имен из DNS, сертификатов и активных HTTPS-блоков. Эти три списка должны согласовываться.
| Что проверить | Пример | Риск пропуска |
|---|---|---|
| Apex | example.org | Корневой домен попадет в default-хост |
| Основной алиас | www.example.org | Часть посетителей получит другой сайт |
| Технические имена | api.example.org, admin.example.org | Сервис продолжит принимать HTTP или получит 404 |
| Wildcard-зона | *.example.org | Новые поддомены не попадут в ожидаемый vhost |
| Сетевые записи | A и AAAA | IPv4 и IPv6 приведут к разным серверам |
| Сертификаты | SAN для каждого имени | Появится ошибка имени сертификата |
Wildcard в списке удобен для общей HTTP-политики, но его нельзя считать подтверждением того, что любой поддомен готов на 443. Для каждого фактически используемого имени проверьте отдельный HTTPS-виртуальный хост и покрытие сертификатом.
Когда общий блок становится слишком общим
Разделяйте HTTP-блоки, если домены должны вести на разные целевые адреса. Типичный пример: старый домен направляется на новый, а рабочий домен сохраняет свое имя.
server {
listen 80;
server_name old.example.org;
return 301 https://new.example.org$request_uri;
}
server {
listen 80;
server_name example.org www.example.org;
return 301 https://$host$request_uri;
}
Отдельная конфигурация нужна для legacy-URL, разных правил www и non-www, служебных поддоменов с ограничением доступа, health-check, который ожидает ответ 200, и разных обработчиков /.well-known/acme-challenge/.
Если ACME-клиент должен получать файл по HTTP, перенесите редирект в location /, а challenge обработайте отдельным location.
server {
listen 80;
listen [::]:80;
server_name example.org www.example.org;
location ^~ /.well-known/acme-challenge/ {
root /var/www/acme;
}
location / {
return 301 https://$host$request_uri;
}
}
При серверном return без location Nginx завершает обработку раньше, поэтому исключение для challenge нужно проектировать вместе со структурой location. Проверьте реальный путь, права на каталог и поведение используемого ACME-клиента.
Как использовать include-файлы для общих правил Nginx
include вставляет содержимое файла в место вызова во время разбора конфигурации. Это текстовая вставка, а не функция с параметрами. Файл должен содержать директивы, допустимые в текущем контексте.
Общий snippet только с return 301
Для одного повторяющегося правила создайте короткий snippet. В него не нужно помещать listen, server_name или целый блок server.
# /etc/nginx/snippets/redirect-to-https.conf
return 301 https://$host$request_uri;
Подключайте его внутри HTTP server-блока:
server {
listen 80;
listen [::]:80;
server_name example.org www.example.org example.net *.example.org;
include /etc/nginx/snippets/redirect-to-https.conf;
}
Директива return допустима в контексте server, поэтому этот snippet подходит для показанного места. Если подключить его внутри http, Nginx сообщит о недопустимом контексте. Проверка nginx -t должна выполняться после каждого изменения.
Структуру основного файла, conf.d и snippets удобно сверить с разбором структуры nginx.conf и практических конфигураций.
Include полного виртуального хоста
Когда у каждого сайта свой сертификат и backend, полный виртуальный хост храните в отдельном файле. Общие правила подключайте внутрь нужного блока.
/etc/nginx/
├── nginx.conf
├── conf.d/
│ ├── example.org.conf
│ └── example.net.conf
└── snippets/
├── redirect-to-https.conf
├── proxy-common.conf
└── security-headers.conf
В Debian-подобных системах похожую роль часто выполняют каталоги sites-available и sites-enabled. Название каталога не меняет правила Nginx: итоговый файл должен попасть под директиву include в основном конфиге.
Разделите snippets по назначению. В redirect-to-https.conf держите редирект, в SSL-файле общие параметры TLS, в proxy-common.conf заголовки reverse proxy. Сертификаты, root, proxy_pass и server_name оставляйте рядом с конкретным сайтом, если они отличаются.
Когда нужны шаблоны и генерация конфигурации
Для нескольких доменов ручной список в одном server_name обычно читается лучше генератора. При десятках или сотнях сайтов ручная правка становится отдельным источником ошибок: теряются имена, сертификаты, адреса backend и даты удаления старых vhost.
include не умеет перебирать домены, принимать аргументы или выполнять циклы. Для массового выпуска конфигураций используйте Ansible, Helm, Nginx Proxy Manager или другой генератор, который хранит данные сайтов отдельно от шаблона.
Сгенерированный результат проверяйте как обычную конфигурацию:
sudo nginx -t
sudo nginx -T
sudo systemctl reload nginx
Команда nginx -T показывает объединенную активную конфигурацию. По ней можно проверить, что нужный файл действительно подключен, имя не продублировано, а snippet оказался внутри ожидаемого server-блока.
Отдельные HTTPS-виртуальные хосты на 443 для сайтов и поддоменов
После HTTP-редиректа клиент обращается к тому же имени через TLS. На порту 443 должен существовать блок с этим именем, подходящим сертификатом и обработчиком контента.
Сайт со статическим root
Для статического сайта Nginx сам читает файлы из каталога, указанного в root.
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.org www.example.org;
ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;
root /var/www/example.org/public;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Сертификат в этом блоке должен содержать оба имени: example.org и www.example.org. Если файл сертификата или ключа отсутствует, nginx -t не пройдет и reload не применит конфигурацию.
Параметры TLS, HSTS и проверку сертификатов удобно сверить с руководством по SSL/TLS и HTTPS в Nginx. Редирект не заменяет настройку сертификата: он лишь сообщает клиенту, куда перейти.
Reverse proxy на backend или Docker-контейнер
Для приложения Nginx завершает TLS на 443, затем передает запрос backend. Директива proxy_pass должна находиться в HTTPS-виртуальном хосте, а не в HTTP-сервере, который возвращает редирект.
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name app.example.org;
ssl_certificate /etc/nginx/tls/example.org/fullchain.pem;
ssl_certificate_key /etc/nginx/tls/example.org/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
Заголовок Host передает приложению исходное имя, X-Real-IP содержит адрес клиента со стороны Nginx, а X-Forwarded-Proto сообщает backend, что внешнее соединение использовало HTTPS. Для контейнера вместо 127.0.0.1:8080 может использоваться имя сервиса в общей Docker-сети.
Если приложение использует WebSocket, добавьте HTTP/1.1 и upgrade-заголовки. Переменная map должна находиться в контексте http, не внутри server.
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl;
server_name app.example.org;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
}
}
Отдельные примеры маршрутизации, передачи реального IP и диагностики ошибок 502 и 504 есть в руководстве по настройке обратного прокси на Nginx.
Сертификаты для wildcard и отдельных доменов
Wildcard-сертификат и wildcard в server_name должны совпадать по зоне, но один механизм не настраивает другой. Для схемы с example.org и *.example.org обычно нужны сертификат с SAN для apex и wildcard, либо два сертификата.
Для доменов разных зон, например example.org и example.net, используйте отдельные сертификаты или один сертификат с обоими SAN. В HTTPS-блоке указывайте тот сертификат, который покрывает все имена из его server_name.
SNI позволяет одному IP-адресу обслуживать разные сертификаты. Если клиент не передал SNI или имя не совпало, Nginx может отдать сертификат default-виртуального хоста. Поэтому проверяйте каждый домен с сохранением имени в URL, а не обращайтесь к серверу только по IP.
Если TLS завершается на CDN или балансировщике
При CDN, load balancer или reverse proxy схема соединений может выглядеть так: клиент подключается к CDN по HTTPS, а CDN обращается к origin по HTTP. Nginx видит $scheme http и отправляет редирект на HTTPS. CDN повторяет запрос к origin по HTTP, и цикл продолжается.
Как распознать redirect loop
Признаки цикла: браузер сообщает о слишком большом числе перенаправлений, а curl -IL показывает повторяющиеся ответы 301 или 302 с одинаковым либо чередующимся заголовком Location.
curl -IL --max-redirs 5 'https://example.org/'
Проверьте SSL-режим CDN, схему соединения между CDN и origin, значение X-Forwarded-Proto и фактический порт, на который балансировщик подключается к Nginx. Если CDN уже перенаправляет HTTP на HTTPS, origin может принимать внутренний HTTP без повторного редиректа.
Безопасная работа с X-Forwarded-Proto
Заголовок X-Forwarded-Proto нельзя слепо принимать от любого клиента. Иначе прямой HTTP-запрос сможет передать поддельное значение https, обойти редирект и изменить логику приложения.
Ограничьте доступ к origin адресами CDN или балансировщика на уровне firewall. После этого Nginx может использовать заголовок от доверенного источника. Пример ниже показывает проверку адресной сети прокси через geo и схемы через map. Сеть 192.0.2.0/24 замените на реальные адреса вашего балансировщика.
# Контекст http
geo $from_trusted_proxy {
default 0;
192.0.2.0/24 1;
}
map "$from_trusted_proxy:$http_x_forwarded_proto" $redirect_to_https {
default 1;
"1:https" 0;
}
server {
listen 80;
server_name example.org www.example.org;
if ($redirect_to_https) {
return 301 https://$host$request_uri;
}
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
}
}
Конструкция if с единственным return безопасна для такого условия. Убедитесь, что список сетей доверенного прокси актуален, а прямой доступ к origin закрыт. Если CDN передает другое значение заголовка, добавьте его в карту явно после проверки поведения конкретного сервиса.
301 или 302: какой редирект использовать при переносе на HTTPS
Во время проверки конфигурации используйте временный код. Для обычных GET-запросов подойдет 302, а для запросов, где нужно сохранить HTTP-метод и тело, удобнее 307.
return 302 https://$host$request_uri;
После проверки всех доменов замените код на 301. Постоянный редирект подходит для окончательной политики HTTPS, но браузеры, поисковые системы и CDN могут кэшировать его. Ошибочный URL после публикации исправить сложнее.
Сохранение host, пути и параметров запроса
Конструкция https://$host$request_uri переносит клиента на тот же ресурс по TLS. Переменная $host берет имя запроса без необходимости копировать его вручную, а $request_uri сохраняет путь и query string.
return 301 https://$host$request_uri;
Для общего редиректа используйте $host, если неизвестные имена уже отсекает отдельный default-хост. Переменная $http_host может содержать порт и напрямую отражает входной заголовок, поэтому она требует более строгой фильтрации.
Редирект на канонический домен
Переход на HTTPS и выбор канонического домена решают разные задачи. Если основным считается www.example.org, редирект с example.org должен сразу вести на итоговый URL, без цепочки сначала на HTTPS без www, затем на HTTPS с www.
Для двух имен можно использовать map в контексте http:
map $host $canonical_host {
default $host;
example.org www.example.org;
www.example.org www.example.org;
}
server {
listen 80;
listen [::]:80;
server_name example.org www.example.org;
return 301 https://$canonical_host$request_uri;
}
Добавьте HTTPS-блок для канонического имени и сертификат, который покрывает оба host. Для разных доменных зон задайте явные правила, чтобы домен не попал на чужой канонический адрес.
Проверка редиректа на HTTPS в Nginx через curl
Проверяйте конфигурацию в четыре шага: синтаксис, активное содержимое, HTTP-ответ и TLS-соединение. Тестируйте каждый домен, а для wildcard выберите хотя бы один реально настроенный поддомен.
Проверка HTTP-ответа и заголовка Location
Сначала проверьте синтаксис и примените конфигурацию:
sudo nginx -t
sudo nginx -T
sudo systemctl reload nginx
Затем запросите HTTP-адрес без автоматического перехода:
curl -I 'http://example.org/docs/page?mode=test'
curl -I 'http://www.example.org/docs/page?mode=test'
curl -I 'http://example.net/docs/page?mode=test'
curl -I 'http://api.example.org/health?full=1'
В ответе ожидайте 301 после публикации постоянного правила или 302 во время теста. Заголовок должен сохранить имя, путь и параметры:
HTTP/1.1 301 Moved Permanently
Location: https://example.org/docs/page?mode=test
Проверьте неизвестное имя отдельно. Оно должно попасть в default-хост и получить 400, если именно такой ответ задан в конфигурации.
curl -I --resolve unknown.example.org:80:203.0.113.10 'http://unknown.example.org/'
Для цепочки редиректов используйте -L и ограничьте число переходов:
curl -IL --max-redirs 5 'http://example.org/docs/page?mode=test'
Проверка HTTPS и сертификата
Параметр --resolve позволяет проверить конкретный IP, сохранив имя в URL. Благодаря этому curl отправляет нужный SNI и заголовок Host, даже если DNS еще не обновился.
curl -vI --resolve example.org:443:203.0.113.10 'https://example.org/docs/page?mode=test'
curl -vI --resolve www.example.org:443:203.0.113.10 'https://www.example.org/'
curl -vI --resolve api.example.org:443:203.0.113.10 'https://api.example.org/health'
Проверьте имя сертификата, срок действия, цепочку доверия и содержимое ответа. Если используется wildcard, отдельно проверьте apex-домен: он требует явного покрытия сертификатом.
Для проверки маршрута по IPv4 и IPv6 выполните два запроса:
curl -4I 'http://example.org/'
curl -6I 'http://example.org/'
Ключ -k временно отключает проверку сертификата и годится только для диагностики сетевого маршрута. Успешный ответ с -k не подтверждает, что сертификат доверенный и подходит имени.
Типовые ошибки и их диагностика
| Симптом | Вероятная причина | Проверка |
|---|---|---|
| Редирект не срабатывает | Запрос идет на другой listen, не загружен файл или IPv6 указывает на другой узел | nginx -T, curl -4I, curl -6I |
| Открывается не тот сайт | Сработал default_server, имя отсутствует или продублировано | Проверить все server_name и порядок блоков |
| Ошибка wrong certificate | Имя отсутствует в SAN, выбран default-сертификат или SNI не совпал | curl -vI с правильным именем и --resolve |
| Бесконечный редирект | CDN подключается к origin по HTTP и не передает доверенный признак исходного HTTPS | Проверить X-Forwarded-Proto и SSL-режим CDN |
| Wildcard не работает для apex | *.example.org не включает example.org | Добавить apex в server_name и SAN сертификата |
| Конфигурация не загружается | Snippet подключен не в том контексте или содержит запрещенную директиву | nginx -t и строка ошибки в выводе |
| После HTTPS появляется 404 | Имя попало в другой HTTPS-блок, неверен root или путь backend | nginx -T, проверка server_name и location |
Сопоставляйте результат HTTP и HTTPS. Успешный 301 на порту 80 еще не подтверждает правильный сертификат или backend на 443.
Итоговый чек-лист перед применением
- Составьте полный список apex-доменов,
www, технических имен и реально используемых wildcard-поддоменов. - Проверьте записи A и AAAA для каждого имени и убедитесь, что IPv4 и IPv6 ведут на нужный сервер.
- Создайте отдельный
default_serverна80для неизвестных запросов. - Соберите доверенные имена в одном HTTP-блоке, если им нужен одинаковый редирект.
- Для каждого сайта проверьте отдельный HTTPS-блок на
443с правильнымrootилиproxy_pass. - Сверьте имена в
server_nameс SAN сертификатов и помните, что wildcard не покрывает apex автоматически. - Проверьте контекст каждого
include. Snippet сreturnподключайте внутрьserverили подходящегоlocation. - Для backend проверьте
Host,X-Real-IP,X-Forwarded-For,X-Forwarded-Protoи WebSocket-заголовки, если они нужны приложению. - Если TLS завершает CDN или балансировщик, ограничьте доступ к origin и принимайте
X-Forwarded-Protoтолько от доверенных адресов. - Выполните
nginx -t, изучитеnginx -T, применитеsystemctl reload nginx, затем проверьте каждый адрес черезcurl -Iиcurl --resolve.
Централизуйте одинаковый HTTP-редирект, а сертификаты, приложения и правила HTTPS храните рядом с конкретными сайтами. Такая граница упрощает аудит конфигурации и помогает быстро найти источник ошибки: в списке имен, default-хосте, TLS, CDN или backend.