openssl s_client: проверяем TLS-соединение и сертификаты из терминала

10 минут чтения
Средний рейтинг статьи — 4.8

openssl s_client — одна из самых полезных команд для диагностики HTTPS и любых TLS-соединений вообще. Она показывает то, что браузер обычно скрывает: какой сертификат отдал сервер, совпадает ли имя хоста, из каких сертификатов состоит цепочка, какая версия TLS и шифр согласованы, работает ли SNI, есть ли OCSP stapling.

Утилита особенно удобна, когда сайт «вроде открывается не у всех», после деплоя поменяли сертификат, балансировщик отдаёт не тот virtual host или браузер показывает ERR_SSL_PROTOCOL_ERROR. В отличие от веб-проверялок, openssl s_client запускается прямо с сервера, CI-раннера или рабочей машины, поэтому помогает проверить соединение именно из нужной точки сети.

Ниже — практический разбор: базовые команды, чтение вывода, проверка сертификатов, SNI, ALPN, версий TLS и типичных ошибок. Если нужна теория про сам протокол, полезно сначала освежить, как работает HTTPS, SSL, TLS и сертификаты.

Что делает openssl s_client

openssl s_client — это TLS-клиент в составе OpenSSL. Он устанавливает TCP-соединение с сервером, выполняет TLS handshake и печатает подробности соединения. По сути, это низкоуровневый аналог браузера или curl, но с гораздо большим фокусом на криптографическую часть.

Команда полезна в нескольких сценариях.

1) Проверить, что TLS вообще поднимается — сервер принимает соединение на 443, handshake проходит, клиент и сервер договорились о версии TLS и наборе шифров.

2) Посмотреть сертификат — subject, issuer, срок действия, SAN-имена, алгоритм подписи, публичный ключ.

3) Проверить цепочку доверия — сервер должен отдать не только leaf-сертификат сайта, но и промежуточные сертификаты. Если цепочка неполная, часть клиентов может получить ошибку доверия.

4) Проверить SNI — на одном IP часто живут десятки сайтов. Без SNI сервер может отдать сертификат «по умолчанию», который не подходит к вашему домену.

5) Отладить ALPN и HTTP/2 — браузер выбирает протокол поверх TLS через ALPN: например, h2 или http/1.1. Это можно проверить из терминала.

6) Диагностировать не только HTTPS — s_client умеет работать с STARTTLS для SMTP, IMAP, POP3 и других протоколов.

openssl s_client не заменяет полноценный HTTP-клиент. Он может отправить HTTP-запрос вручную, но не будет автоматически следовать редиректам, хранить cookies или красиво показывать заголовки. Для HTTP-уровня удобнее curl; у нас есть отдельный разбор, что такое curl и как им пользоваться. Но когда вопрос именно в TLS, s_client обычно быстрее и информативнее.

Быстрая проверка HTTPS-соединения

Базовая команда для сайта выглядит так:

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

Ключ -connect задаёт адрес и порт. Ключ -servername включает SNI и передаёт имя хоста серверу во время TLS handshake.

Для большинства публичных сайтов -servername обязателен. Без него вы можете получить чужой сертификат или сертификат default-виртуалхоста:

openssl s_client -connect example.com:443

Если сервер обслуживает несколько доменов на одном IP, результат без SNI часто будет отличаться от браузера. Поэтому правило простое: проверяете домен — почти всегда добавляйте -servername.

Чтобы сразу отправить HTTP-запрос после установки TLS-соединения, можно использовать printf:

printf 'GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n' \
  | openssl s_client -connect example.com:443 -servername example.com -quiet

Ключ -quiet уменьшает служебный вывод и оставляет больше места для HTTP-ответа. Но для диагностики сертификатов его лучше не использовать — часть полезных деталей пропадёт.

Если нужно сохранить вывод в файл:

openssl s_client -connect example.com:443 -servername example.com < /dev/null > tls.txt 2>&1

< /dev/null нужен, чтобы команда не ждала ввода с клавиатуры. Перенаправление 2>&1 полезно потому, что OpenSSL часть диагностического вывода пишет в stderr.

Типичная успешная проверка заканчивается строками вроде:

New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Verify return code: 0 (ok)

