SSD Nodes Learn Hosting plans →
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-25

Установка Certbot для Nginx на Ubuntu 24.04

Установите Certbot через apt install python3-certbot-nginx или snap. Узнайте, как избежать конфликтов версий, настроить автоматическое обновление и решить проблему таймаута 80 порта.

Установка Certbot: apt или snap

В Ubuntu 24.04 пакет sudo apt install certbot python3-certbot-nginx предоставляет работоспособный Certbot, который выпускает настоящие, доверенные публичные сертификаты Let's Encrypt. Официальная документация Certbot рекомендует использовать snap; разница невелика: snap отслеживает релизы разработчиков, а пакет из репозитория следует версии, выпущенной вместе с LTS, и получает только обновления безопасности.

Выберите один вариант. Наличие двух копий Certbot означает два таймера обновления, нацеленных на одно и то же дерево /etc/letsencrypt, и та копия, о которой вы забудете, станет причиной неожиданных проблем.

Путь через apt:

sudo apt update
sudo apt install certbot python3-certbot-nginx

Эта команда устанавливает /usr/bin/certbot, плагин для nginx, пару certbot.service + certbot.timer и запись /etc/cron.d/certbot, которая не выполняет действий при использовании systemd.

Путь через snap:

sudo apt remove certbot python3-certbot-nginx
sudo snap install core && sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

Snap поставляется со своим собственным таймером snap.certbot.renew.timer. Удалите пакет apt перед установкой snap.

После установки оба варианта работают одинаково. Certbot 2.x по умолчанию использует ключи ECDSA (P-256); передавайте флаг --key-type rsa только для клиентов, которые не поддерживают ECDSA. Все данные хранятся в /etc/letsencrypt: в archive/ находятся актуальные файлы ключей и сертификатов, live/ содержит символические ссылки на текущие версии, renewal/ хранит по одному конфигурационному файлу на каждый сертификат, а accounts/ — ваш ключ учетной записи ACME.

Как работает HTTP-01 и почему порт 80 обязателен

Проверка HTTP-01 — это механизм обратного вызова. Вы запрашиваете у Let's Encrypt сертификат для example.com; центр сертификации разрешает имя через публичный DNS, открывает соединение с портом 80 по найденному адресу и запрашивает http://example.com/.well-known/acme-challenge/<token>. Ваш сервер отвечает точным содержимым токена, который Certbot только что записал на диск. Это весь механизм целиком. Из него вытекают три следствия, которые объясняют большинство неудачных попыток выпуска сертификатов.

  • Порт 80 должен быть доступен из публичного интернета, а не только с вашего локального компьютера. Правило ufw, группа безопасности облачного провайдера или брандмауэр в консоли VPS, открывающий только 443 порт, блокируют выпуск сертификата и любое его последующее обновление.
  • DNS уже должен указывать на этот сервер. Сервер валидации выполняет собственный поиск извне; ваши записи /etc/hosts и кэш браузера для него не имеют значения.
  • Если вы публикуете запись AAAA, сначала проверяется IPv6. Let's Encrypt повторяет попытку через IPv4, если соединение по IPv6 полностью не удается, но устаревшая запись AAAA, указывающая на хост, который принимает соединение и отдает что-то другое, приведет к критической ошибке.

Редиректы разрешены: валидация следует за HTTP-редиректом на HTTPS и не обращает внимания на то, что сертификат на конечном узле отсутствует, просрочен или является самоподписанным. Чего она не будет делать, так это начинать проверку где-либо, кроме порта 80. В Certbot нет реализации TLS-ALPN-01, поэтому вариант «просто используйте 443» не является обходным путем.

Выбор метода аутентификации: --nginx, --webroot, --standalone

--nginx — оптимальный выбор по умолчанию, если nginx уже запущен и обслуживает домен. Certbot анализирует конфигурацию, добавляет временную локацию для прохождения проверки (challenge), перезагружает nginx, подтверждает владение доменом, а затем записывает TLS-директивы в блок server. Простой не требуется.

sudo certbot --nginx -d example.com -d www.example.com

Скриптовый вариант для «чистой» системы:

sudo certbot --nginx \
  -d example.com -d www.example.com \
  --agree-tos -m ops@example.com --no-eff-email \
  --redirect --non-interactive

--webroot подходит, если вы не хотите, чтобы Certbot вносил изменения в конфигурацию nginx. Это актуально, если вы генерируете конфиги из шаблонов, храните их в git или разворачиваете через Ansible. Certbot создает только файл проверки в директории, которую вы уже обслуживаете.

sudo certbot certonly --webroot -w /var/www/example.com \
  -d example.com -d www.example.com \
  --deploy-hook "systemctl reload nginx"

