Як встановити Certbot для nginx на Ubuntu 24.04
Встановіть Certbot через apt або snap, виконайте certbot --nginx і отримайте сертифікат Let's Encrypt. Дізнайтеся про apt, snap та тайм-аут порту 80.
Встановлення Certbot: apt або snap
В Ubuntu 24.04 sudo apt install certbot python3-certbot-nginx встановлює робочий Certbot, який отримує справжні сертифікати Let’s Encrypt, довірені публічними центрами сертифікації. Документація upstream для Certbot рекомендує snap. Різниця невелика: snap відстежує upstream-релізи, а пакет з архіву відповідає версії, що постачалася з 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/certbotsnap постачається із власним таймером 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 — це callback. Ви запитуєте в Let's Encrypt сертифікат для example.com; сервіс визначає адресу за публічним DNS, відкриває з’єднання з портом 80 за знайденою адресою та запитує http://example.com/.well-known/acme-challenge/<token>. Ваш сервер відповідає точним вмістом токена, який Certbot щойно записав на диск. На цьому весь механізм завершується. З нього випливають три наслідки, які пояснюють більшість невдалих випусків сертифікатів.
- Порт 80 має бути доступний із публічного інтернету, а не лише з вашого ноутбука. Правило
ufw, security group хмарного провайдера або firewall у консолі VPS, який відкриває лише 443, зірве випуск сертифіката та всі наступні поновлення. - DNS уже має вказувати на цей сервер. Сервер перевірки самостійно виконує DNS-запит із зовнішньої мережі; ваші записи
/etc/hostsі кеш браузера для нього нічого не означають. - Якщо ви опублікували запис AAAA, спочатку буде використано IPv6. Let's Encrypt повторює спробу через IPv4 лише якщо з’єднання IPv6 завершується тайм-аутом. Відхилене з’єднання IPv6 не повторюється, а застарілий AAAA, спрямований на хост, який приймає з’єднання та віддає інший вміст, призводить до остаточної помилки.
Редиректи дозволені: під час перевірки виконується HTTP-редирект на HTTPS, і не має значення, що сертифікат на іншому кінці відсутній, прострочений або самопідписаний. Але перевірка не починається з іншого порту, крім порту 80. Certbot не реалізує TLS-ALPN-01, тому варіант «просто використати 443» не підходить.
Вибір authenticator: --nginx, --webroot, --standalone
--nginx — правильний варіант за замовчуванням, якщо nginx уже запущений і обслуговує домен. Certbot аналізує конфігурацію, додає тимчасову location для 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 підходить, якщо ви не хочете, щоб Certbot змінював конфігурацію nginx, яку ви генеруєте з шаблону, зберігаєте в 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 не прослуховується жодним процесом: наприклад, це mail server, API, який працює лише через 443, або скрипт першого запуску, що виконується до запуску nginx. Certbot сам прив’язується до порту 80 на кілька секунд. Якщо nginx запущений, команда завершиться помилкою. Зупиніть його на час виконання:
sudo certbot certonly --standalone -d mail.example.com \
--pre-hook "systemctl stop nginx" \
--post-hook "systemctl start nginx"Ці hook-команди записуються в конфігурацію поновлення сертифіката, тому під час автоматичного поновлення 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;
}
}Довший префікс ACME уже має пріоритет над location /, тому return 301 ніколи не обробляє запит перевірки. Модифікатор ^~ потрібен в іншому випадку: після збігу цього префікса nginx повністю пропускає регулярні location, тому правило, додане пізніше, наприклад location ~ /\. { deny all; }, не зможе заблокувати перевірку. Якщо залишити цей location на порту 80, поновлення сертифіката продовжать працювати після переведення решти сайту в режим лише HTTPS.
Обидва наведені блоки обслуговують файли з диска. Якщо nginx натомість працює як проксі перед застосунком, location / стає блоком proxy_pass, а блок server для reverse proxy, рядок за рядком описує заголовки, які потрібні цьому застосунку. ACME location і директиви TLS залишаються без змін.
Синтаксис HTTP/2 залежить від версії nginx. Ubuntu 24.04 постачається з nginx 1.24, якому потрібен вбудований запис listen 443 ssl http2;; запуск завершується помилкою, якщо використати окрему директиву http2 on;. Debian 13 постачається з nginx 1.26, якому потрібен запис http2 on;; у разі вбудованого синтаксису він лише попереджає про його застарілість. Спочатку перевірте nginx -v.
Вказуйте nginx на live/, а не на archive/. Символічні посилання live/ перенаправляються під час кожного поновлення. Жорсткий шлях до archive/ прив’яже конфігурацію до сертифіката, термін дії якого сплине.
Wildcards означають DNS-01, а DNS-01 означає використання плагіна
Wildcard-сертифікат (*.example.com) не можна перевірити через HTTP-01, оскільки немає одного hostname, з якого можна отримати файл. 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, внутрішнього сервісу, сервера, доступного лише через self-hosted WireGuard VPN на VPS, або панелі адміністрування на приватному інтерфейсі.
Поновлення: 90 днів, timer і deploy hook
Сертифікати Let’s Encrypt дійсні протягом 90 днів. Пакет apt (Certbot 2.9.0) поновлює їх, коли залишається менше ніж 30 днів. snap використовує актуальну версію Certbot. Починаючи з версії 4.1, Certbot також запитує в Let’s Encrypt рекомендований час поновлення через ACME Renewal Info (ARI) і може поновити сертифікат раніше. Для сертифіката Let’s Encrypt це зазвичай означає поновлення приблизно після двох третин строку його дії. У будь-якому разі ви маєте приблизно 30 днів, щоб виправити проблему з поновленням, перш ніж вона спричинить збій. Починаючи з наступного року, поновлення відбуватимуться частіше: профіль за замовчуванням Let’s Encrypt перейде на сертифікати строком дії 64 дні 10 лютого 2027 року, а 16 лютого 2028 року — на сертифікати строком дії 45 днів. Let’s Encrypt більше не надсилає електронні листи з попередженнями про завершення строку дії. Ніхто не нагадає вам про це особисто, тому тепер моніторинг потрібно налаштувати самостійно.
Перевірте timer, який встановив ваш пакет:
systemctl list-timers 'certbot*' 'snap.certbot*'
sudo certbot certificatescertbot renew переглядає кожну конфігурацію в /etc/letsencrypt/renewal/, пропускає ті, для яких ще не настав час, і поновлює решту з параметрами, збереженими в цій конфігурації. Це прапорці початкового запуску, якщо ви згодом не змінили їх за допомогою certbot reconfigure. Саме тому перший запуск має значення: саме його параметри записуються.
Поновлення файлу на диску саме по собі нічого не змінює. 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Команда запускає повну перевірку для Let's Encrypt у staging-середовищі: використовується той самий шлях виконання, той самий firewall і той самий DNS. Запити не враховуються в production-лімітах частоти (для staging діють окремі, значно вищі ліміти), а сертифікати не зберігаються на диску. Якщо сьогодні перевірка проходить успішно, автоматичне поновлення через 60 днів також має пройти, якщо конфігурація сервера за цей час не зміниться.
Dry run не доводить, що hook для reload спрацює, оскільки його поведінка залежить від версії Certbot. Цю частину перевірте вручну: виконайте скрипт hook безпосередньо, переконайтеся, що 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'), потім власний firewall 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 доступний, але token не надається. Запит потрапив в інший 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 (5) already issued ..., перевищено rate limit. Саме цю помилку часто отримують під час циклічного налагодження. У повідомленні зазначено обмеження, а в кінці наведено час retry after. Let's Encrypt обмежує кількість дублікатів сертифікатів для точно такого самого набору імен до п’яти на тиждень і не передбачає для цього обмеження override, тому його можна зняти лише після завершення цього періоду. Окремо дозволено отримувати 50 нових сертифікатів на зареєстрований домен на тиждень. Це обмеження можна підвищити, надіславши запит на override. Запит для точно такого самого набору імен, як у наявному сертифікаті, вважається поновленням і не підпадає під це обмеження. Поновлення, скоординовані через ARI, не підпадають під жодне обмеження. Виконуйте налагодження в 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, випустіть сертифікат і відновіть block.
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 не пройшов перевірку, і всі інші імена в цьому сертифікаті також перестануть працювати. Окремі сертифікати для кожного сайту виходять з ладу незалежно один від одного. Саме це потрібно на сервері, де розміщено більше ніж кілька сервісів. Коли сайтів стає більше, ніж кілька, варто використовувати 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 зводиться до таких кроків: синхронізувати дерево за допомогою rsync із -a, встановити Certbot, змінити DNS і виконати certbot renew --dry-run перед перемиканням.
Після перебудови сервера або переходу на новий LTS таймер поновлення не переноситься автоматично. Після будь-якої міграції, відновлення зі snapshot або оновлення дистрибутива виконайте systemctl list-timers 'certbot*' і один раз --dry-run. Якщо цього не зробити, через 89 днів сайт перестане працювати о 3-й годині ночі, коли всі вважали, що сертифікат поновлюється автоматично.
Наведені вище маршрути HTTP-01 передбачають сервер, яким ви керуєте, публічну IP-адресу та порт 80, відкритий для всього світу. Іншими словами, потрібен VPS. Для DNS-01 це не потрібно. Наведені вище дії однакові в обох випадках.
Ті самі кроки для роботи із сертифікатами застосовуються в Apache замість nginx. Якщо публічний сертифікат недоступний, self-signed certificate в Ubuntu підходить для внутрішніх сервісів.
FAQ
Чи потрібно відкривати порт 80, якщо сайт працює лише через HTTPS?
Так, для перевірки HTTP-01. Let’s Encrypt завжди починає запит перевірки через порт 80, а Certbot не має реалізації TLS-ALPN-01. Тому firewall, у якому відкрито лише 443, блокує і первинну видачу сертифіката, і кожне подальше автоматичне поновлення. Перенаправлення з порту 80 на HTTPS допустиме: перевірка переходить за ним. Повністю відмовитися від порту 80 можна лише за допомогою DNS-01 — через plugin провайдера або Certbot's --manual --preferred-challenges dns. У такому разі TXT-запис потрібно додати вручну, а поновлення перестає бути автоматичним, якщо не вказати скрипт --manual-auth-hook.
apt чи snap: що встановити для Certbot з nginx на Ubuntu 24.04?
Використовуйте apt. sudo apt install certbot python3-certbot-nginx надає Certbot 2.9.0 в Ubuntu 24.04. Ця версія достатньо актуальна для всіх описаних у цьому посібнику дій. Вона отримує security patches через unattended-upgrades і не потребує snapd. Обирайте snap лише тоді, коли вам негайно потрібен найновіший реліз або DNS plugin, який поширюється тільки як snap. У будь-якому разі виберіть рівно один варіант: два встановлення створюють два таймери поновлення, що працюють з одним деревом /etc/letsencrypt, а забутий варіант зрештою спричиняє проблеми.
Чи може Certbot видати wildcard-сертифікат для nginx?
Лише через DNS-01. Для wildcard на кшталт *.example.com немає одного hostname, з якого можна отримати файл перевірки. Тому --nginx, --webroot і --standalone не підходять. Встановіть plugin для свого DNS-провайдера, збережіть обмежений за правами API token у файлі облікових даних, доступному лише root, і виконайте certbot certonly --dns-cloudflare -d example.com -d '*.example.com'. Візьміть wildcard у лапки, щоб shell не обробив його як glob.
Чому nginx досі віддає старий сертифікат після успішного поновлення?
nginx зберігає сертифікат у пам’яті й не бачить нового файлу на диску, доки не перезавантажить конфігурацію. certbot --nginx виконує reload автоматично, але --webroot і --standalone цього не роблять. Тому поновлення може завершитися успішно, а браузер і далі бачити сертифікат, термін дії якого скоро спливає. Додайте виконуваний скрипт у /etc/letsencrypt/renewal-hooks/deploy/. Скрипт має виконувати nginx -t && systemctl reload nginx, і він запускатиметься після кожного успішного поновлення.
Чи підтверджує certbot renew --dry-run, що поновлення працюватиме?
Переважно. Команда виконує реальну перевірку в staging-середовищі, використовуючи той самий firewall, DNS і кодовий шлях, але не впливаючи на production rate limits і не зберігаючи сертифікати на диску. Тому успішна перевірка означає, що мережева частина працює. Однак вона ненадійно підтверджує запуск deploy hook. Перевірте це окремо: запустіть hook script вручну та перевірте sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.