UptimeChecker

Let's Encrypt не продлился: чек-лист починки

Обложка: Let's Encrypt не продлился: чек-лист починки

Почему Let's Encrypt не продлился: как это устроено

Let's Encrypt выдаёт сертификаты бесплатно, но с коротким сроком действия — 90 дней. Это осознанное решение: чем короче жизнь сертификата, тем меньше окно для злоупотреблений, если ключ скомпрометирован. Плата за короткий срок — необходимость регулярно продлевать сертификат. Большинство администраторов автоматизируют это через certbot и таймер systemd, поэтому в норме продление происходит незаметно за несколько недель до истечения.

Проблема возникает, когда автоматика ломается, а узнаёте вы об этом в самый неподходящий момент: браузер показывает «срок действия сертификата истёк», посетители видят предупреждение, а клиенты пишут, что «сайт лежит». Симптом «Let's Encrypt не продлился» почти всегда означает одно: автоматический запуск certbot renew перестал выполняться или стал падать с ошибкой, и это тихо копилось неделями.

Разберём, как устроено продление, какие причины ломают его чаще всего и как восстановить работоспособность — по шагам, с командами и пояснением вывода.

Как продление работает изнутри

Стандартная схема на сервере с nginx или Apache выглядит так:

  1. certbot устанавливается вместе с плагином (python3-certbot-nginx или python3-certbot-apache).
  2. При первой выдаче сертификата создаётся конфигурация в /etc/letsencrypt/renewal/.
  3. Таймер systemd (certbot.timer) по расписанию вызывает certbot renew.
  4. certbot проверяет, каким сертификатам до истечения осталось меньше 30 дней, и продлевает их.
  5. После продления certbot выполняет хуки — перезагрузку веб-сервера, чтобы тот подхватил новые файлы.

Ключевой файл — /etc/letsencrypt/renewal/ваш-домен.conf. В нём записаны параметры продления: как проверять владение доменом, какие хуки запускать после выдачи.

cat /etc/letsencrypt/renewal/example.com.conf

В выводе важны две директивы:

[renewalparams]
authenticator = webroot
webroot_path = /var/www/html,
account = abc123...
  • authenticator — способ подтверждения владения доменом (webroot, nginx, apache, standalone, dns-*).
  • webroot_path — каталог, куда certbot кладёт проверочный файл для HTTP-01 валидации.

Если здесь указан путь, который больше не существует, или веб-сервер отдаёт не тот каталог, продление падает. Это одна из самых частых причин, почему Let's Encrypt не продлился.

Причины, по которым продление ломается

Причин немного, но каждая встречается регулярно. Удобно держать их в виде таблицы «симптом → причина → решение».

Симптом Причина Решение
certbot renew падает с «no such file or directory» Удалён каталог webroot_path или сайт перенесён на другой путь Вернуть каталог или обновить webroot_path
Ошибка HTTP-01 валидации, «connection refused» Порт 80 закрыт, сервер не отвечает, сайт за редиректом на HTTPS без обработки ACME Открыть порт 80, исключить ACME-пути из редиректа
Ошибка DNS («NXDOMAIN», «no valid A records») Домен не резолвится или A/AAAA-запись смотрит на другой сервер Проверить dig, поправить DNS
Таймер не срабатывает certbot.timer выключен или отсутствует Включить и проверить systemctl list-timers
Продление проходит, но сайт всё равно с истёкшим сертификатом Веб-сервер не перезагружен, отдаёт старый сертификат из кеша Выполнить systemctl reload nginx
«too many certificates already issued» Превышен лимит Let's Encrypt на домен Дождаться сброса лимита, разобраться с дублями
«urn:ietf:params:acme:error:rateLimited» Слишком много неудачных попыток валидации подряд Подождать час, устранить корневую причину

Шаг 1. Проверить, что с сертификатом и таймером

Первое, что стоит сделать, — посмотреть фактическое состояние сертификатов на сервере. certbot умеет показывать это одной командой:

certbot certificates

Вывод для каждого сертификата:

Found the following certs:
  Certificate Name: example.com
    Serial Number: 4f2a...
    Key Type: RSA
    Domains: example.com www.example.com
    Expiry Date: 2024-05-01 12:00:00+00:00 (INVALID: EXPIRED)
    Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/example.com/privkey.pem

