Выберите продукт

SNI и default server в Nginx/Apache: почему отдаётся «чужой» сертификат и как это быстро исправить

Если при HTTPS браузер показывает сертификат другого домена, чаще всего виноваты SNI и выбор default server (или первого vhost). Разберём диагностику через openssl s_client, поиск конфликтов vhost и быстрые правки конфигов Nginx/Apache.
SNI и default server в Nginx/Apache: почему отдаётся «чужой» сертификат и как это быстро исправить

Иногда всё выглядит идеально: сертификат выписан, цепочка собрана, DNS указывает на нужный IP — а при заходе на сайт браузер показывает mismatched certificate (сертификат выдан на другой домен). В реальности это почти всегда означает одно: на этапе TLS handshake веб-сервер выбрал не тот виртуальный хост — сработал default server или «первый попавшийся» vhost, и клиенту отдали «чужой» сертификат.

Ниже разберём, как SNI влияет на выбор сертификата, почему порядок vhost критичен в Apache, как правильно задавать listen 443 default_server в Nginx и как диагностировать проблему через openssl s_client. В конце — короткий runbook, который можно держать под рукой.

Что такое SNI и где ломается выбор сертификата

SNI (Server Name Indication) — расширение TLS, которое позволяет клиенту (браузеру, curl, мониторингу) передать имя хоста во время TLS-рукопожатия. Это принципиально важно, потому что сертификат нужно выбрать до того, как сервер прочитает HTTP-запрос и заголовок Host.

Если SNI не передан или не совпал с конфигурацией, сервер видит только IP:порт. При нескольких HTTPS-сайтах на одном IP он вынужден выбрать «дефолтный» сертификат — отсюда и классическая симптоматика: один домен работает, а остальные внезапно показывают сертификат «соседа».

Ключевой момент: при HTTPS выбор сертификата происходит до обработки HTTP. Если SNI не передан или не матчится на vhost — будет выбран default server (или первый vhost), и вы увидите wrong vhost.

Типовые причины mismatched certificate

  • Клиент не отправляет SNI (старые клиенты, некоторые библиотеки, специфические проверки).
  • На 443 неправильно определён default vhost/server block и он перехватывает запросы.
  • В Nginx неверные server_name, нет блока под нужный домен или есть конфликт server blocks.
  • В Apache порядок vhost влияет на то, кто станет «первым» (а значит дефолтом при отсутствии совпадения).
  • Проверяете не тот IP (особенно часто — из-за AAAA/IPv6).
  • TLS терминирует внешний узел (балансировщик/прокси), и сертификат берётся уже там.

Как быстро подтвердить проблему: openssl s_client

Сначала нужно увидеть, какой сертификат реально выдаётся с конкретного IP и с указанным SNI. Самый быстрый инструмент — openssl s_client.

Проверка с SNI (правильный способ)

openssl s_client -connect 203.0.113.10:443 -servername example.com -showcerts

В выводе проверьте:

  • subject= и issuer= — какой сертификат отдали.
  • SAN (Subject Alternative Name): есть ли DNS:example.com.
  • Если видите сертификат другого домена — это подтверждение wrong vhost/default server.

Проверка без SNI (чтобы увидеть дефолт)

openssl s_client -connect 203.0.113.10:443 -showcerts

Если без -servername вы получаете «левый» сертификат — это нормально для нескольких сайтов на одном IP: сервер отдаёт дефолтный. Важно, чтобы с SNI выдавался правильный.

Частая ловушка: проверка по домену, но попали не на тот IP

Когда вы делаете:

openssl s_client -connect example.com:443 -servername example.com

OpenSSL резолвит домен сам. Если у домена несколько A/AAAA или вы стоите за прокси/балансировщиком — легко проверить «не тот» узел. Для чистоты теста фиксируйте IP в -connect и отдельно задавайте имя в -servername.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Перед покупкой и установкой сертификата полезно заранее определить, какой vhost будет дефолтным на 443. Так вы избежите ситуации, когда на «неизвестные» домены и проверки без SNI отдаётся сертификат боевого сайта. Если нужен выпуск и продление в одном месте — посмотрите SSL-сертификаты (GlobalSign).

Проверка сертификата через openssl s_client с указанием SNI

Nginx: как выбирается default server и почему важен listen

В Nginx выбор server block на 443 идёт по логике: адрес:порт, затем SNI (по server_name), и если подходящего имени нет — используется default server для этого listen.

