Короткий ответ
Продление — это certbot renew (dry-run сперва), не ручной выпуск каждый раз. Для site.example на 10.0.20.40 HTTP-01 требует: порт 80 доходит до этого nginx/Apache, location /.well-known/acme-challenge/ без 301 на HTTPS до отдачи файла, webroot /var/www/site.example совпадает с -w. DNS-01 требует TXT _acme-challenge.site.example у того провайдера, куда смотрит NS. На Ubuntu 22.04/24.04 штатно certbot.timer, не обязательно crontab root.
Если сертификат уже просрочен — выпуск заново, но причина renew всё равно та же.
Симптомы и как отличить
- Браузер ещё зелёный,
renew --dry-runкрасный — почините до expiry. - Timer не запускался недели.
- HTTP-01: CA получил 404/403/timeout с вашего A-записи.
- DNS-01: TXT нет или старый.
| Сообщение | Смысл |
|---|---|
| The client lacks sufficient authorization | HTTP-01 не отдал токен |
| Failed to connect to host | 80 закрыт, не тот IP |
| NXDOMAIN / no TXT | DNS-01 |
| too many certificates | лимит, часто после force |
Возможные причины
- A-запись указывает на старый сервер.
location / { return 301 https://...; }без исключения ACME.denyна dot-files режет/.well-known.- Webroot certbot ≠
rootvhost (файлы пишутся не туда). - Apache
AllowOverride/ nginxaliasдля well-known отсутствует. certbot.timerdisabled, ручной crontab с опечаткой.- Плагин
--nginxправит конфиг и ломаетnginx -t(старт nginx). - DNS-01: TTL/кэш, API токен панели истёк.
- IPv6 AAAA указывает не туда, CA идёт по AAAA первым.
Диагностика
1. Что стоит и когда renew
sudo certbot certificates
systemctl list-timers certbot.timer --all
systemctl status certbot.timer certbot.service --no-pager
sudo ls -l /etc/letsencrypt/renewal/site.example.conf
sudo grep -E 'authenticator|webroot|installer' /etc/letsencrypt/renewal/site.example.conf2. Dry-run
sudo certbot renew --dry-run --cert-name site.exampleЧитайте challenge URL из лога.
3. HTTP-01 руками
echo test-acme | sudo tee /var/www/site.example/.well-known/acme-challenge/ping >/dev/null
curl -sI --resolve site.example:80:10.0.20.40 http://site.example/.well-known/acme-challenge/ping
curl -s --resolve site.example:80:10.0.20.40 http://site.example/.well-known/acme-challenge/pingОжидание: 200 и тело test-acme. 301 на HTTPS проверьте вторым curl на 443 того же пути. 403 — права.
С мира (не только --resolve), чтобы увидеть чужой DNS:
curl -sI http://site.example/.well-known/acme-challenge/ping | head
dig +short A site.example
dig +short AAAA site.example4. DNS-01
dig +short TXT _acme-challenge.site.example
dig NS site.exampleПустой TXT во время dry-run плагина — API не создал запись или NS другие.
5. Порт 80 снаружи
sudo ss -tlnp | grep ':80'
sudo ufw status | grep 80Решение
Сценарий A. Исключить ACME из редиректа (nginx)
server {
listen 80;
listen [::]:80;
server_name site.example www.site.example;
location /.well-known/acme-challenge/ {
root /var/www/site.example;
default_type text/plain;
}
location / {
return 301 https://site.example$request_uri;
}
}nginx -t && reload. Подробнее про циклы: HTTP→HTTPS.
Сценарий B. Согласовать webroot
В /etc/letsencrypt/renewal/site.example.conf:
[renewalparams]
authenticator = webroot
webroot_path = /var/www/site.exampleКаталог .well-known/acme-challenge должен быть доступен www-data на запись для certbot (certbot обычно root).
Сценарий C. Timer
sudo systemctl enable --now certbot.timer
systemctl list-timers certbot.timerПакет Ubuntu ставит /lib/systemd/system/certbot.timer. Не дублируйте certbot renew в cron каждые 10 минут.
Сценарий D. DNS-01
Используйте плагин DNS вашего провайдера или ручной --manual только на стенде (на проде manual не продлится из timer). Проверьте, что API-ключ в /etc/letsencrypt с правами 600.
Сценарий E. AAAA мимо
Либо поправьте AAAA на 10.0.20.40 (если IPv6 есть — свой адрес, не выдумывайте), либо уберите битую AAAA. CA предпочитает v6.
Сценарий F. Лимит после force
Ждите окно лимитов Let's Encrypt, не выпускайте новые «тестовые» с прод. Staging CA (--test-cert) для отладки challenge.
Сценарий G. Несколько имён и expand
В renewal-файле site.example.conf перечислены SAN. Добавление shop.site.example без certbot certonly --expand -d site.example -d www.site.example -d shop.site.example оставит старый набор. HTTP-01 для нового имени требует A-записи и vhost. Не выпускайте второй независимый сертификат на тот же nginx ssl_certificate, если не умеете выбирать файл по SNI — проще один lineage.
Ubuntu: systemctl cat certbot.timer — обычно OnCalendar=*-*-* 00/12:00:00 плюс рандом. Если timer disabled после ручного mask, dry-run руками не доказывает продление в 3 часа ночи. Включите timer явно.
Проверяйте IPv6 с той же машины, откуда «у меня открывается»: curl -6 -sI http://site.example/.well-known/acme-challenge/ping.
Как проверить, что проблема устранена
sudo certbot renew --dry-run --cert-name site.example
systemctl is-enabled certbot.timer
curl -s http://site.example/.well-known/acme-challenge/pingDry-run: Congratulations. Удалите тестовый ping. Дата Expiry в certbot certificates ещё может быть старой — это нормально, renew сработает за 30 дней. Цепочка: fullchain.
Если не помогло
- CDN «оранжевое облако» на Apex без гибкого SSL: origin не видит HTTP-01 как CA. Режим DNS-only на время выпуска или DNS-01.
- Несколько vhost, default_server перехватывает
/.well-known. - Snap certbot vs apt: два бинарника, timer зовёт не тот.
- HSTS на 80 не бывает; HSTS на 443 не мешает HTTP-01, мешает только если challenge уходит на HTTPS другого хоста.
Профилактика
- Алерт за 21 день до expiry (
openssl x509 -enddate). - Dry-run в мониторинге раз в неделю.
- Документировать authenticator: webroot vs dns vs nginx plugin.
- Не держать выпуск только руками админа.
FAQ
Certificate not yet due for renewal — это ошибка?
Нет. Dry-run всё равно должен прогонять challenge. Без --dry-run renew молча выходит 0.
Нужен ли порт 443 для HTTP-01?
Нет. Нужен 80 (или DNS-01). 443 важен для самого сайта.
--nginx vs --webroot
--nginx временно крутит конфиг. На жёстко шаблонизированных vhost надёжнее webroot.
www и apex — два сертификата?
Лучше один SAN: -d site.example -d www.site.example. Renew один.
Apache .well-known Alias
Alias /.well-known/acme-challenge/ /var/www/site.example/.well-known/acme-challenge/С Require all granted на этот путь.