Смотрим на строку Expiry Date. Если там EXPIRED — продление действительно не случилось. Если до истечения ещё больше 30 дней, а сайт всё равно ругается, проблема не в certbot, а в том, что веб-сервер отдаёт старый файл.

Второе — проверяем, живой ли таймер автопродления:

systemctl list-timers certbot.timer
systemctl status certbot.timer

Если таймера в списке нет или статус inactive, автоматическое продление не выполняется вообще. Включить его можно так:

systemctl enable --now certbot.timer

После этого certbot renew будет запускаться сам по расписанию (обычно дважды в сутки), а реальное продление сертификатов произойдёт, когда до истечения останется меньше 30 дней.

Вывод certbot certificates с истёкшим сертификатом

Шаг 2. Прогнать сухую проверку certbot renew --dry-run

Прежде чем трогать что-то по-настоящему, безопасно прогнать весь цикл продления в тестовом режиме. Для этого есть флаг --dry-run:

certbot renew --dry-run

Команда проходит все те же этапы, что и реальное продление — обращается к серверу Let's Encrypt (в тестовую среду staging), выполняет валидацию домена, перевыпускает сертификат, — но полученный сертификат не сохраняется как рабочий, а используется только для проверки. Успешный вывод:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/example.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Account registered.
Simulating renewal of an existing certificate for example.com and www.example.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all simulated renewals succeeded:
  /etc/letsencrypt/live/example.com/fullchain.pem (success)

Ключевая строка — Congratulations, all simulated renewals succeeded. Если она появилась, конфигурация валидна и проблема была либо в отключённом таймере, либо в том, что реальное продление не запускалось.

Если же проверка падает, читаем конкретную ошибку. Типичные случаи:

Failed authorization procedure. example.com (http-01): urn:ietf:params:acme:error:connection :: The server could not connect to the client to verify the domain :: Fetching http://example.com/.well-known/acme-challenge/xxx: Connection refused

Здесь сервер Let's Encrypt не смог подключиться к сайту по порту 80. Причина — порт закрыт фаерволом, сайт переехал, или nginx слушает только 443. Проверить доступность порта можно снаружи и изнутри:

curl -I http://example.com/.well-known/acme-challenge/test

Если запрос не доходит, надо открыть порт 80 и убедиться, что веб-сервер на него отвечает. Важный нюанс: редирект с HTTP на HTTPS не должен перехватывать ACME-пути, иначе certbot при authenticator = webroot не сможет добраться до проверочного файла.

Шаг 3. Исправить причину и запустить реальное продление

Когда --dry-run прошёл успешно, можно запускать настоящее продление:

certbot renew

Успешный вывод сообщит, какие сертификаты обновлены, а какие ещё не требуют этого:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/example.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Certificate not yet due for renewal; no action taken.

Или, если сертификат действительно подошёл к концу:

Renewing an existing certificate for example.com and www.example.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all renewals succeeded:
  /etc/letsencrypt/live/example.com/fullchain.pem (success)

После успешного продления новые файлы лежат в /etc/letsencrypt/live/example.com/, но nginx или Apache всё ещё могут держать в памяти старый сертификат. Чтобы сервер подхватил обновлённые файлы, его нужно перезагрузить:

systemctl reload nginx

Для Apache команда аналогична:

systemctl reload apache2

reload мягче, чем restart: сервер перечитывает конфигурацию и файлы сертификатов, не обрывая текущие соединения. Именно поэтому в хуках certbot почти всегда используется reload, а не полный перезапуск.

Шаг 4. Настроить автопродление так, чтобы оно не ломалось

