Ситуация из практики: после переноса сервисов на новый сервер выпускаем сертификат командой certbot --nginx — и обнаруживаем, что nginx отдаёт не то. Кастомные location исчезли, проксирование на бэкенд слетело, а в server-блоке появились чужие строки. Certbot молча переписал конфиг. Разберёмся, почему он это делает, как вернуть всё назад и как выпускать сертификаты, не подпуская certbot к конфигурации вообще.

Почему certbot трогает конфиг

У certbot есть два принципиально разных режима работы с nginx:

  • --nginx (installer) — certbot сам парсит конфиги nginx, сам вписывает пути к сертификатам, директивы listen 443 ssl, редирект с 80 на 443 и перечитывает nginx. Удобно на типовом сервере, разрушительно на сервере с ручной конфигурацией: парсер certbot не понимает сложные конструкции, include-цепочки и нестандартные блоки — и «нормализует» их по своему разумению.
  • certonly — certbot только получает сертификат и кладёт файлы в /etc/letsencrypt/live/домен/. Конфигурацию nginx не трогает вообще.

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

Как вернуть конфиг

Хорошая новость: certbot делает бэкапы перед каждой правкой. Лежат они здесь:

ls -la /etc/letsencrypt/backups/
ls -la /var/lib/letsencrypt/backups/   # в старых версиях

Внутри — каталоги с датой изменения, в каждом лежат оригинальные файлы. Можно откатиться и штатно:

certbot rollback

Эта команда возвращает конфиги nginx к состоянию до последнего вмешательства certbot (сами сертификаты остаются на месте). После отката обязательно:

nginx -t && systemctl reload nginx

Правильный workflow: certonly + webroot

Выпускаем сертификат, не давая certbot прав на конфиг. Для домена, который уже обслуживается nginx, удобнее всего метод webroot. В server-блок порта 80 добавляем один location:

location /.well-known/acme-challenge/ {
    root /var/www/letsencrypt;
}

Создаём каталог и выпускаем:

mkdir -p /var/www/letsencrypt
nginx -t && systemctl reload nginx

certbot certonly --webroot -w /var/www/letsencrypt -d example.com -d www.example.com

Пути к сертификатам вписываем в конфиг сами — ровно туда и так, как нужно нам:

ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

Продление при этом полностью автоматическое: таймер certbot.timer дважды в день проверяет сроки, а nginx достаточно перечитать после обновления. Для этого добавляем deploy-hook — он срабатывает только при фактическом продлении:

cat > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh <<'EOF'
#!/bin/sh
nginx -t && systemctl reload nginx
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Если сертификат уже выпущен через --nginx

Переводим существующий сертификат на webroot без перевыпуска — правим его renewal-конфиг /etc/letsencrypt/renewal/example.com.conf:

[renewalparams]
authenticator = webroot
webroot_path = /var/www/letsencrypt
# строку installer = nginx &mdash; удалить или закомментировать

Именно строка installer = nginx означает «certbot имеет право править конфиги при продлении». Убрали её — и при следующем renewal конфигурация останется нетронутой. Проверяем вхолостую:

certbot renew --dry-run

Итого

  • На серверах с ручной конфигурацией — только certbot certonly --webroot.
  • Уже пострадавший конфиг возвращается через certbot rollback или из /etc/letsencrypt/backups/.
  • У существующих сертификатов проверьте renewal-конфиг: там не должно быть installer = nginx.
  • Перечитывание nginx после продления — через deploy-hook, а не через права certbot на конфиг.

Проверено на Ubuntu 22.04/24.04 с certbot из системных пакетов и snap. Если нужна помощь с настройкой веб-серверов, SSL или переносом сайтов — оставьте заявку, поможем.