Практическое правило: для каждого listen на 443 (и для IPv4, и для IPv6) должен быть либо корректный набор server_name, либо аккуратный дефолтный сервер с нейтральным сертификатом и понятным поведением. Иначе «чужой» сертификат будет всплывать в самых неожиданных местах: у части пользователей, по IPv6, в мониторинге, в API-клиентах.

Симптомы неправильного default server в Nginx

  • Добавили новый сайт, но Nginx продолжает отдавать сертификат «старого» сайта.
  • Один домен работает, второй получает сертификат первого домена.
  • При наличии IPv6 проблема проявляется только по AAAA.

Правильный дефолтный сервер на 443: «глушилка»

Практичный подход — отдельный дефолтный server block, который явно помечен default_server, использует нейтральный сертификат и возвращает короткий ответ. Часто используют return 444; (Nginx закроет соединение), либо 400/421 по вашей политике.

server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;

    server_name _;

    ssl_certificate     /etc/ssl/certs/default/fullchain.pem;
    ssl_certificate_key /etc/ssl/private/default/privkey.pem;

    return 444;
}

А каждый реальный сайт должен иметь свой server block с корректным server_name и своим сертификатом:

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name example.com www.example.com;

    ssl_certificate     /etc/ssl/certs/example.com/fullchain.pem;
    ssl_certificate_key /etc/ssl/private/example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
    }
}

Где чаще всего ошибаются с nginx server_name

  • Опечатка в домене или лишняя точка.
  • Забыли www (или наоборот), а сертификат выписан на оба имени.
  • Слишком общий wildcard в одном месте перекрывает ожидания в другом.
  • Дубликаты server_name в разных include-файлах: Nginx выберет один (обычно первый), а второй будет «жить своей жизнью».

Проверка, какой сервер Nginx считает default и какие имена видит

nginx -T

Команда печатает итоговую конфигурацию со всеми include. Ищите блоки listen 443, отметки default_server и соответствие server_name доменам и сертификатам.

Если размещаете много сайтов и хотите предсказуемо контролировать IPv4/IPv6, порядок include и TLS-настройки без сюрпризов, удобнее делать это на отдельном сервере: на VDS проще держать конфигурацию Nginx/Apache «под себя».

Apache: порядок vhost, выбор сертификата и «первый» виртуальный хост

В Apache ситуация исторически сложнее из-за разных поколений конфигурации, но принцип тот же: для HTTPS сертификат выбирается до обработки HTTP-заголовков. С SNI Apache способен отдать правильный сертификат для нужного имени. Без SNI (или при несовпадении) он отдаст сертификат из дефолтного vhost — обычно это первый объявленный виртуальный хост на этом адресе:порту.

Почему «первый» становится дефолтом

Если у вас несколько vhost вида <VirtualHost *:443>, Apache пытается сматчить имя по SNI и ServerName/ServerAlias. Но если матч не найден, в игру вступает «первый». Поэтому не стоит держать «боевой» домен первым, если сервер обслуживает много сайтов.

Что делать с NameVirtualHost

NameVirtualHost — директива из старых версий Apache (в 2.4 обычно не требуется). В смешанных конфигурациях она иногда встречается, и администраторы пытаются «лечить» ею HTTPS-проблемы.

На практике лечится это не NameVirtualHost, а корректной структурой vhost на 443, отсутствием дублей и предсказуемым дефолтом. Если вы видите NameVirtualHost *:443, проверьте версию Apache и не дублируются ли определения: дубли могут приводить к неожиданному wrong vhost.

Пример «дефолтного» Apache vhost на 443

<VirtualHost *:443>
    ServerName default.invalid

    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/default/fullchain.pem
    SSLCertificateKeyFile /etc/ssl/private/default/privkey.pem

    <Location />
        Require all denied
    </Location>
</VirtualHost>

Затем — реальные сайты отдельными vhost, где имя и сертификат совпадают:

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com

    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/ssl/private/example.com/privkey.pem

    ProxyPreserveHost On
    ProxyPass / http://127.0.0.1:8080/
    ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>

Если хотите освежить в памяти различия подходов и типовые грабли при миграциях конфигов, держите ссылку: сравнение Nginx и Apache для админов: синтаксис, vhost и практические отличия.

Схема выбора виртуального хоста по SNI и fallback на default server