Это значит, что handshake прошёл, сертификат проверен локальным хранилищем доверенных CA, а клиент и сервер договорились о TLS 1.3 и конкретном cipher suite.

Если вы проверяете продакшен-сайт после обновления сертификата, одной ручной проверки мало. Сроки сертификатов и доступность лучше контролировать регулярно: например, сервис мониторинга доступности может проверять сайт с заданным интервалом и присылать уведомление о сбое. Про причины отдельного контроля сертификатов хорошо написано в статье почему важен мониторинг SSL-сертификатов.

Как читать вывод openssl s_client

Вывод openssl s_client может выглядеть шумно. Его проще читать блоками.

1) Подключение и цепочка сертификатов — в начале часто выводится Certificate chain:

Certificate chain
 0 s:CN = example.com
   i:C = US, O = Let's Encrypt, CN = R3
 1 s:C = US, O = Let's Encrypt, CN = R3
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1

Сертификат 0 — leaf-сертификат сайта. Сертификат 1 — промежуточный. Поле s означает subject, поле i — issuer.

Если сервер отдаёт только leaf-сертификат и не отдаёт intermediate, некоторые клиенты могут не построить цепочку. Браузеры иногда «достраивают» цепочку сами, поэтому проблема проявляется выборочно: у одного клиента всё работает, у другого — ошибка.

2) Server certificate — ниже OpenSSL показывает сам сертификат сервера:

Server certificate
subject=CN = example.com
issuer=C = US, O = Let's Encrypt, CN = R3

Не ориентируйтесь только на CN. Для современных сертификатов основная проверка имени идёт по расширению subjectAltName. Его удобнее смотреть через openssl x509, об этом ниже.

3) Параметры TLS-сессии — блок SSL-Session показывает версию протокола, cipher, session ticket и другие детали:

SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384

Если вы видите TLSv1 или TLSv1.1, это сигнал проверить настройки сервера. Старые версии TLS давно считаются устаревшими и часто отключены в современных клиентах.

4) Результат проверки сертификата — самая важная строка в конце:

Verify return code: 0 (ok)

Код 0 означает, что цепочка проверена успешно. Другие варианты указывают на проблему:

Verify return code: 10 (certificate has expired)
Verify return code: 20 (unable to get local issuer certificate)
Verify return code: 62 (hostname mismatch)

Сама по себе строка CONNECTED(00000003) ещё не значит, что всё хорошо. Она говорит только о TCP-соединении. TLS handshake и проверка сертификата могут завершиться ошибкой ниже.

5) Ошибки handshake — сообщения вроде handshake failure, wrong version number, no peer certificate available требуют смотреть контекст. Иногда сервер вообще не говорит TLS на этом порту, иногда TLS прерывает балансировщик, иногда клиент и сервер не имеют общей версии протокола или шифров.

Если хочется глубже понять, что происходит на этапе обмена сообщениями, см. разбор TLS handshake под микроскопом.

SNI, ALPN и версии TLS

Многие проблемы с TLS появляются не из-за самого сертификата, а из-за неверно выбранного виртуального хоста или протокола.

1) SNI — Server Name Indication передаёт имя домена до выбора сертификата:

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

Это удобно, когда DNS ещё не переключён, но нужно проверить будущий балансировщик или новый IP: вы подключаетесь к IP, а SNI передаёте как домен.

Можно сравнить два варианта:

openssl s_client -connect example.com:443 < /dev/null
openssl s_client -connect example.com:443 -servername example.com < /dev/null

Если сертификаты разные, значит сервер зависит от SNI. Это нормально для shared hosting, reverse proxy, Kubernetes ingress, CDN и балансировщиков.

2) ALPN — Application-Layer Protocol Negotiation позволяет договориться, какой протокол будет использоваться поверх TLS:

openssl s_client -connect example.com:443 -servername example.com -alpn h2,http/1.1

В выводе ищите строку:

ALPN protocol: h2

Если сервер должен поддерживать HTTP/2, но возвращает http/1.1 или вообще не показывает ALPN, проверьте настройки веб-сервера, TLS-библиотеку и прокси перед приложением. Подробнее про связь SNI и ALPN есть отдельная статья: SNI и ALPN: как браузер выбирает сертификат и протокол.

