Когда на одном IP живут десятки сайтов, а браузер упорно показывает «не тот сертификат» или вместо HTTP/2 вы видите HTTP/1.1, почти всегда виноваты две части TLS-рукопожатия: SNI и ALPN. Первая сообщает серверу имя хоста ещё до выбора сертификата, вторая — согласовывает прикладной протокол (например, HTTP/2) поверх TLS.
Ниже — практический разбор: как происходит выбор сертификата (tls certificate selection), чем SNI отличается от HTTP-заголовка Host, как проверить поведение через openssl s_client, и что смотреть в конфигурациях nginx и Apache, чтобы убрать mismatch certificate.
Коротко: кто за что отвечает — SNI и ALPN
SNI (Server Name Indication) — расширение TLS, в котором клиент отправляет серверу имя хоста в момент TLS-рукопожатия. Сервер использует это имя, чтобы выбрать правильный сертификат и конфигурацию виртуального хоста на одном IP:порт.
ALPN (Application-Layer Protocol Negotiation) — расширение TLS, позволяющее клиенту предложить список протоколов (например, h2, http/1.1), а серверу — выбрать один. Так браузер понимает, будет ли соединение по HTTP/2 или останется на HTTP/1.1.
Запомните: SNI отвечает за «какой сайт/сертификат», ALPN — за «какой протокол поверх TLS».
На практике это обычно выглядит так:
- ошибки по сертификату (mismatch) чаще связаны с SNI/выбором vhost по умолчанию;
- отсутствие HTTP/2 чаще связано с ALPN или настройками/сборкой фронтенда.
Как реально выглядит выбор сертификата (tls certificate selection)
Ключевой момент: выбор сертификата происходит до того, как сервер увидит HTTP-запрос и заголовок Host. Сначала устанавливается TCP-соединение, затем клиент отправляет TLS ClientHello, в котором (если умеет) будут:
- SNI: имя домена, к которому клиент обращается;
- ALPN: список поддерживаемых прикладных протоколов.
Если SNI передано — сервер может выбрать сертификат для конкретного домена. Если SNI не передано — сервер обязан выбрать «сертификат по умолчанию» для этого IP:порт (default vhost), и именно его увидит клиент. Отсюда типовая картина: «у домена A сертификат домена B».
Почему SNI критично для мультисайтов на одном IP
На одном IP:443 можно обслуживать множество доменов. Но без SNI сервер не знает, какой именно домен запрашивают, и выдаёт дефолтный сертификат. В браузерах это почти не встречается (современные клиенты SNI поддерживают давно), но в интеграциях всё ещё всплывает: старые Java-рантаймы, устаревшие агенты мониторинга, некоторые встроенные устройства.
Почему ALPN влияет на HTTP/2 и gRPC
Браузер предпочитает h2, но если ALPN не согласован (сервер не объявляет h2 или не поддерживает), соединение останется на http/1.1. Для gRPC ALPN практически обязателен, потому что базовый транспорт — HTTP/2.