--standalone подходит, если порт 80 свободен: например, для почтового сервера, API, работающего только по 443 порту, или скрипта первичной настройки, выполняемого до установки nginx. Certbot самостоятельно занимает порт 80 на несколько секунд. Если nginx уже запущен, этот метод завершится ошибкой, поэтому остановите его перед запуском:

sudo certbot certonly --standalone -d mail.example.com \
  --pre-hook "systemctl stop nginx" \
  --post-hook "systemctl start nginx"

Эти хуки сохраняются в конфигурации обновления сертификата, поэтому остановка и запуск будут выполняться автоматически при каждом продлении.

Серверный блок, работающий до и после получения сертификата

Проблема «курицы и яйца»: nginx отказывается запускаться, если ssl_certificate указывает на несуществующий файл, а Certbot не может пройти валидацию, пока nginx остановлен. Сначала запустите сайт на 80 порту.

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    root /var/www/example.com;
    index index.html;

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/example.com;
        default_type "text/plain";
        try_files $uri =404;
    }

    location / {
        try_files $uri $uri/ =404;
    }
}

Выполните sudo nginx -t && sudo systemctl reload nginx, убедитесь, что curl -I http://example.com/ отвечает извне сервера, а затем выпустите сертификат. После этого:

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/example.com;
        default_type "text/plain";
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name example.com www.example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    root /var/www/example.com;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

Префикс ^~ для location ACME оправдывает себя: он не дает блоку return 301 перехватывать запрос на проверку. Сохранение этого location на 80 порту гарантирует, что продление сертификатов продолжит работать даже после того, как остальная часть сайта перейдет исключительно на HTTPS.

Оба блока выше отдают файлы с диска; если nginx выступает в роли фронтенда для приложения, location / превращается в блок proxy_pass, а серверный блок обратного прокси, построчно описывает заголовки, необходимые приложению, в то время как location ACME и директивы TLS остаются без изменений.

Синтаксис HTTP/2 зависит от версии nginx, и смешивание этих двух форм приводит к ошибке при запуске. В Ubuntu 24.04 поставляется nginx 1.24, который требует указания параметра в строке, listen 443 ssl http2;. В Debian 13 поставляется более новый nginx, требующий отдельной директивы http2 on;. Сначала проверьте nginx -v.

Указывайте nginx путь к live/, никогда не используйте archive/. Символические ссылки live/ обновляются при каждом продлении; жестко прописанный путь в archive/ привяжет вас к сертификату, который истечет, пока вы об этом не узнаете.

Wildcard-сертификаты требуют DNS-01, а DNS-01 требует плагин

Wildcard-сертификат (*.example.com) невозможно подтвердить через HTTP-01, так как не существует единственного имени хоста, с которого можно было бы получить файл. Единственный путь — DNS-01: вы подтверждаете контроль над доменом, публикуя TXT-запись _acme-challenge.example.com. Для автоматизации этого процесса Certbot требуются API-ключи вашего DNS-провайдера, именно для этого и существуют плагины. В полном руководстве по wildcard-сертификатам описаны механизмы работы с TXT-записями и ловушки при ручном продлении; ниже приведена краткая версия для Cloudflare.

sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflare

В случае использования apt-пакета путь будет выглядеть как sudo apt install python3-certbot-dns-cloudflare. Учётные данные следует разместить в файле, доступном только пользователю root:

# /root/.secrets/cloudflare.ini
# then: sudo chmod 600 /root/.secrets/cloudflare.ini
dns_cloudflare_api_token = your_scoped_token_here

Ограничьте права токена только редактированием DNS для конкретной зоны. Это ключ к вашей DNS-инфраструктуре; относитесь к нему соответствующим образом.

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
  -d example.com -d '*.example.com'

Заключайте wildcard-имя в кавычки, чтобы оболочка (shell) не интерпретировала символ подстановки. DNS-01 также решает задачи, недоступные для HTTP-01: получение сертификатов для хостов без открытого 80-го порта, внутренних сервисов, серверов, доступных только через собственный WireGuard VPN на VPS, или панелей управления в закрытых сетях.

Обновление: 90 дней, таймер и deploy hook

Сертификаты Let's Encrypt действительны в течение 90 дней. Certbot выполняет обновление, когда до истечения срока остается менее 30 дней. Это дает вам окно в 30 дней, в течение которого сбой обновления — это лишь повод для исправления, а не причина простоя. Let's Encrypt больше не рассылает уведомления об истечении срока действия по электронной почте, никто не напомнит вам об этом, поэтому мониторинг теперь — ваша задача.

Проверьте таймер, который был установлен при инсталляции:

systemctl list-timers 'certbot*' 'snap.certbot*'
sudo certbot certificates