3) Принудительная версия TLS — можно проверить, поддерживает ли сервер конкретную версию:

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

Для TLS 1.2:

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

Если команда с -tls1_3 падает, сервер может не поддерживать TLS 1.3. Это не всегда критично, но для новых конфигураций обычно ожидается поддержка TLS 1.2 и TLS 1.3.

Проверка старых версий:

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

Если они успешно проходят, это повод пересмотреть конфигурацию. Для публичного сайта обычно не нужно держать TLS 1.0 и TLS 1.1 ради совместимости с древними клиентами.

4) Cipher suites — для TLS 1.2 и ниже используется -cipher:

openssl s_client -connect example.com:443 -servername example.com -tls1_2 -cipher 'ECDHE+AESGCM'

Для TLS 1.3 — -ciphersuites:

openssl s_client -connect example.com:443 -servername example.com -tls1_3 \
  -ciphersuites TLS_AES_128_GCM_SHA256

На практике это нужно, когда вы проверяете жёсткую TLS-политику, совместимость старых клиентов или расследуете handshake failure.

Проверка сертификата и цепочки

Чтобы вывести все сертификаты, которые сервер отдаёт в цепочке, используйте -showcerts:

openssl s_client -connect example.com:443 -servername example.com -showcerts < /dev/null

В выводе будут блоки:

-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

Их можно сохранить в файл и разобрать:

openssl s_client -connect example.com:443 -servername example.com -showcerts < /dev/null \
  2>/dev/null | sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' > chain.pem

Посмотреть первый сертификат из цепочки:

openssl x509 -in chain.pem -noout -subject -issuer -dates

Если в chain.pem несколько сертификатов, openssl x509 прочитает первый. Для быстрой проверки leaf-сертификата обычно хватает такой команды:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

Она выведет примерно:

subject=CN = example.com
issuer=C = US, O = Let's Encrypt, CN = R3
notBefore=Jan 10 12:00:00 2026 GMT
notAfter=Apr 10 12:00:00 2026 GMT

Чтобы проверить SAN-имена:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -ext subjectAltName

Пример вывода:

X509v3 Subject Alternative Name:
    DNS:example.com, DNS:www.example.com

Если домена нет в subjectAltName, браузер не должен считать сертификат подходящим, даже если CN выглядит правильно.

Для строгой проверки имени хоста добавьте -verify_hostname:

openssl s_client -connect example.com:443 \
  -servername example.com \
  -verify_hostname example.com \
  -verify_return_error < /dev/null

Ключ -verify_return_error заставляет OpenSSL завершаться ошибкой при проблемах верификации, а не просто печатать предупреждение и продолжать.

Проверить OCSP stapling можно так:

openssl s_client -connect example.com:443 -servername example.com -status < /dev/null

Ищите блок OCSP response. Если видите:

OCSP response: no response sent

значит сервер не отдаёт stapled OCSP-ответ. Это не всегда авария, но для публичных HTTPS-сайтов OCSP stapling часто включают, чтобы клиентам не приходилось отдельно обращаться к CA за статусом сертификата.

Если сертификат самоподписанный или выпущен внутренним CA, системное хранилище доверия его не примет. Для таких случаев можно явно указать CA-файл:

openssl s_client -connect internal.example:443 \
  -servername internal.example \
  -CAfile ./company-root-ca.pem \
  -verify_return_error < /dev/null

Про создание тестовых сертификатов есть отдельный материал: создание самоподписанного SSL-сертификата с помощью OpenSSL. Для продакшена чаще используют публичный CA и автоматическое продление, например через certbot; см. инструкцию как настроить автоматическое обновление сертификатов Let’s Encrypt.

Диагностика типичных ошибок

openssl s_client особенно хорош тем, что показывает низкоуровневую причину, а не только браузерное сообщение.

1) Истёк сертификат — в конце вывода:

Verify return code: 10 (certificate has expired)

Проверьте notAfter:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -dates

Если используется Let’s Encrypt, проверьте таймеры certbot, права на директории, reload веб-сервера после обновления и то, какой именно сертификат подключён в конфигурации.

2) Неполная цепочка — частый симптом:

