Короткий ответ
Просроченный сертификат на 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 443 | nginx не слушает |
| HTTP не редиректит | редирект |
Возможные причины
certbot.timerвыключен, сертификат 90 дней истёк.- Renew падал месяцами (403 на ACME), никто не смотрел лог.
- В nginx указан
/etc/ssl/certs/ssl-cert-snakeoil.pem, а не/etc/letsencrypt/live/site.example/fullchain.pem. - Несколько файлов: vhost отдаёт старый
cert.pemиз/etc/nginx/ssl/копии. - Часы сервера сильно в будущем/прошлом (редко, NTP).
- 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-enabled3. Время хоста
timedatectl
date -u4. Почему не продлилось
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 nginxApache:
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-runDeploy-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.timernotAfter в будущем, 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.