Диагностика через openssl s_client: проверяем SNI и ALPN
Самый надёжный способ поймать mismatch certificate — посмотреть, какой сертификат сервер отдаёт с SNI и без SNI, и сравнить. На разных версиях OpenSSL поведение по умолчанию отличается, поэтому SNI лучше задавать явно через -servername.
Проверка сертификата с явным SNI
openssl s_client -connect example.com:443 -servername example.com -showcerts
Что смотреть в выводе:
subject=иissuer=у leaf-сертификата (первый сертификат в цепочке);Verify return code(0 — ок; другое — проблемы с цепочкой/доверенным корнем);- SAN: ищите в тексте сертификата
X509v3 Subject Alternative Nameи убедитесь, что домен там есть.
Проверка «как будто клиент без SNI»
openssl s_client -connect example.com:443 -showcerts
Если сертификат здесь отличается от проверки с -servername, значит на IP:443 есть дефолтный виртуальный хост, и клиенты без SNI будут получать именно его сертификат. Это не всегда ошибка, но часто неожиданность для мониторинга и интеграций.
Проверка ALPN (договаривались ли о h2)
openssl s_client -connect example.com:443 -servername example.com -alpn h2 -brief
Если сервер поддерживает HTTP/2 и разрешает его на этом vhost, в кратком выводе появится согласованный протокол. Если нет — вы увидите выбор http/1.1 или отсутствие ALPN-согласования (зависит от OpenSSL и сервера).
Полезная привычка: фиксируйте, куда вы реально подключились
Иногда вы тестируете домен, который резолвится «не туда»: CDN, старый IP, другой пул балансировщика, разные ответы по AAAA/A. Чтобы исключить это, подключайтесь к конкретному IP, но сохраняйте нужное SNI:
openssl s_client -connect 203.0.113.10:443 -servername example.com -showcerts
Если вы работаете с фронтовыми прокси и сложной терминацией, полезно держать под рукой отдельный разбор по теме: как терминация TLS/HTTP на прокси влияет на SNI и ALPN.
Типовые причины mismatch certificate (и как их быстро отличить)
Под mismatch certificate обычно скрывается один из трёх сценариев:
- сервер отдал сертификат «чужого» домена;
- сертификат «почти тот», но домен отсутствует в SAN (например, есть
www, но нет apex, или наоборот); - сертификат правильный, но клиент попал на другой виртуальный хост из-за особенностей SNI/прокси/балансировщика.
1) Неправильный default server / default vhost
Если клиент не отправил SNI, сервер выбирает дефолтный хост. В nginx это часто связано с директивой default_server, в Apache — с порядком vhost’ов или отдельным default-конфигом. Симптом: без -servername OpenSSL показывает «левый» сертификат.
2) SNI есть, но конфиг совпал не с тем блоком
Такое бывает, когда:
- в
server_name(nginx) опечатка или неверная маска; - используются пересекающиеся шаблоны имён, и матчится «первый подходящий» серверный блок;
- сертификат указан не в том месте, а соединение пришло в другой листенер (другой IP/порт/443 vs 8443 и т. п.).
3) На уровне балансировщика или прокси SNI/ALPN «не там»
Если TLS терминируется не на веб-сервере, а на прокси/балансировщике, то именно он должен разрулить SNI и объявить ALPN. А если TLS «пробрасывается» дальше (passthrough), прокси должен уметь маршрутизировать по SNI на TCP-уровне. Симптом: на бэкенде «всё правильно», но клиент видит чужой сертификат или не договаривается на h2.
4) Ошибка SAN: сертификат не покрывает нужное имя
Классика: сертификат только на www.example.com, а пользователи ходят на example.com (или наоборот). С точки зрения браузера это тоже mismatch. Верифицируйте SAN и при необходимости перевыпустите сертификат. Если нужен коммерческий вариант с понятной поддержкой и прогнозируемыми сроками выпуска, смотрите SSL-сертификаты.
Nginx: выбор сертификата по SNI и частые ошибки
В nginx логика простая: один IP:порт слушается директивой listen 443 ssl, а дальше выбор идёт по server_name. Сертификат задаётся директивами ssl_certificate и ssl_certificate_key внутри конкретного server-блока.
Минимальный пример двух сайтов на одном IP
server {
listen 443 ssl default_server;
server_name _;
ssl_certificate /etc/ssl/default/fullchain.pem;
ssl_certificate_key /etc/ssl/default/privkey.pem;
return 444;
}
server {
listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/ssl/example/fullchain.pem;
ssl_certificate_key /etc/ssl/example/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
}
}
Почему полезен «явный» default: если какой-то клиент пришёл без SNI или с мусорным именем, вы не отдаёте случайно сертификат живого сайта и не создаёте путаницу в логах и алертах.
Как быстро понять, какой серверный блок обслужил TLS-запрос
Для HTTP-запросов помогают $host и $server_name, но для TLS-рукопожатия важнее имя из SNI — переменная $ssl_server_name. Её удобно писать в access_log (если переменная доступна в вашей версии).
Если вы одновременно приводите в порядок TLS и «обвязку» сайта, не забудьте про базовую гигиену ответов: настройка HTTP security headers в nginx и Apache.

