1. 1
  2. 2
  3. 3
  4. 4
  5. 5

# теория · шаг 2 из 5

Нативный ACME-модуль nginx

Коротко

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

  • Ставится пакетом nginx-module-acme и подключается директивой load_module в самом начале nginx.conf (вне всех блоков).
  • Обязателен resolver - без него конфиг не соберётся: acme_issuer: "resolver" is not configured.
  • А вот забытый state_path, отсутствие ssl_certificate_cache и маска в server_name проверку конфига проходят и ломаются уже в работе.
  • Wildcard модуль не умеет: для них по-прежнему certbot с dns-01.

Если знаешь, что такое acme_issuer и зачем ему state_path - листай до «Теперь сам».

Сначала ответь сам

Модуль подключён, конфиг написан, nginx -t говорит successful. Сколько из четырёх типовых ошибок ниже он поймал?

/etc/nginx/nginx.confhttp
acme_issuer le {
    uri https://acme-v02.api.letsencrypt.org/directory;
    contact mailto:admin@shop.local;
    # state_path не указан
}
server {
    server_name *.shop.local;          # маска
    acme_certificate le;
    ssl_certificate     $acme_certificate;
    ssl_certificate_key $acme_certificate_key;
    # ssl_certificate_cache не указан
}

Ни одной. Проверка конфига ловит только отсутствие resolver; остальные три всплывут в работе - первая при перезапуске сервера, вторая под нагрузкой, третья при первом же выпуске. Это стоит помнить: у модуля nginx -t проверяет синтаксис, а не осмысленность.

На что это похоже

Разница между «нанять курьера, который раз в квартал ездит за справкой» и «поставить в офисе аппарат, который печатает справку сам». Курьера надо помнить, оплачивать и проверять; аппарат работает молча, но у него свои требования - его надо подключить к сети и не выключать из розетки, иначе всё, что он напечатал, исчезнет.

state_path - это и есть розетка.

Механизм: кто и когда ходит в центр сертификации

certbot таймер systemd файлы на диске deploy-hook reload модуль ACME nginx следит за сроком сам state_path на диске reload не нужен у модуля на одну движущуюся часть меньше - и на один каталог больше
Обе схемы делают одно и то же. Разница в том, сколько отдельных механизмов должно сработать.

Правило

/etc/nginx/nginx.confhttp
resolver 127.0.0.53;                 # DNS-сервер: модуль сам ходит в сеть, и ему нужно имя разрешать в адрес

acme_issuer letsencrypt {
    uri                     https://acme-v02.api.letsencrypt.org/directory;
    contact                 mailto:admin@shop.local;
    state_path              /var/cache/nginx/acme-letsencrypt;
    accept_terms_of_service;
}

server {
    listen 443 ssl;
    server_name shop.local www.shop.local;

    acme_certificate letsencrypt;

    ssl_certificate       $acme_certificate;
    ssl_certificate_key   $acme_certificate_key;
    ssl_certificate_cache max=2;     # держать в памяти до 2 сертификатов (RSA и EC), не разбирая файл на каждое соединение

    root /var/www/shop;
}

server {
    listen 80;                        # нужен для проверки http-01
    server_name shop.local www.shop.local;
    return 301 https://$host$request_uri;
}

acme_issuer описывает центр, acme_certificate заказывает сертификат для имён из server_name, переменные подставляют выпущенное. ssl_certificate_cache обязателен: при сертификате из переменной без него он разбирается заново на каждое соединение.

Обрати внимание на блок на 80-м порту: путь /.well-known/acme-challenge/ модуль обрабатывает сам, отдельный location не нужен. Редирект на https ему тоже не мешает - челлендж модуль перехватывает раньше. Это ровно та ловушка, которая в предыдущем уроке ломала certbot, и здесь её нет.

Разбор: что ловит проверка конфига, а что нет

Прогон по стенду с настоящим модулем, каждый раз убираем одну строку:

что убрали nginx -t когда всплывёт
resolver [emerg] acme_issuer: "resolver" is not configured сразу
state_path successful при перезапуске сервера: всё выпускается заново
ssl_certificate_cache successful под нагрузкой: разбор сертификата на каждое соединение
accept_terms_of_service successful при первом обращении к центру
маска в server_name successful при выпуске: имена нужны явные

Первая строка - единственная, ради которой стоит помнить точный текст ошибки. Остальные четыре - причина, по которой конфиг модуля стоит один раз прогнать не только через nginx -t, но и через реальный старт со --staging-центром.

Разбор: почему state_path не мелочь

Без него аккаунтный ключ и выпущенные сертификаты живут только в памяти. Они переживают reload, но теряются при рестарте - и после каждой перезагрузки сервера всё выпускается заново.

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

Что ломается без этого

Две системы на один домен. Если раньше домен вёл certbot, а теперь его ведёт модуль, старый таймер надо выключить. Иначе обе системы выпускают сертификаты на одни и те же имена, съедая лимит вдвое быстрее, а в конфиге остаются пути к файлам, которые уже никто не обновляет.

Первые секунды после старта. Пока сертификат не выпущен, сайт по https не отвечает. Для боевого сервера это несколько секунд, для тестов - повод не удивляться, что сразу после docker compose up соединение не устанавливается.

Зачем это в работе

Правило выбора получается простое. Модуль - когда сайтов немного, имена известны и хочется убрать из системы одну движущуюся часть: ни таймера, ни хуков, ни отдельного пользователя с правами на конфиг. certbot - когда нужны wildcard (это только dns-01), когда сертификаты используются не одним nginx (почтовый сервер, база) или когда инфраструктура уже построена вокруг него.

Что происходит, видно в обычном логе ошибок:

↳  выводtail -f /var/log/nginx/error.log | grep -i acme
[notice] 914#914: ACME: certificate for shop.local, www.shop.local issued,
expires Nov  5 16:02:10 2026 GMT

Теперь сам

Сервер с модулем перезагружали трижды за вечер во время отладки, и теперь сайт не поднимается: сертификата нет, в логе ошибка от центра сертификации. Что произошло и что делать?

Скорее всего, в конфиге нет state_path: при каждом рестарте модуль выпускал сертификат заново, и три-четыре выпуска подряд выбрали недельный лимит на домен. Делать: добавить state_path, а на время ожидания лимита переключить uri на --staging-адрес центра, чтобы отладку можно было продолжать. Сертификат оттуда браузер не примет, но сайт хотя бы поднимется.

Главное

Нативный ACME-модуль (1.29.0, пакет nginx-module-acme) выпускает и продлевает сертификаты внутри nginx: acme_issuer описывает центр, acme_certificate заказывает сертификат для имён из server_name, $acme_certificate подставляет его в ssl_certificate вместе с обязательным ssl_certificate_cache. Перезагрузка после продления не нужна, а путь челленджа модуль обрабатывает сам. Из четырёх типовых ошибок nginx -t ловит только отсутствие resolver; забытый state_path теряет всё при рестарте и съедает лимиты, а wildcard модуль не умеет вовсе.

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.