После разовой починки стоит устранить корень проблемы, чтобы через 90 дней история не повторилась. Практические меры:

  1. Прописать reload-хук. В /etc/letsencrypt/renewal/example.com.conf добавить директиву, чтобы после каждого продления веб-сервер перезагружался сам:

    renew_hook = systemctl reload nginx
    

    Тогда не будет ситуации «сертификат обновился, а сайт отдаёт старый».

  2. Убедиться, что порт 80 открыт и отдаёт ACME-пути. Даже если весь сайт редиректит на HTTPS, для webroot-валидации нужен HTTP.

  3. Проверить корректность DNS. Если домен смотрит на другой сервер или записи A/AAAA отсутствуют, валидация провалится. Проверить резолвинг можно так:

    dig example.com A +short
    

    Вывод — список IP-адресов, на которые указывает домен. Если он пуст или указывает на другой хост, проблему надо искать в DNS, а не в certbot.

  4. Следить за сроком заранее. Не дожидаться, пока браузеры начнут показывать ошибку. Регулярная проверка срока действия сертификата — это то, что умеет делать инструмент проверки срока SSL-сертификата у UptimeChecker: он показывает, сколько дней осталось до истечения, и позволяет настроить напоминание до того, как сертификат протухнет.

Частные случаи: webroot, standalone и смена плагина

Отдельный класс проблем возникает, когда способ валидации в конфигурации больше не соответствует реальности. Классический сценарий: сайт изначально выдавался с authenticator = nginx, затем администратор вручную перенёс сертификат на другой сервер или пересобрал конфигурацию nginx, и теперь плагин не может выполнить валидацию привычным способом.

Если сайт переехал и проверочный каталог изменился, обновить путь можно вручную в /etc/letsencrypt/renewal/example.com.conf (директива webroot_path) или перевыпустить сертификат с новым способом:

certbot certonly --webroot -w /новый/путь -d example.com -d www.example.com

Флаг --webroot явно указывает плагину класть проверочные файлы в заданный каталог, а -w задаёт сам путь. После перевыпуска с новым путём автопродление будет использовать уже корректные параметры.

Ещё один нюанс — лимиты Let's Encrypt. Если в конфигурации накопилось много дублей (несколько записей на один домен с разными путями), можно упереться в лимит на число сертификатов и получить too many certificates already issued. В таком случае стоит навести порядок: удалить неиспользуемые записи из /etc/letsencrypt/renewal/, оставив одну актуальную на домен, и дождаться сброса лимита.

Связка с мониторингом: как не пропустить момент

Отдельная ручная проверка раз в 90 дней не спасёт — человеческий фактор почти гарантированно приведёт к пропуску. Рабочий подход — автоматическая проверка, которая оповестит заранее, а не постфактум.

Схема простая:

  1. Внешний мониторинг регулярно проверяет срок действия сертификата сайта.
  2. Если до истечения осталось меньше порога (например, 14 или 7 дней), приходит уведомление.
  3. Администратор успевает продлить сертификат, пока посетители ничего не заметили.

Для самопроверки прямо сейчас удобно использовать проверку SSL-сертификата: она покажет, действителен ли сертификат, до какой даты и корректно ли собрана цепочка. А если вы уже столкнулись с истёкшим сертификатом на проде, начните со статьи про истёкший SSL-сертификат — там разобраны первые действия в аварийной ситуации.

Чек-лист починки: короткая версия

Соберём всё в один список, по которому удобно идти при инциденте «Let's Encrypt не продлился»:

  1. Посмотреть состояние: certbot certificates — истёк ли сертификат на самом деле.
  2. Проверить таймер: systemctl list-timers certbot.timer — включено ли автопродление.
  3. Прогнать тест: certbot renew --dry-run — валидна ли конфигурация, какая именно ошибка.
  4. Устранить причину: порт 80, DNS, webroot-путь, лимиты Let's Encrypt.
  5. Запустить реальное продление: certbot renew.
  6. Перезагрузить веб-сервер: systemctl reload nginx.
  7. Проверить результат снаружи: curl -Iv https://example.com и срок действия через сервис проверки SSL.
  8. Закрепить: reload-хук, открытый порт 80, автоматический мониторинг срока.

Успешная симуляция продления certbot

Цикл автоматического продления сертификата

Если продление настроено правильно и сервер перезагружается после каждой выдачи, проблема «Let's Encrypt не продлился» перестанет быть регулярной. Останется только одна зона риска — сам факт истечения сертификата, и её надёжно закрывает внешняя проверка срока действия. Проверьте свой сайт в инструменте срока SSL-сертификата, чтобы убедиться, что до ближайшего истечения у вас есть запас времени.