Истёк SSL-сертификат: что видят посетители, что ломается и что делать по шагам
Истёк SSL-сертификат — что делать: найти, что именно истекло (домен, цепочка, порт 465/993), продлить, перезагрузить nginx и почту, проверить снаружи.
Если истёк SSL-сертификат, порядок действий такой: за минуту выяснить, что именно истекло и на каком хосте, выпустить новый сертификат (для Let's Encrypt — certbot renew, для платного — перевыпуск в панели продавца), положить его на все серверы, которые терминируют TLS, перезагрузить nginx, Apache, Postfix и Dovecot, и проверить результат с внешней точки, а не с самого сервера. Чистить HSTS-кеш у посетителей не нужно: как только сервер отдаёт действующий сертификат, браузеры открывают сайт без вопросов.
Пока вы это делаете, сайт для посетителей не «тормозит» и не «частично работает» — он закрыт красным экраном, а всё, что ходит к нему без браузера (мобильное приложение, платёжные колбэки, обмен с 1С, интеграции маркетплейсов), молча падает. Ниже — что именно видят разные клиенты, как диагностировать проблему тремя командами, пошаговое восстановление и четыре ловушки, из-за которых «сертификат заменили, а браузер всё равно ругается».
Что видят посетители и клиенты в первые часы
Браузер сверяет поле Not After сертификата с часами устройства. Как только время вышло, каждая загрузка страницы упирается в межстраничное предупреждение:
| Клиент | Что показывает | Можно ли «всё равно перейти» |
|---|---|---|
| Chrome, Яндекс.Браузер, Edge, Opera | «Ваше подключение не защищено», код NET::ERR_CERT_DATE_INVALID | Да, через «Дополнительно», кроме сайтов с HSTS |
| Safari (macOS, iOS) | «Это соединение не является частным», ниже — «срок действия сертификата истёк» | Да, через «Подробнее → посетить сайт» |
| Firefox | SEC_ERROR_EXPIRED_CERTIFICATE | Да, «Принять риск», кроме сайтов с HSTS |
curl, wget, Python requests, Node.js, Java | certificate has expired (curl: код 60; Node: CERT_HAS_EXPIRED; Java: PKIX path validation failed) | Нет, только флагом «не проверять», которого в проде не бывает |
| Мобильные приложения (iOS ATS, Android) | Общая ошибка сети: «Не удалось подключиться», «Проверьте соединение» | Нет |
| Почтовые клиенты (Outlook, Thunderbird, Почта iOS) | «Не удаётся проверить идентичность сервера», синхронизация останавливается до ручного согласия | Да, но каждый пользователь делает это сам |
Что при этом обычно недооценивают.
Если сайт отдаёт заголовок Strict-Transport-Security, Chrome, Яндекс.Браузер и Firefox убирают кнопку обхода совсем: в Chrome посетитель видит текст «вы не можете посетить сайт, потому что он использует HSTS» и уходит. Это не баг, а именно то, что HSTS обещает.
Автоматика не показывает предупреждений — она возвращает ошибку. Колбэк платёжной системы о том, что заказ оплачен, не дойдёт до вашего HTTPS-эндпоинта: шлюз проверяет сертификат так же строго, как curl. Деньги списаны, заказ в базе «не оплачен», поддержка узнаёт от клиентов. То же с вебхуками Telegram-ботов, обменом с 1С, фидами маркетплейсов и cron-скриптами на других серверах.
Поисковые роботы терпеливее браузеров. Googlebot сертификат не проверяет и продолжает обходить сайт — прямого удара по индексу в Google не будет, ущерб придёт через поведенческие сигналы: посетители из выдачи упираются в красный экран и уходят. Яндекс строже: Вебмастер помечает в «Диагностике» фатальную ошибку «Некорректная настройка SSL-сертификата», и пока она не устранена, робот считает страницы недоступными. Реклама при этом может продолжать крутиться (если в Директе не включён мониторинг сайта): клики оплачены, посадочная закрыта предупреждением.
С почтой отдельная история. Пользователи с IMAP/SMTP на mail.example.ru перестают получать письма до тех пор, пока не нажмут «доверять» в клиенте. Приём почты от других серверов чаще всего продолжается: большинство MTA используют оппортунистический TLS и не проверяют сертификат получателя, — но если у вас настроен MTA-STS или DANE, отправители, соблюдающие политику, отложат доставку.
Что именно истекло: три команды
Сначала — что реально отдаёт сервер, а не что лежит на диске. Проверяйте с внешней машины: с самого сервера легко получить ответ через localhost, минуя балансировщик и CDN.
openssl s_client -connect example.ru:443 -servername example.ru </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates -fingerprint -sha256
Флаг -servername оставляйте всегда: при подключении по IP (как в примере с бэкендами ниже) и на старых OpenSSL до 1.1.1 без него SNI не отправляется, сервер с несколькими сайтами отдаст сертификат по умолчанию, и вы будете чинить не тот. В выводе смотрите notAfter и, в полной версии s_client без пайпа в x509, строку Verify return code:
Verify return code: 10 (certificate has expired)
Если код 10, а notAfter у сертификата домена в будущем, — истёк промежуточный сертификат в цепочке. Массово такое было 30 сентября 2021 года с корнем DST Root CA X3, а точечно повторяется на серверах, куда цепочку положили руками из старого архива. Проверить каждый сертификат цепочки по отдельности:
openssl s_client -connect example.ru:443 -servername example.ru -showcerts </dev/null 2>/dev/null \
| awk '/BEGIN CERT/{i++} i{print > "chain" i ".pem"}'
for f in chain*.pem; do openssl x509 -in "$f" -noout -subject -enddate; done
Условие i перед print нужно, чтобы служебные строки s_client до первого сертификата не попали в файл без номера. Первый файл — сертификат домена, остальные — промежуточные. У Let's Encrypt промежуточные сейчас называются R10–R14 и E5–E9; если в цепочке всплыл R3, это старый файл chain.pem, а не свежий выпуск.
Третья команда — нестандартные порты. Почтовый сервер обычно живёт на отдельном сертификате или на том же, но с отдельной копией, которую забыли обновить:
openssl s_client -connect mail.example.ru:465 -servername mail.example.ru </dev/null 2>/dev/null | openssl x509 -noout -enddate
openssl s_client -connect mail.example.ru:993 -servername mail.example.ru </dev/null 2>/dev/null | openssl x509 -noout -enddate
openssl s_client -connect mail.example.ru:587 -starttls smtp </dev/null 2>/dev/null | openssl x509 -noout -enddate
Порты 465 и 993 — TLS сразу, 587 и 143 — STARTTLS, поэтому для них нужен флаг -starttls smtp или -starttls imap. Если под рукой нет терминала, тот же ответ (срок, издатель, SAN, цепочка, порт через host:port) даёт онлайн-проверка SSL-сертификата с трёх точек — полезно, когда сайт за CDN и сертификат для Москвы и Хельсинки может отличаться.
Одновременно сравните с тем, что на диске:
openssl x509 -in /etc/letsencrypt/live/example.ru/fullchain.pem -noout -enddate -fingerprint -sha256
Если на диске сертификат свежий, а отпечаток отличается от того, что отдаёт сервер, — продление сработало, не сработала установка. Переходите сразу к перезагрузке служб.
Продлить и установить
Let's Encrypt
Истёкший сертификат продлению не мешает. Валидация HTTP-01 идёт на порт 80, а если у вас редирект на HTTPS, сервер Let's Encrypt следует по нему и не проверяет сертификат при этом. Запускайте:
certbot renew --deploy-hook "systemctl reload nginx"
certbot renew сам увидит, что срок вышел, и перевыпустит всё, что истекло или истекает в ближайшие 30 дней. Если команда закончилась ошибкой — валидация, права, токен DNS-провайдера, — причины и текст ошибок разобраны в статье «Certbot не обновил сертификат»; здесь только напомню, что после исправления причины нужно запустить certbot renew ещё раз, а не ждать таймера.
Если certbot на этом сервере вообще не помнит домен (переезд, восстановление из бэкапа), выпускайте заново:
certbot certonly --webroot -w /var/www/example.ru -d example.ru -d www.example.ru
Платный сертификат
У коммерческих сертификатов «продление» — новый заказ и перевыпуск. В панели продавца: заказать продление, создать CSR (или использовать старый ключ), пройти проверку домена (письмо на admin@, DNS-запись или файл в корне сайта), скачать архив. В нём обычно сертификат домена, промежуточный и корневой. Веб-серверу нужны первые два, склеенные в порядке «домен, потом промежуточные»:
cat example.ru.crt intermediate.crt > /etc/ssl/example.ru/fullchain.pem
Корневой в цепочку класть не надо: он уже есть у клиентов. Проверка, что ключ и сертификат от одной пары:
openssl x509 -noout -pubkey -in fullchain.pem | sha256sum
openssl pkey -pubout -in privkey.pem | sha256sum
Хеши должны совпасть — команда одинаково работает для RSA и ECDSA-ключей.
Перезагрузить всё, что терминирует TLS
Файл на диске службы не перечитывают. Каждая держит сертификат в памяти с момента старта:
nginx -t && systemctl reload nginx
apachectl configtest && systemctl reload apache2
systemctl reload postfix
doveadm reload
systemctl reload haproxy
Для HAProxy сначала пересоберите его PEM — он ждёт сертификат, цепочку и ключ в одном файле: cat fullchain.pem privkey.pem > /etc/haproxy/certs/example.ru.pem. Для Postfix убедитесь, что smtpd_tls_cert_file и smtpd_tls_key_file в main.cf указывают на обновлённые файлы, для Dovecot — ssl_cert = </etc/letsencrypt/live/example.ru/fullchain.pem в conf.d/10-ssl.conf (в Dovecot 2.4 параметр называется ssl_server_cert_file = /etc/letsencrypt/live/example.ru/fullchain.pem).
Проверить снаружи
Повторите первую команду из раздела про диагностику: notAfter сдвинулся, отпечаток совпал с файлом на диске — для каждого хоста и порта из списка: 443, 465, 993, 8443 у панели. Затем обновите страницу; если браузер показывает старую ошибку из кеша, откройте сайт в новой вкладке или приватном окне — этого достаточно.
Ничего чистить не надо. HSTS-запись у посетителя означает только «ходи на этот домен по HTTPS и не давай обходить ошибки сертификата». Ошибка исчезла — запись бездействует; chrome://net-internals/#hsts и «состояние SSL» в Windows на проверку действующего сертификата не влияют.
Сертификат заменили, а браузер всё равно ругается
nginx держит старый сертификат
Самая частая причина. ls -l /etc/letsencrypt/live/ показывает свежую дату, openssl x509 на файле — свежий срок, а с внешней точки прилетает истёкший. Значит reload не выполнялся, выполнился с ошибкой в nginx -t, или воркеры не перезапустились. Подробно этот случай — включая ситуацию, когда certbot renew отработал, а хук не вызвался, — разобран в статье про certbot. Быстрая проверка: systemctl status nginx и journalctl -u nginx -n 20 покажут, был ли reload и чем он закончился.
CDN или Cloudflare отдают свой сертификат
Если домен проксируется через Cloudflare или другой CDN, посетитель видит сертификат CDN, а не ваш. Тогда истечение возможно на двух уровнях. Истёк сертификат на границе CDN — посетители видят красный экран, и чинить это нужно в панели CDN (у Cloudflare Universal SSL продлевается сам; истёкший на границе сертификат почти всегда означает, что вы загрузили свой и забыли). Истёк сертификат на вашем сервере (origin) — посетители видят не браузерную ошибку, а страницу Cloudflare с кодом 526 Invalid SSL certificate в режиме Full (strict). В режиме Full без strict срок origin-сертификата не проверяется вовсе, и сайт продолжит работать с истёкшим сертификатом, пока кто-нибудь не зайдёт на сервер напрямую по IP.
Wildcard и поддомены
*.example.ru покрывает shop.example.ru и api.example.ru, но не покрывает сам example.ru (он должен быть отдельным именем в SAN) и не покрывает test.shop.example.ru — подстановочный знак действует ровно на один уровень. Если после продления перестал работать один поддомен, а остальные живы, проверьте список SAN:
openssl s_client -connect api.example.ru:443 -servername api.example.ru </dev/null 2>/dev/null \
| openssl x509 -noout -ext subjectAltName
Часто оказывается, что у поддомена был отдельный сертификат со своим конфигом в /etc/letsencrypt/renewal/, и продлился не он.
Несколько серверов за балансировщиком
Если TLS терминируется на балансировщике — обновлять нужно его, а не бэкенды. Если TLS проходит до бэкендов (TCP-балансировка, proxy_pass на HTTPS), сертификат нужен на каждом: обновили один из трёх — ошибка у трети посетителей, внешняя проверка ловит её через раз. Диагностика — по IP каждого бэкенда:
for ip in 10.0.0.11 10.0.0.12 10.0.0.13; do
echo -n "$ip: "; openssl s_client -connect $ip:443 -servername example.ru </dev/null 2>/dev/null | openssl x509 -noout -enddate
done
Как не попасть снова
Истёкший сертификат — всегда два отказа: сломалась автоматика продления, и никто не узнал об этом за 30 дней, пока сертификат доживал срок. Первый лечится хуком и проверкой:
certbot renew --dry-run
systemctl list-timers | grep certbot
--dry-run прогоняет продление на тестовом сервере Let's Encrypt: выпущенный там сертификат не сохраняется, а лимиты тестовой среды в разы выше боевых, так что на реальные квоты это не влияет; таймер должен быть в списке с датой следующего запуска. Для платных сертификатов — запись в календаре за 30 дней до срока: перевыпуск с проверкой домена занимает от часа до дня.
Второй отказ лечится контролем снаружи. Заведите список всех хостов и портов, где есть TLS, — сайт, www, API, панель на 8443, почта на 465/993, — и проверяйте срок каждого с внешней точки, а не с сервера, где лежат «свежие» файлы. Порог — 14 дней для Let's Encrypt (если за 14 дней до конца продление не прошло, автоматика уже сломана, и у вас две недели на ремонт) и 30 дней для платных. Письма-напоминания от Let's Encrypt с середины 2025 года не рассылаются, рассчитывать на них нельзя. Эту часть можно отдать мониторингу SSL-сертификата: он проверяет каждый хост и порт раз в сутки за 0,002 ₽, предупреждает за 14 дней (срок настраивается) и сообщает о смене сертификата — уведомления приходят в Telegram в тот же чат, что и сообщения о сбоях.
Коротко
- Истёкший сертификат для браузера — красный экран
NET::ERR_CERT_DATE_INVALID; при HSTS обойти его нельзя. Приложения, API-клиенты и платёжные колбэки падают без предупреждения. - Диагностика с внешней машины:
openssl s_client -servernameдля 443, 465, 993;-showcerts— чтобы найти истёкший промежуточный; сравнить отпечаток с файлом на диске. - Let's Encrypt:
certbot renew --deploy-hook "systemctl reload nginx"— истёкший сертификат продлению не мешает. Платный: перевыпуск, склейка домен + промежуточный, проверка пары ключ/сертификат. - Перезагрузить всё, что терминирует TLS: nginx, Apache, Postfix, Dovecot, HAProxy, балансировщик. HSTS и кеши у посетителей чистить не нужно.
- Если не помогло: nginx держит старый, CDN отдаёт свой, wildcard не покрывает уровень, обновлён не каждый бэкенд.
- Чтобы не повторилось:
certbot renew --dry-run, таймер вlist-timers, список всех хостов и портов и проверка сроков снаружи за 14–30 дней.
Вопросы и ответы
Можно ли открыть сайт с истёкшим сертификатом?
Сколько времени занимает продление истёкшего сертификата?
Почему после продления сайт всё ещё показывает истёкший сертификат?
Нужно ли очищать HSTS или кеш браузера после замены сертификата?
Влияет ли истёкший сертификат на позиции в Яндексе и Google?
Продолжит ли приходить почта, если истёк сертификат на mail-сервере?
Не проверяйте вручную — следите автоматически
Нотифарио проверит сайт с нескольких точек и пришлёт уведомление в Telegram, MAX, по SMS или звонком, когда что-то сломается. Оплата только за проверки, 50 ₽ на старт.
Подключить бесплатно Проверить сайт сейчас