Короткий ответ

Просроченный сертификат на site.example (10.0.20.40) чинится выпуском нового (renew того же lineage или certonly), reload nginx/Apache и проверкой, что certbot.timer жив. Сначала openssl s_client / certbot certificates, чтобы понять: expired leaf, не тот vhost, или отдаётся snakeoil Ubuntu. Не мажьте дату на клиенте.

После выпуска проверьте fullchain: цепочка.

Не копируйте cert.pem в ssl_certificate после выпуска: нужен fullchain.pem. Reload nginx обязателен, иначе worker держит старый inode.

Симптомы и как отличить

  • Chrome: дата сертификата.
  • curl -vI https://site.example/SSL certificate problem: certificate has expired.
  • HTTP 80 работает, HTTPS нет.
  • Часть клиентов ок: у них в кэше? Нет, TLS проверяется каждый handshake. Скорее другой IP/CDN/старый A.
КартинаНе expiry
name mismatchдругой CN/SAN
unknown CA / incompleteцепочка
connection refused 443nginx не слушает
HTTP не редиректитредирект

Возможные причины

  1. certbot.timer выключен, сертификат 90 дней истёк.
  2. Renew падал месяцами (403 на ACME), никто не смотрел лог.
  3. В nginx указан /etc/ssl/certs/ssl-cert-snakeoil.pem, а не /etc/letsencrypt/live/site.example/fullchain.pem.
  4. Несколько файлов: vhost отдаёт старый cert.pem из /etc/nginx/ssl/ копии.
  5. Часы сервера сильно в будущем/прошлом (редко, NTP).
  6. CDN держит старый cert на edge, origin уже новый.

Диагностика

1. Что отдаёт 443

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

Сравните IP и DNS: разные cert — вы смотрите не тот узел.

2. Файлы на диске

sudo certbot certificates
sudo openssl x509 -in /etc/letsencrypt/live/site.example/fullchain.pem -noout -dates
sudo grep -R ssl_certificate /etc/nginx/sites-enabled
sudo grep -R SSLCertificateFile /etc/apache2/sites-enabled

3. Время хоста

timedatectl
date -u

4. Почему не продлилось

sudo journalctl -u certbot.service -n 50 --no-pager
sudo cat /var/log/letsencrypt/letsencrypt.log | tail -n 80
systemctl list-timers certbot.timer

Решение

Сценарий A. Lineage есть, файлы expired, challenge жив

sudo certbot renew --cert-name site.example
sudo nginx -t && sudo systemctl reload nginx
# или
sudo apache2ctl configtest && sudo systemctl reload apache2

Если renew говорит not due — для уже expired это странно: даты в lineage должны быть в прошлом, renew должен пройти. Смотрите, что --cert-name тот.

Сценарий B. Challenge сломан

Почините HTTP-01 (webroot /var/www/site.example, порт 80), затем:

sudo certbot certonly --webroot -w /var/www/site.example -d site.example -d www.site.example

Плагин --nginx / --apache допустим, если vhost стандартный.

Не --force-renewal, пока dry-run не зелёный:

sudo certbot renew --dry-run --cert-name site.example

Сценарий C. vhost смотрит не в live/

Исправление nginx:

ssl_certificate     /etc/letsencrypt/live/site.example/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/site.example/privkey.pem;

Не копируйте fullchain.pem в /etc/nginx/ssl/ как единственный путь: renew обновит только live/. Если копия нужна — хук --deploy-hook.

sudo nginx -t && sudo systemctl reload nginx

Apache:

SSLCertificateFile /etc/letsencrypt/live/site.example/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/site.example/privkey.pem

Сценарий D. Включить автопродление

sudo systemctl enable --now certbot.timer
sudo certbot renew --dry-run

Deploy-hook пример:

sudo sh -c 'printf "%s\n" "#!/bin/sh" "nginx -t && systemctl reload nginx" > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh'
sudo chmod 700 /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Без reload после renew процессы держат старый inode до reload — на практике пакетный hook часто уже reload делает; проверьте.

Сценарий E. CDN

Обновите cert на edge или включите «Full (strict)» только когда origin уже с валидным cert. Иначе получите петлю/ошибки.

Сценарий F. Несколько сертификатов в ssl_certificate (SNI)

nginx -T может показать два server на 443: default_server со snakeoil и site.example с LE. Клиент без SNI (редкость) или openssl s_client без -servername получит snakeoil / expired default. Всегда тестируйте -servername site.example. Почините default 443 или уберите listen 443 ssl default_server с просроченным файлом.

После копирования /etc/letsencrypt с другого сервера проверьте, что archive/ и live/ симлинки целы (ls -l live/site.example). Обрыв симлинка → nginx не стартует или отдаёт старый inode. Не certbot --force-renewal как замена ln -sf.

Часы: если timedatectl показывает +2 дня, браузер может считать cert ещё живым, а другой клиент — нет. Сначала NTP, потом выпуск.

Как проверить, что проблема устранена

echo | openssl s_client -connect 10.0.20.40:443 -servername site.example 2>/dev/null | openssl x509 -noout -dates
curl -sI --resolve site.example:443:10.0.20.40 https://site.example/ | head
sudo certbot renew --dry-run --cert-name site.example
systemctl is-active certbot.timer

notAfter в будущем, curl без -k код 200/301, dry-run OK. Проверьте SAN: www.site.example тоже.

HSTS мог закэшировать HTTPS: это хорошо, если cert валиден. Если HSTS включили на сломанном HTTPS — HSTS.

Если не помогло

  • s_client с --resolve ок, с DNS нет: старый A, DNS.
  • Мобильные ещё ругаются: другой vhost SNI, IPv6.
  • Цепочка неполная при новом leaf — отдайте fullchain, не cert.pem.
  • Коммерческий CA (не LE): другой процесс выпуска, certbot ни при чём.
  • После reload браузер всё ещё expired: смотрите не origin, а CDN/edge; curl --resolve на 10.0.20.40 уже новый, публичный IP — старый PoP.

Профилактика

  • Алерт notAfter - today < 21d.
  • Dry-run еженедельно.
  • Один путь к файлам: только live/.
  • Документировать, кто владеет DNS для DNS-01.

FAQ

Можно ли продлить уже expired тем же renew?

Да, если challenge проходит. Это обычный ACME.

--force-renewal обязателен?

Нет для expired. Нужен редко, когда lineage жив, до due date, и вы осознанно меняете ключ. Не как первая кнопка.

Пользователи всё ещё видят ошибку

Их DNS/кэш CDN. Проверьте dig A site.example с их сети.

HTTP работает, зачем HTTPS чинить срочно?

Пароли, cookie, SEO, HSTS. Срок — инцидент.

snakeoil после установки apache2

Замените на LE, не продлевайте snakeoil.