Verify return code: 20 (unable to get local issuer certificate)

Сервер может отдавать только leaf-сертификат без intermediate. В Nginx директива ssl_certificate обычно должна указывать на fullchain.pem, а не только на cert.pem.

3) Несовпадение имени хоста — при строгой проверке:

verify error:num=62:hostname mismatch

Проверьте subjectAltName. Частые причины: забыли добавить www, перепутали staging и production, балансировщик отдаёт default-сертификат, CDN ещё не применил изменения.

4) Нет SNI или неправильный SNI — без -servername сервер отдаёт не тот сертификат. Сравните вывод с SNI и без SNI, если проблема воспроизводится только у части клиентов.

5) Сервер не говорит TLS на порту — сообщение может выглядеть так:

error:...:wrong version number

Частая причина: вы подключились к HTTP-порту 80, к backend-порту без TLS или к proxy protocol listener. Проверьте порт, балансировщик и схему http/https.

6) Нет общего протокола или cipher suite — возможны ошибки handshake failure, protocol version, no shared cipher. Проверьте -tls1_2, -tls1_3, настройки ssl_protocols, ssl_ciphers, политику CDN или ingress.

7) Проблема не в TLS, а в HTTP — handshake успешен, Verify return code: 0 (ok), но сайт возвращает 502, 503 или редиректит по кругу. Тогда переходите на уровень HTTP и приложения: смотрите логи, прокси, upstream, health checks. Для общей диагностики пригодится материал почему не работает сайт.

Ручная диагностика помогает найти причину, но не заменяет наблюдение. Если сертификат истёк ночью или балансировщик начал отдавать неверную цепочку после деплоя, мониторинг доступности заметит сбой снаружи и отправит уведомление. Statuser, например, проверяет сайт с заданным интервалом и сообщает о проблеме, а openssl s_client уже помогает быстро подтвердить техническую причину.

Не только HTTPS: SMTP, IMAP и другие TLS-сервисы

TLS используется не только в вебе. openssl s_client умеет подключаться к сервисам с «чистым» TLS и к протоколам, где сначала открывается обычное соединение, а потом выполняется STARTTLS.

1) SMTP с STARTTLS — проверка почтового сервера:

openssl s_client -connect mail.example.com:587 -starttls smtp -servername mail.example.com

Порт 587 обычно использует STARTTLS: соединение начинается как SMTP, затем клиент отправляет команду на переход в TLS. Порт 465 чаще использует TLS сразу:

openssl s_client -connect mail.example.com:465 -servername mail.example.com

После подключения можно вручную отправить SMTP-команды, например EHLO example.com.

2) IMAP — порт 143 с STARTTLS:

openssl s_client -connect imap.example.com:143 -starttls imap -servername imap.example.com

IMAPS на 993:

openssl s_client -connect imap.example.com:993 -servername imap.example.com

3) POP3 — порт 110 с STARTTLS:

openssl s_client -connect pop.example.com:110 -starttls pop3 -servername pop.example.com

POP3S на 995:

openssl s_client -connect pop.example.com:995 -servername pop.example.com

4) PostgreSQL — у OpenSSL есть режим -starttls postgres:

openssl s_client -connect db.example.com:5432 -starttls postgres

Но для полноценной проверки доступа к базе всё равно лучше использовать нативный клиент psql, потому что TLS — только часть соединения.

5) LDAP, XMPP и другие протоколы — список поддерживаемых вариантов зависит от версии OpenSSL. Посмотреть подсказку можно так:

openssl s_client -help

Ищите опцию -starttls. В разных сборках могут отличаться доступные протоколы и названия.

Полезные команды для ежедневной работы

Короткий справочник команд, которые удобно держать под рукой — большинство из них уже разбирались выше, здесь они собраны в одном месте.

1) Сертификат и срок действия: echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -issuer -dates

2) SAN-имена: echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -ext subjectAltName

3) Версия TLS и cipher:

openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null \
  | grep -E 'Protocol|Cipher|Verify return code'

В разных версиях OpenSSL формат вывода может отличаться — если grep ничего не нашёл, запустите команду без фильтра.

