+7 (920) 556-26-91 пн–пт: 10:00–19:00 (МСК)
Проконсультироваться

Инструкции

Nginx и Let’s Encrypt на Ubuntu 24.04: certbot snap

nginx из apt, Certbot только snap. HTTP-01 требует порт 80. AAAA Let's Encrypt проверяет первым. fullchain + privkey.

Nginx и Let’s Encrypt на Ubuntu 24.04: certbot snap

Короткий ответ: на Ubuntu 24.04 ставьте nginx из apt (ветка 1.24 — норма), Certbot — только snap: sudo snap install --classic certbot. Пакет certbot из apt снесите. Сертификат: sudo certbot --nginx. Для HTTP-01 обязателен порт 80 с интернета. Если у домена есть AAAA, Let’s Encrypt идёт туда первым. В конфиге: ssl_certificate = fullchain, ssl_certificate_key = privkey. Продление — таймер snap.

Гайд для одного домена на чистом VPS Ubuntu 24.04, факты на 13–14 сентября 2026. Сначала базовая система: настройка Ubuntu. После сертификата тот же nginx станет прокси для n8n, WordPress или вебхука Telegram. Команды копируйте, example.com замените на свой домен.

Что должно быть до команд

  • Домен с A-записью на публичный IPv4 этого VPS. Проверка: dig +short A example.com совпадает с curl -4 -s https://ifconfig.me (или IP из панели хостера).
  • Если заводите AAAA — она должна указывать на IPv6 этого же сервера, не на старый хостинг и не на «заглушку регистратора».
  • Порты 80 и 443 свободны на хосте: ничего не слушает их, пока не поставите nginx. Docker с -p 80:80 тоже не должен висеть — опубликованные порты контейнеров обходят ufw, см. Docker CE.
Шаг Канон Не делать
nginx sudo apt install nginx, Ubuntu 1.24 Собирать nginx с нуля, тащить сторонние PPA без нужды
Certbot snap classic, затем certbot --nginx apt install certbot python3-certbot-nginx и оставлять оба
Проверка владения HTTP-01, порт 80 с мира Закрыть 80 после выдачи сертификата и ждать, что renew пройдёт
Файлы TLS fullchain.pem + privkey.pem cert.pem вместо fullchain, «цепочка отдельно когда-нибудь»

Документация: инструкция Certbot для nginx + snap, TLS-сертификаты в Ubuntu Server, IPv6 у Let’s Encrypt, пакет nginx в noble.

Ставим nginx из apt

В Ubuntu 24.04 (noble) пакет nginx — 1.24.0 с патчами дистрибутива. Для обратного прокси и Let’s Encrypt этой ветки достаточно. Не гонитесь за 1.26/1.28 из сторонних репозиториев, пока нет конкретной нужды.

sudo apt update
sudo apt install nginx
nginx -v
sudo systemctl enable --now nginx
sudo systemctl status nginx --no-pager
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1/

Проверка: nginx version: nginx/1.24.0, юнит active (running), локальный HTTP отвечает 200 (дефолтная страница Ubuntu). Если 80 занят — ss -tulpn | grep ':80 ', не ставьте второй веб-сервер рядом.

Откройте порты в ufw до запроса сертификата. HTTP-01 ходит на 80; 443 понадобится сразу после.

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw status verbose

Профиль Nginx Full — это 80/tcp и 443/tcp. Не оставляйте только 443: renew тоже использует 80.

Минимальный HTTP-блок домена. Certbot ищет server_name в sites-enabled и вписывает SSL туда. Без этого файла certbot --nginx не поймёт, какой vhost править.

sudo tee /etc/nginx/sites-available/example.com <<'EOF'
server {
    listen 80;
    listen [::]:80;
    server_name example.com;
    root /var/www/example.com;
    index index.html;
    location / {
        try_files $uri $uri/ =404;
    }
}
EOF
sudo mkdir -p /var/www/example.com
echo 'ok' | sudo tee /var/www/example.com/index.html
sudo ln -sfn /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
curl -sS -o /dev/null -w '%{http_code} %{redirect_url}\n' -H 'Host: example.com' http://127.0.0.1/
curl -sS -o /dev/null -w '%{http_code}\n' http://example.com/

С сервера и с телефона по сотовой сети (не с той же подсети, если у провайдера «серый» NAT) http://example.com/ должен отдать 200 и ok. Если с мира таймаут — DNS, фаервол хостера или AAAA на чужой адрес. Certbot в этой точке запускать рано.

Certbot только из snap

Официальный путь EFF и Ubuntu: snap с classic confinement. Пакет apt в noble отстаёт и ставит свой systemd-timer. Два certbot на одной машине — рулетка, какая бинарник попадёт в PATH. Снесите apt-пакет, даже если «его вроде нет»: команда идемпотентна.

sudo apt remove certbot python3-certbot-nginx python3-certbot-apache
sudo snap install --classic certbot
sudo ln -sf /snap/bin/certbot /usr/local/bin/certbot
command -v certbot
certbot --version
sudo snap list certbot

Проверка: command -v certbot/usr/local/bin/certbot или /snap/bin/certbot, не /usr/bin/certbot от apt. Версию в этом тексте не фиксируем: snap сам обновляет клиент. Симлинк в /usr/local/bin — шаг из инструкции certbot.eff.org.

Выпуск сертификата, nginx-плагин правит конфиг сам:

sudo certbot --nginx -d example.com

