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

Продление — это 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 authorizationHTTP-01 не отдал токен
Failed to connect to host80 закрыт, не тот IP
NXDOMAIN / no TXTDNS-01
too many certificatesлимит, часто после force

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

  1. A-запись указывает на старый сервер.
  2. location / { return 301 https://...; } без исключения ACME.
  3. deny на dot-files режет /.well-known.
  4. Webroot certbot ≠ root vhost (файлы пишутся не туда).
  5. Apache AllowOverride / nginx alias для well-known отсутствует.
  6. certbot.timer disabled, ручной crontab с опечаткой.
  7. Плагин --nginx правит конфиг и ломает nginx -t (старт nginx).
  8. DNS-01: TTL/кэш, API токен панели истёк.
  9. 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.conf

2. 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.example

4. 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/ping

Dry-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 на этот путь.