4) HTTP/2 через ALPN: openssl s_client -connect example.com:443 -servername example.com -alpn h2,http/1.1 < /dev/null 2>/dev/null | grep -i 'ALPN'

5) OCSP stapling: openssl s_client -connect example.com:443 -servername example.com -status < /dev/null

6) Конкретный IP с нужным SNI: openssl s_client -connect 203.0.113.10:443 -servername example.com — полезно при миграции DNS или сравнении разных нод кластера.

7) Вся цепочка сертификатов: openssl s_client -connect example.com:443 -servername example.com -showcerts < /dev/null

8) Строгая проверка hostname и цепочки: openssl s_client -connect example.com:443 -servername example.com -verify_hostname example.com -verify_return_error < /dev/null

9) Дата истечения в машинно читаемом виде:

end_date=$(echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -enddate | cut -d= -f2)
 
date -d "$end_date" +%s

На macOS синтаксис date другой:

date -j -f "%b %d %T %Y %Z" "$end_date" +%s

Для серьёзного мониторинга лучше не писать хрупкие скрипты вокруг текстового вывода OpenSSL, а использовать готовые проверки или экспортеры. Но для разовой диагностики эти команды работают быстро и прозрачно.

Как встроить openssl s_client в рабочий процесс

openssl s_client можно использовать не только вручную «на случай пожара», но и в стандартных операционных процедурах.

1) После выпуска сертификата — проверьте, что сервер реально отдаёт новый сертификат, сравнив fingerprint с ожидаемым:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -serial -fingerprint -sha256 -dates

2) После изменения Nginx, ingress или балансировщика — проверяйте SNI, цепочку, ALPN и TLS-версии. Ошибка может быть не в приложении, а в том, что один backend отдаёт правильный сертификат, а второй — старый.

3) Перед переключением DNS — подключайтесь к новому IP напрямую, но передавайте production-домен в -servername. Так можно проверить TLS до того, как пользователи попадут на новый адрес.

4) В CI/CD для smoke-теста — после деплоя выполните короткую строгую проверку (-verify_hostname и -verify_return_error, см. выше). Если команда завершилась ошибкой, деплой или переключение трафика лучше остановить.

5) В runbook для инцидентов — добавьте готовые команды в инструкцию: «проверить SNI», «проверить цепочку», «проверить срок», «проверить ALPN». Во время инцидента не хочется вспоминать синтаксис.

6) Вместе с внешним мониторингом — мониторинг отвечает на вопрос «есть ли проблема у пользователей», а openssl s_client помогает ответить «почему именно TLS не проходит». Это разные уровни одной диагностики. Для общей стратегии снижения простоев полезен материал как избежать простоя сайта.

Не забывайте про версию OpenSSL. На старых серверах могут отличаться доступные ключи, формат вывода и поддержка TLS 1.3. Проверить версию можно так:

openssl version -a

Если результаты на локальной машине и на сервере различаются, сравните версии OpenSSL и системные хранилища CA. Иногда «сертификат невалиден» только потому, что на старой системе нет актуального корневого сертификата.

FAQ

Можно ли проверить сайт без SNI? Можно, но для современных сайтов это часто бессмысленно. Без -servername сервер может отдать сертификат default-виртуалхоста, а не нужного домена.

Почему браузер открывает сайт, а openssl s_client показывает ошибку цепочки? Браузер может достроить цепочку через кэш или AIA-загрузку intermediate-сертификата. Некоторые клиенты так не делают, поэтому сервер всё равно должен отдавать полную цепочку.

Чем openssl s_client отличается от curl -v? curl -v удобнее для HTTP: заголовки, редиректы, статус-коды. openssl s_client подробнее показывает TLS: сертификаты, handshake, ALPN, cipher suites, OCSP.

Можно ли использовать openssl s_client для мониторинга сертификатов? Для простого скрипта — да, но парсить вывод OpenSSL хрупко. Для продакшена лучше использовать специализированный мониторинг сертификатов и применять s_client как инструмент диагностики.

Опубликовано 22 октября 202610 минут чтенияАнастасия Левина
Средний рейтинг статьи — 4.8

Настроить мониторинг за 30 секунд

Надёжные уведомления о даунтаймах. Без ложных срабатываний