Let's Encrypt: сертификат не обновляется автоматически — причины и решение
Сайт вдруг встречает посетителей предупреждением о просроченном сертификате — хотя вы «всё настроили один раз и забыли». Когда Let's Encrypt сертификат не обновляется автоматически, дело почти никогда не в самом Let's Encrypt: ломается механизм автопродления на вашей стороне. Таймер не запущен, hook не перезагрузил веб-сервер, порт 80 закрылся. Разберём, где рвётся цепочка, и починим её так, чтобы забыть о сертификате по-настоящему.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Как устроено автопродление
Сертификаты Let's Encrypt живут 90 дней, и продлевать их положено заранее — обычно за 30 дней до конца. Автоматику обеспечивает не магия, а конкретный планировщик: либо systemd-таймер certbot.timer, либо cron-задание. Он периодически запускает certbot renew, а тот сам проверяет, каким сертификатам пора продлеваться, и продлевает только их.
Отсюда ясно, что «не обновляется» распадается на несколько независимых вопросов: запускается ли планировщик вообще; доходит ли дело до certbot renew; успешно ли проходит валидация при продлении; и подхватывает ли веб-сервер новый сертификат после выпуска. Сбой на любом из этих этапов даёт один и тот же симптом — просроченный сертификат. Поэтому чинить надо по порядку, проверяя каждое звено.
Важно понять и то, почему проблема всплывает так внезапно. Автопродление — тихий фоновый процесс: пока сертификат действует, никто не замечает, что механизм давно сломан. Поломка происходит в момент выпуска или сразу после первичной настройки, а последствия проявляются через два-три месяца, когда старый сертификат истекает. Из-за этой отсрочки причину легко искать не там: вы вспоминаете вчерашние правки, хотя реальный сбой заложен ещё при установке. Поэтому не доверяйте ощущению «оно же работало» — проверяйте цепочку целиком, звено за звеном.
Проверяем, работает ли планировщик
Первым делом убедитесь, что механизм запуска вообще жив. На современных системах это systemd-таймер.
systemctl list-timers | grep certbot
systemctl status certbot.timer
Если таймер inactive или его нет — автопродление просто некому запускать. Включите его:
systemctl enable --now certbot.timer
На системах с cron проверьте задание вместо таймера:
cat /etc/cron.d/certbot
ls -l /etc/cron.d/ | grep certbot
Частая причина, почему таймера нет: certbot ставили не из пакета, а через pip или snap, и юнит не создался. Или систему переустанавливали/мигрировали, а таймер забыли включить. Убедитесь, что планировщик активен — без него всё остальное не имеет значения, продление никогда не запустится само.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.
Арендовать VPS с чистым IPТестируем продление вручную
Даже при живом таймере само продление может падать. Проверьте это безопасно, не тратя лимиты, в режиме симуляции:
certbot renew --dry-run
Эта команда проделывает весь путь продления, но не выпускает реальный сертификат. Если --dry-run проходит — механизм валидации в порядке, и проблема в планировщике или в hook перезагрузки. Если падает — читайте, на чём именно. Чаще всего всплывают те же причины, что и при первом выпуске: закрылся порт 80, изменился конфиг nginx, слетел webroot.
tail -40 /var/log/letsencrypt/letsencrypt.log
Особенно коварна ситуация, когда за 90 дней вы поменяли что-то в инфраструктуре — перенесли webroot, добавили редирект на HTTPS, поставили Cloudflare-прокси. При первом выпуске всё работало, а тихое автопродление спустя месяцы упёрлось в изменившееся окружение. --dry-run ловит это заранее, поэтому запускайте его после любых правок веб-сервера.
Порт 80 закрылся со временем
Очень распространённый сценарий: изначально сайт был на HTTP, порт 80 был открыт, сертификат выпустился. Потом вы перевели всё на HTTPS и закрыли или перестали слушать 80-й как «ненужный». А продление по HTTP-01 требует именно 80-й. В итоге спустя два месяца certbot renew не может пройти валидацию.
ss -tlnp | grep ':80'
ufw status | grep 80
curl -I http://ваш-домен/.well-known/acme-challenge/test
Держите 80-й порт открытым и слушающим постоянно, даже на чисто-HTTPS сайте — хотя бы для отдачи ACME-challenge и редиректа. Если закрыть 80 принципиально нужно, переведите продление на DNS-01 через плагин вашего DNS-провайдера, тогда входящий 80-й не потребуется. Но для большинства проще оставить 80-й приоткрытым только под .well-known/acme-challenge/.
Hook не перезагружает веб-сервер
Хитрая поломка: сертификат исправно продлевается, но сайт всё равно отдаёт старый — потому что nginx или apache держит в памяти прежний файл и не знает, что на диске уже новый. Продление без перезагрузки веб-сервера бесполезно для посетителя.
Проверьте, настроен ли deploy-hook:
cat /etc/letsencrypt/renewal/ваш-домен.conf | grep hook
Добавьте перезагрузку после успешного продления — глобально для всех сертификатов:
certbot renew --deploy-hook "systemctl reload nginx"
Или пропишите в файле /etc/letsencrypt/renewal-hooks/deploy/reload.sh скрипт с systemctl reload nginx и правами на исполнение — тогда он выполнится после любого успешного продления. Сравните дату файла сертификата и время последней перезагрузки nginx: если сертификат новее — значит, hook не сработал, и посетители видят старый.
Обратите внимание на разницу между --deploy-hook и устаревшими --renew-hook и --pre-hook. Deploy-hook выполняется только после реального обновления сертификата, а не при каждом холостом запуске certbot renew, — это именно то поведение, которое нужно для перезагрузки веб-сервера. Если вы по старой памяти повесили перезагрузку на pre-hook, nginx будет дёргаться при каждой проверке, даже когда продлевать нечего, а нужный момент — сразу после выпуска — может оказаться не покрыт. Держите один явный deploy-hook и не смешивайте его со старыми вариантами.
openssl x509 -enddate -noout -in /etc/letsencrypt/live/ваш-домен/fullchain.pem
Ставим мониторинг срока
Даже отлаженная автоматика может тихо сломаться, и вы узнаете об этом только от рассерженных пользователей. Правильно — следить за сроком заранее. Простая проверка даты окончания в мониторинге снимает риск.
# сколько дней осталось до истечения
openssl x509 -enddate -noout -in /etc/letsencrypt/live/ваш-домен/fullchain.pem
# проверка удалённо, как видит посетитель
echo | openssl s_client -servername ваш-домен -connect ваш-домен:443 2>/dev/null | openssl x509 -noout -enddate
Заведите алерт, если до истечения осталось меньше 20 дней — это запас, чтобы спокойно разобраться, если автопродление подведёт. Let's Encrypt также присылает письма о скором истечении на указанный при регистрации email, но полагаться только на них не стоит: письмо легко пропустить. Собственный мониторинг надёжнее. На стабильном сервере с постоянно открытым 80-м портом и рабочим deploy-hook сертификат обновляется годами без вашего участия.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.
Арендовать VPS с чистым IPОбсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Частые вопросы
Как проверить, что автопродление вообще настроено?
Выполните systemctl list-timers | grep certbot и certbot renew --dry-run. Первый показывает, запускается ли планировщик, второй симулирует продление целиком. Оба должны отрабатывать без ошибок.
Почему сертификат продлился, но браузер видит старый?
Веб-сервер держит прежний файл в памяти и не перечитал новый. Нужен deploy-hook с systemctl reload nginx, который перезагружает сервер после каждого продления. Без него новый сертификат лежит на диске, но не отдаётся.
Может ли автопродление сломаться из-за изменений на сайте?
Да, и это частый случай: перенос webroot, новый редирект на HTTPS, закрытый порт 80 или Cloudflare-прокси ломают валидацию, которая при первом выпуске проходила. Запускайте --dry-run после любых правок веб-сервера.
Нужен ли открытый порт 80, если сайт полностью на HTTPS?
Для HTTP-01 продления — да, 80-й нужен постоянно. Либо держите его приоткрытым под ACME-challenge, либо переведите продление на DNS-01, где входящий 80-й не требуется.
Нужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.