certbot renew обходит все конфигурации в /etc/letsencrypt/renewal/, пропускает те, до истечения срока которых более 30 дней, и обновляет остальные, используя в точности те же флаги, что были при первом запуске. Именно поэтому первый запуск так важен: его параметры сохраняются.

Обновление файла на диске само по себе ничего не меняет: nginx продолжает отдавать старый сертификат из оперативной памяти, пока не получит команду на перезагрузку. Настройте deploy hook один раз:

sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh >/dev/null <<'EOF'
#!/bin/sh
set -e
nginx -t && systemctl reload nginx
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

Любой исполняемый файл в renewal-hooks/deploy/ запускается после каждого успешного обновления. Флаг --deploy-hook выполняет ту же задачу для одного сертификата, сохраняя renew_hook = ... в его конфигурации обновления. certbot --nginx выполняет перезагрузку автоматически; в конфигурациях --webroot и --standalone это не предусмотрено. Отсутствие hook — это именно та причина, по которой сайт отдает просроченный сертификат, в то время как certbot certificates бодро сообщает о наличии свежего. Любое другое приложение, которое считывает сертификат при запуске, требует аналогичного hook. Контейнеризированному приложению, такому как инсталляция Nextcloud на VPS с Docker, TLS и резервным копированием, также необходимо настроить здесь собственный шаг перезапуска или перезагрузки.

Тестирование обновления в реальных условиях

sudo certbot renew --dry-run

Эта команда запускает полную процедуру проверки в тестовой среде (staging) Let's Encrypt: используется тот же путь выполнения кода, те же настройки межсетевого экрана, те же DNS-записи, при этом лимиты на количество запросов не расходуются, а изменения на диск не записываются. Если проверка проходит успешно сегодня, автоматическое обновление через 60 дней также пройдет успешно, при условии, что конфигурация сервера останется неизменной.

Пробный запуск (dry run) не гарантирует срабатывание хука перезагрузки (reload hook), так как поведение этой функции зависит от версии Certbot. Проверьте этот этап вручную: запустите скрипт хука напрямую, убедитесь, что команда systemctl reload nginx выполняется успешно, и проверьте статус через sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.

Ошибки, с которыми вы столкнетесь на практике

Could not bind to IPv4 or IPv6., --standalone возникает, когда nginx уже занял 80 порт. Используйте --nginx или --webroot, либо остановите nginx на время запуска. Проверьте, какой процесс занимает порт, с помощью sudo ss -lntp | grep ':80'.

Timeout during connect (likely firewall problem), Let's Encrypt не может подключиться к 80 порту. Проверяйте по порядку: sudo ufw status (откройте его через sudo ufw allow 'Nginx Full'), затем встроенный файрвол провайдера VPS, затем DNS. Протестируйте доступность с внешнего узла, а не с самого сервера: curl -sSv http://example.com/.well-known/acme-challenge/test. Устаревшая AAAA-запись приводит к такому же сообщению.

unauthorized :: Invalid response from http://example.com/.well-known/acme-challenge/xyz: 404, 80 порт доступен, но токен не отдается. Запрос попал в другой server block (проверьте, какой из них владеет default_server), либо директория, переданная в -w, не совпадает с той, которую обслуживает nginx. Создайте файл в /var/www/example.com/.well-known/acme-challenge/test и попробуйте получить его извне; если сервер возвращает 404, проблема была не в сертификате.

DNS problem: NXDOMAIN looking up A for example.com, имя не разрешается публично. Это новые записи, которые еще не распространились, либо запись в зоне, которую ваш регистратор не обслуживает.

too many certificates already issued for: example.com, ограничение по количеству запросов (rate limit), с которым сталкиваются при отладке в цикле. Let's Encrypt ограничивает выпуск дубликатов сертификатов (для того же набора имен) пятью штуками в неделю, а также разрешает 50 новых сертификатов на зарегистрированный домен в неделю; эти ограничения снимаются только со временем. Для отладки используйте тестовую среду с флагом --dry-run.

nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/example.com/fullchain.pem": No such file or directory, nginx настроен на сертификат, который не был выпущен или был удален командой certbot delete. Закомментируйте TLS-блок, запустите nginx, выпустите сертификат, затем верните блок в конфигурацию.

open() "/etc/letsencrypt/options-ssl-nginx.conf" failed, этот файл поставляется вместе с пакетом плагина для nginx. На системе certonly без python3-certbot-nginx либо установите плагин, либо замените строку include на собственные настройки ssl_protocols и ssl_ciphers.

Масштабирование управления сертификатами

