SSD Nodes Learn
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-07-24

Как установить Certbot для nginx на Ubuntu 24.04

Инструкция по установке Certbot через apt или snap. Узнайте, почему важно удалить старый пакет и как избежать конфликтов таймеров обновления Let's Encrypt.

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

В Ubuntu 24.04 sudo apt install certbot python3-certbot-nginx предоставляет рабочий Certbot, который выпускает реальные, общедоступные сертификаты Let's Encrypt. В официальной документации Certbot рекомендуется использовать snap. Различие минимально: snap-пакет следует за обновлениями разработчика, а пакет из репозитория (apt) содержит версии, поставляемые с 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 challenge — это механизм обратного вызова. Вы запрашиваете у 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-запись, направляющая запрос на хост, который принимает соединение, но отдает другие данные, приведет к критической ошибке.

Перенаправления (redirects) разрешены: проверка следует по HTTP-перенаправлению на HTTPS и не учитывает, что сертификат на конечном узле отсутствует, просрочен или является самоподписанным. Однако проверка не начнется ни на каком порту, кроме 80. У Certbot нет реализации TLS-ALPN-01, поэтому использование только 443 порта не является решением проблемы.

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

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

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 подходит, если вы не хотите изменять конфигурацию nginx через Certbot — например, если вы используете конфигурации из шаблонов, храните их в git или развертываете через Ansible. Certbot записывает только файл challenge в директорию, которую вы уже обслуживаете.

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"

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

Блок server, работающий до создания сертификата и после него

Проблема «курицы и яйца»: 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.

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

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

Wildcards означают DNS-01, а DNS-01 означает плагин

Wildcard-сертификат (*.example.com) невозможно проверить через HTTP-01, так как для загрузки файла нет единого имени хоста. DNS-01 — единственный доступный способ: вы подтверждаете владение доменом, создавая _acme-challenge.example.com TXT-запись. Для автоматического выполнения этой задачи 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) не выполняла подстановку (globbing). DNS-01 также решает задачи, которые не под силу HTTP-01: получение сертификатов для хостов без открытого 80-го порта — например, для внутренних сервисов, узлов, доступных только через self-hosted 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 этого не делают. Отсутствие хука — это причина, по которой сайт отдает просроченный сертификат, в то время как certbot certificates сообщает о наличии свежего. Любое другое приложение, считывающее сертификат при запуске, требует такого же хука — контейнеризированное приложение, такое как Nextcloud VPS install with Docker, TLS and backups, также требует настройки шага перезапуска или перезагрузки здесь.

Тестирование процесса обновления

sudo certbot renew --dry-run

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

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

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

Could not bind to IPv4 or IPv6.--standalone, так как порт 80 уже занят процессом nginx. Используйте --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 новых сертификатов на один зарегистрированный домен в неделю. Ограничения снимаются только со временем. Для отладки используйте staging с помощью --dry-run.

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

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

Управление сертификатами в масштабе

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

Создайте резервную копию /etc/letsencrypt целиком — sudo tar -czf letsencrypt-$(date +%F).tar.gz -C /etc letsencrypt — с сохранением всех symlinks. В этой директории хранится accounts/, ваш ключ аккаунта ACME, который невозможно идентично перегенерировать. Перенос на новый VPS сводится к следующему: выполните rsync директории с флагом -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, заблокирует как первичную выдачу сертификата, так и все последующие автоматические обновления. Перенаправление (redirect) с порта 80 на HTTPS допустимо — проверка пройдет по нему. Единственный способ полностью обойти порт 80 — использовать DNS-01 с плагином провайдера.

apt или snap — какой Certbot установить для nginx на Ubuntu 24.04?

Используйте 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, чтобы предотвратить подстановку символов оболочкой (shell globbing).

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

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

Доказывает ли certbot renew --dry-run, что обновление будет работать?

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