Apache: что важно понимать про SSL vhost на 443
В Apache SNI работает через name-based virtual hosts на *:443 (или на конкретном IP:443). В современных версиях достаточно описать <VirtualHost *:443> блоки и указать сертификаты внутри каждого, но порядок и «дефолтность» всё ещё имеют значение.
Пример: два SSL vhost
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
SSLEngine on
SSLCertificateFile /etc/ssl/example/cert.pem
SSLCertificateKeyFile /etc/ssl/example/key.pem
SSLCertificateChainFile /etc/ssl/example/chain.pem
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>
<VirtualHost *:443>
ServerName other.example
SSLEngine on
SSLCertificateFile /etc/ssl/other/cert.pem
SSLCertificateKeyFile /etc/ssl/other/key.pem
SSLCertificateChainFile /etc/ssl/other/chain.pem
</VirtualHost>
Если клиент без SNI, Apache отдаст сертификат первого объявленного vhost для этого адреса:порта. Поэтому порядок конфигов и наличие «осознанного дефолта» важны так же, как и в nginx.
ALPN на практике: почему HTTP/2 «не включился», хотя TLS работает
Ситуация: сертификат правильный, HTTPS работает, но в DevTools вы видите HTTP/1.1. Это не всегда проблема, но если вы ожидаете HTTP/2 (или он нужен для gRPC), проверяйте ALPN.
Частые причины отсутствия h2
- веб-сервер/сборка не поддерживает HTTP/2 (в nginx — отсутствие модуля, в Apache — не включён mod_http2);
- HTTP/2 включён только для части vhost’ов или только на одном из листенеров;
- TLS терминируется на прокси, который не объявляет
h2в ALPN или форсируетhttp/1.1к клиенту; - редкий случай: слишком жёсткие ограничения по TLS-версиям/параметрам приводят к несовместимости с конкретным клиентом.
Как отличить проблему ALPN от проблемы приложения
ALPN определяется в момент TLS. Если через openssl s_client вы не можете согласовать h2, никакая настройка приложения «выше» (заголовки, редиректы, роутинг) это не исправит: сначала чинится TLS-конфигурация и поддержка протокола на фронте.
Чеклист: что проверить, если SNI/ALPN ведут себя странно
- DNS и IP: вы тестируете тот же адрес, куда ходят пользователи? Сверьте A/AAAA и фактический IP на балансировщике/сервере.
- Сертификат с SNI:
openssl s_client -servernameдолжен отдавать правильный leaf и цепочку. - Сертификат без SNI: убедитесь, что дефолтный vhost не вводит в заблуждение мониторинг и интеграции.
- SAN: покрывает ли сертификат apex,
wwwи нужные поддомены. - Матчинг vhost: в nginx проверьте
server_nameи наличиеdefault_server; в Apache — порядок SSL vhost на 443. - ALPN: тестируйте
-alpn h2и фиксируйте, что выбрано. - Точка терминации: где именно заканчивается TLS — на веб-сервере или на прокси/балансировщике?
Практический сценарий: «чужой сертификат» ругается только мониторинг
Частый кейс: браузеры всё показывают правильно, а мониторинг (или старый клиент) ругается на «чужой» сертификат. Почти всегда это проверка, которая подключается к IP:443 без SNI (например, мониторинг ходит по IP, а не по доменному имени).
Что делать:
- настроить мониторинг так, чтобы он подключался по доменному имени и отправлял SNI;
- иметь осознанный default vhost на 443 (с отдельным сертификатом-заглушкой или политикой «не обслуживаем»), чтобы проверки «без имени» не путали картину;
- если обязаны поддерживать клиентов без SNI — выделить отдельный IP для критичного домена или объединить имена под одним сертификатом (если это допустимо по архитектуре).
Вывод
SNI и ALPN — две вещи, которые незаметны, пока всё работает, и крайне заметны, когда что-то пошло не так. SNI определяет, какой сертификат и конфигурацию выберет сервер, а ALPN — согласует протокол (например, HTTP/2). Если вы видите mismatch certificate, начинайте со сравнения выдачи сертификата в openssl s_client с параметром -servername и без него, затем проверяйте дефолтный vhost и шаблоны имён в nginx/Apache.
Если вам нужно разнести много проектов по изоляции (разные версии ПО, разные TLS-политики, отдельные IP, собственные прокси) — иногда проще вынести сайты на отдельные инстансы. В таком случае пригодится VDS, чтобы жёстко разделить окружения и исключить сюрпризы с default vhost на общем фронтенде.