Сценарии из практики: почему всё «вдруг сломалось»

1) Добавили IPv6 и забыли про него

Очень частый кейс: по IPv4 всё правильно, а по IPv6 браузер видит другой сертификат. Причина — на [::]:443 работает другой default server, либо вообще другой server block/vhost, либо Nginx/Apache слушает IPv6 иначе, чем IPv4.

Диагностика: отдельно проверяйте IPv6-адрес в openssl s_client -connect и убедитесь, что конфиг содержит явный listen [::]:443 там, где нужно.

2) Два server block в Nginx делят одно имя

Когда в разных include-файлах случайно встречается одинаковый server_name example.com, Nginx не «объединяет» их. Он выберет один (обычно первый по порядку загрузки конфигов), а второй будет работать непредсказуемо. Это легко приводит к mismatched certificate: сертификат лежит в одном блоке, а запрос попадает в другой.

3) Клиент без SNI (мониторинг, старый Java, устройство)

Даже если ваш браузер всегда шлёт SNI, какие-то внешние системы могут не слать. Тогда они всегда увидят сертификат default server. Это не всегда «баг», но важно, чтобы дефолтный сертификат не «светил» ваши домены и не пугал пользователей или заказчиков отчётов.

Пошаговый план исправления (runbook)

  1. Зафиксируйте, на каком IP проявляется проблема (IPv4 и IPv6 отдельно). Снимите вывод openssl s_client с -servername и без него.

  2. Убедитесь, что нужный vhost/server block существует и содержит корректный server_name (Nginx) или ServerName/ServerAlias (Apache).

  3. Проверьте дефолт на 443: Nginx — явный listen 443 ssl default_server (и для IPv6 тоже); Apache — отдельный нейтральный vhost первым в порядке загрузки.

  4. Проверьте дубли и порядок: Nginx — через nginx -T; Apache — через вывод загруженной конфигурации вашего дистрибутива (важно увидеть реальный порядок подключений).

  5. Сделайте reload конфигурации и сразу повторите openssl s_client на IP (IPv4/IPv6).

  6. Если проблема осталась — ищите внешний TLS-терминатор: балансировщик, прокси, панель управления, отдельный сервис на 443.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Если конфигов становится много (несколько сайтов, IPv6, разные окружения), проще вынести их на отдельный сервер и держать порядок: на VDS удобно разруливать default vhost, SNI и сертификаты без ограничений виртуального хостинга.

Мини-чеклист: как сделать так, чтобы wrong vhost больше не возвращался

  • Держите отдельный дефолтный HTTPS-виртуальный хост (не «боевой» сайт).
  • Явно задавайте default для IPv4 и IPv6.
  • Не допускайте дублей server_name/ServerName в разных файлах.
  • Проверяйте реальный сертификат на выдаче через openssl s_client при любых изменениях TLS/виртуальных хостов.
  • Документируйте, какой vhost является default и зачем.

Если параллельно настраиваете «внешнюю» безопасность сайта, проверьте и заголовки: неправильный vhost иногда тянет за собой чужие политики HSTS/CSP. По теме: настройка security headers в Nginx/Apache.

Поделиться статьей

Вам будет интересно

Health-check и graceful failover в Nginx и HAProxy: active-passive, веса, slow start OpenAI Статья написана AI (GPT 5)

Health-check и graceful failover в Nginx и HAProxy: active-passive, веса, slow start

Разбираем, как настроить быстрый и при этом «мягкий» failover между бэкендами: отличие L4 и L7 health-check, active-passive и веса ...
nftables + WireGuard: NAT, forward chain и policy routing без сюрпризов OpenAI Статья написана AI (GPT 5)

nftables + WireGuard: NAT, forward chain и policy routing без сюрпризов

Разбираем типовые схемы WireGuard на Linux с nftables: интернет через wg0, доступ LAN в VPN, split-tunnel и policy routing. Дам ра ...
Linux 6.x: UDP GRO/GSO и USO (tx-udp-segmentation) для ускорения VPN OpenAI Статья написана AI (GPT 5)

Linux 6.x: UDP GRO/GSO и USO (tx-udp-segmentation) для ускорения VPN

UDP GRO/GSO и USO в Linux 6.x могут заметно поднять throughput в WireGuard и других UDP‑VPN, снижая нагрузку на CPU и softirq. В с ...