Один сертификат может содержать до 100 имен, и использование одного certbot --nginx -d a.example.com -d b.example.com ... выглядит заманчиво, пока одна устаревшая DNS-запись не нарушит валидацию и не выведет из строя все остальные имена в этом сертификате. Отдельные сертификаты для каждого сайта выходят из строя независимо, что и требуется на сервере, где размещено более нескольких ресурсов. Если сайтов много, использование reverse proxy с поддержкой ACME оправдывает себя: Traefik reverse proxy, работающий с несколькими приложениями через Docker Compose, самостоятельно запрашивает и обновляет сертификаты, а Certbot полностью исключается из процесса. Выбор конкретного прокси — отдельная задача, и сравнение Nginx, Caddy и Traefik сводится к тому, какую часть работы с сертификатами и конфигурацией приложений вы готовы делегировать прокси-серверу.

Выполняйте резервное копирование /etc/letsencrypt целиком, используя sudo tar -czf letsencrypt-$(date +%F).tar.gz -C /etc letsencrypt, с сохранением символических ссылок. В этом дереве каталогов хранится accounts/ — ваш ключ учетной записи ACME, который невозможно восстановить в исходном виде. Переезд на новый VPS сводится к следующему: синхронизируйте дерево каталогов с помощью -a, установите Certbot, обновите DNS-записи и выполните certbot renew --dry-run перед переключением трафика.

При пересборке сервера или переходе на новый LTS-релиз таймер обновления не переносится автоматически. После любой миграции, восстановления из снимка (snapshot) или обновления дистрибутива выполните systemctl list-timers 'certbot*' и один --dry-run. Если пропустить этот шаг, сайт станет недоступен через 89 дней, в 3 часа ночи, из-за сертификата, который, как все полагали, обновляется автоматически.

Все вышесказанное предполагает, что вы контролируете сервер с публичным IP-адресом и открытым 80 портом, то есть используете VPS. Механизмы работы на любом из них идентичны.

Те же шаги по работе с сертификатами применимы для Apache вместо nginx, а если использование публичного сертификата невозможно, самоподписанный сертификат на Ubuntu подойдет для внутренних сервисов.

FAQ

Нужно ли открывать 80 порт, если сайт работает только по HTTPS?

Да, для прохождения проверки HTTP-01. Let's Encrypt всегда начинает запрос на проверку с 80 порта, а в Certbot нет реализации TLS-ALPN-01. Поэтому файрвол, открывающий только 443 порт, заблокирует как первичную выдачу сертификата, так и любое последующее автоматическое продление. Редирект с 80 порта на HTTPS допустим, проверка проследует за ним. Единственный способ полностью исключить 80 порт — использовать DNS-01 с плагином для вашего провайдера.

Что выбрать для nginx на Ubuntu 24.04: apt или snap?

Используйте apt. sudo apt install certbot python3-certbot-nginx предоставляет Certbot 2.9.0 в Ubuntu 24.04, чего достаточно для всех задач из этого руководства. Обновления безопасности приходят через unattended-upgrades, а установка snapd не требуется. Выбирайте snap только в том случае, если вам срочно нужна самая новая версия или DNS-плагин, распространяемый исключительно через snap. В любом случае выберите что-то одно: две установки означают два таймера продления, направленных на один и тот же каталог /etc/letsencrypt, и тот, о котором вы забудете, обязательно создаст проблемы.

Может ли Certbot выпустить wildcard-сертификат для nginx?

Только через DNS-01. У wildcard-домена вроде *.example.com нет конкретного имени хоста, с которого можно было бы получить файл проверки, поэтому --nginx, --webroot и --standalone не подходят. Установите плагин для вашего DNS-провайдера, поместите API-токен с ограниченными правами в файл с доступом только для root и запустите certbot certonly --dns-cloudflare -d example.com -d '*.example.com', взяв wildcard в кавычки, чтобы оболочка не интерпретировала его как шаблон.

Почему nginx продолжает отдавать старый сертификат после успешного продления?

nginx хранит сертификат в оперативной памяти и не замечает изменений файла на диске до перезагрузки. certbot --nginx выполняет перезагрузку автоматически, но --webroot и --standalone этого не делают. В результате продление может пройти успешно, а браузер продолжит видеть истекающий сертификат. Поместите исполняемый скрипт в /etc/letsencrypt/renewal-hooks/deploy/, который выполняет nginx -t && systemctl reload nginx, и он будет запускаться после каждого успешного продления.

Гарантирует ли certbot renew --dry-run, что продление сработает?

Почти. Команда выполняет реальную проверку в тестовой среде (staging), используя те же настройки файрвола, DNS и пути выполнения кода. Это не расходует лимиты на запросы и не записывает данные на диск, поэтому успех означает, что сетевая часть настроена верно. Однако это не гарантирует срабатывание скрипта развертывания (deploy hook). Протестируйте его отдельно: запустите скрипт вручную и проверьте sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.