Редирект на HTTPS в Nginx для нескольких доменов и поддоменов | AdminWiki

Редирект на HTTPS в Nginx для нескольких доменов и поддоменов

05 сентября 2026 16 мин. чтения
Содержание статьи

Короткий ответ: как сделать редирект в 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-запрос сопоставляется с виртуальным хостом.

При одинаковом адресе и порте правила выбора имени имеют такой порядок:

  1. точное имя, например example.org;
  2. самый подходящий wildcard с префиксом, например *.example.org;
  3. самый подходящий wildcard с суффиксом, например mail.*;
  4. регулярное выражение в порядке его появления в конфигурации.

Если подходящего имени нет, запрос получает виртуальный хост по умолчанию для этого сочетания адреса и порта.

Точные имена для доменов с одинаковым редиректом

Когда несколько доменов должны сохранить свое имя и перейти на 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-блоков. Эти три списка должны согласовываться.

Что проверитьПримерРиск пропуска
Apexexample.orgКорневой домен попадет в default-хост
Основной алиасwww.example.orgЧасть посетителей получит другой сайт
Технические именаapi.example.org, admin.example.orgСервис продолжит принимать HTTP или получит 404
Wildcard-зона*.example.orgНовые поддомены не попадут в ожидаемый vhost
Сетевые записиA и AAAAIPv4 и 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 или путь backendnginx -T, проверка server_name и location

Сопоставляйте результат HTTP и HTTPS. Успешный 301 на порту 80 еще не подтверждает правильный сертификат или backend на 443.

Итоговый чек-лист перед применением

  1. Составьте полный список apex-доменов, www, технических имен и реально используемых wildcard-поддоменов.
  2. Проверьте записи A и AAAA для каждого имени и убедитесь, что IPv4 и IPv6 ведут на нужный сервер.
  3. Создайте отдельный default_server на 80 для неизвестных запросов.
  4. Соберите доверенные имена в одном HTTP-блоке, если им нужен одинаковый редирект.
  5. Для каждого сайта проверьте отдельный HTTPS-блок на 443 с правильным root или proxy_pass.
  6. Сверьте имена в server_name с SAN сертификатов и помните, что wildcard не покрывает apex автоматически.
  7. Проверьте контекст каждого include. Snippet с return подключайте внутрь server или подходящего location.
  8. Для backend проверьте Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto и WebSocket-заголовки, если они нужны приложению.
  9. Если TLS завершает CDN или балансировщик, ограничьте доступ к origin и принимайте X-Forwarded-Proto только от доверенных адресов.
  10. Выполните nginx -t, изучите nginx -T, примените systemctl reload nginx, затем проверьте каждый адрес через curl -I и curl --resolve.

Централизуйте одинаковый HTTP-редирект, а сертификаты, приложения и правила HTTPS храните рядом с конкретными сайтами. Такая граница упрощает аудит конфигурации и помогает быстро найти источник ошибки: в списке имен, default-хосте, TLS, CDN или backend.

Поделиться:
Сохранить гайд? В закладки браузера