Интерактив: email (уведомления об истечении), согласие с ToS, вопрос про редирект HTTP→HTTPS — для сайта отвечайте «редирект». Certbot добавит listen 443 ssl, пути к ключам и, при согласии, 301 с 80 на 443. Затем:

sudo nginx -t
sudo systemctl reload nginx
curl -sS -o /dev/null -w '%{http_code}\n' http://example.com/
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/
sudo grep -n 'ssl_certificate' /etc/nginx/sites-enabled/example.com

Ожидаемо: HTTP 301 на https, HTTPS 200, в конфиге строки:

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

Именно fullchain, не cert.pem. Браузерам нужна цепочка. Ключ — privkey.pem, не «bundle» и не cert. Файлы живые симлинки в /etc/letsencrypt/live/; после renew они продолжают указывать на новый номер в archive. Не копируйте pem «в /etc/nginx/ssl, чтобы было спокойнее» — тогда renew обновит archive, а nginx останется со старой копией.

Порт 80, IPv6 и почему выпуск падает

HTTP-01: CA кладёт (через certbot) файл на http://example.com/.well-known/acme-challenge/... и забирает его с своих серверов. Порт 80 обязан быть открыт с интернета и в момент выпуска, и каждые ~60 дней на renew. Закрыть 80 «потому что теперь HTTPS» — типичный способ сломать продление через два месяца.

Let’s Encrypt при наличии A и AAAA сначала идёт в AAAA. Если IPv6 указывает не на этот nginx, проверка провалится, даже когда IPv4 идеальный. Фоллбэк на IPv4 есть только при таймауте соединения, не когда «на AAAA отвечает чужой веб-сервер». Чинится так: поправить AAAA на этот VPS или удалить AAAA, если IPv6 вам не нужен. Предпочесть IPv4 запросом к LE нельзя — см. IPv6 Support.

dig +short A example.com
dig +short AAAA example.com
curl -4 -sS -o /dev/null -w 'v4 %{http_code}\n' http://example.com/
curl -6 -sS -o /dev/null -w 'v6 %{http_code}\n' http://example.com/ || echo 'IPv6 с этой машины не проходит — проверьте AAAA'

Если AAAA есть, а curl -6 не 200/301 с этого же сервера — не вызывайте certbot, сначала DNS.

После сертификата: превратить сайт в прокси

Один домен уже на HTTPS. Теперь можно не раздавать статику, а проксировать бэкенд на loopback. Паттерн тот же, что в гайде по n8n: снаружи 443, внутри 127.0.0.1:порт. Certbot уже вписал ssl-пути — их не трогайте. Добавьте location.

Пример: прокси на приложение на 127.0.0.1:8080. Для n8n порт 5678 и обязательный WebSocket — в отдельном гайде, не копируйте этот упрощённый блок туда без Upgrade.

sudo tee /etc/nginx/sites-available/example.com <<'EOF'
server {
    listen 80;
    listen [::]:80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

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

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

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
EOF
sudo nginx -t
sudo systemctl reload nginx

После ручной перезаписи файла прогоните sudo certbot renew --dry-run: плагин должен по-прежнему находить vhost. Если dry-run ругается на server_name или не видит 80 — вы выкинули listen 80; верните его хотя бы с редиректом, ACME нужен HTTP.

Автопродление snap: таймер snap.certbot.renew.timer, не certbot.timer от apt.

systemctl list-timers | grep certbot
sudo systemctl status snap.certbot.renew.timer --no-pager
sudo certbot renew --dry-run
sudo certbot certificates

dry-run должен закончиться без ошибок. certbot certificates показывает домен, expiry (~90 дней) и пути fullchain/privkey. Если таймера snap нет, а есть только certbot.timer — вы всё ещё на apt-пакете, вернитесь к шагу «снести apt, поставить snap».

Проверка глазами: браузер, замок, не «сертификат не доверен». С командной строки: echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -issuer -dates -ext subjectAltName — issuer Let’s Encrypt, в SAN ваш домен.

FAQ

Почему нельзя apt install certbot?

Официальная инструкция EFF для Linux — snap. Apt-пакет в Ubuntu 24.04 отстаёт и ставит другой таймер. Два certbot конфликтуют в PATH. Снесите apt, поставьте sudo snap install --classic certbot.

Зачем порт 80, если сайт уже на HTTPS?

HTTP-01 проверяет владение по HTTP на 80, в том числе при renew. Редирект на HTTPS нормален, если challenge-путь достижим. Закрытый 80 ломает продление. DNS-01 для обычного одного домена не нужен.

Выпуск падает, хотя A-запись верная

Смотрите AAAA. Let’s Encrypt сначала стучится в IPv6. Чужой или мёртвый AAAA = ошибка challenge, даже при идеальном IPv4. Исправьте AAAA или удалите её.

Чем fullchain отличается от cert.pem?

cert.pem — только сертификат имени. fullchain.pem — он плюс промежуточный. В nginx в ssl_certificate нужен fullchain, в ssl_certificate_key — privkey. Иначе часть клиентов не соберёт цепочку.

Как понять, что renew живой, не дожидаясь 90 дней?

sudo certbot renew --dry-run и systemctl status snap.certbot.renew.timer. Snap гоняет renew дважды в сутки, сертификат обновляется, когда до истечения меньше 30 дней. После успешного renew nginx перечитывается плагином —nginx.

Ещё гайды