Instalacja Certbot dla nginx na Ubuntu 24.04
Porównanie metod apt oraz snap dla Certbot na Ubuntu 24.04. Dowiedz się, dlaczego należy unikać duplikacji pakietów i jak poprawnie skonfigurować odnawianie.
Instalacja Certbot: apt lub snap
W systemie Ubuntu 24.04 sudo apt install certbot python3-certbot-nginx zapewnia działający pakiet Certbot, który wystawia rzeczywiste, publicznie zaufane certyfikaty Let's Encrypt. Dokumentacja producenta zaleca użycie wersji snap; różnica jest niewielka — wersja snap śledzi najnowsze wydania, natomiast pakiet z repozytorium śledzi wersję dostarczoną w LTS i otrzymuje poprawki bezpieczeństwa.
Należy wybrać jedną metodę. Posiadanie dwóch kopii Certbot powoduje istnienie dwóch harmonogramów odnawiania skierowanych do tego samego drzewa /etc/letsencrypt, co może prowadzić do błędów w działaniu zapomnianej wersji.
Metoda apt:
sudo apt update
sudo apt install certbot python3-certbot-nginxPowyższa komenda instaluje /usr/bin/certbot, wtyczkę nginx, parę certbot.service + certbot.timer oraz wpis /etc/cron.d/certbot, który nie wykonuje żadnych operacji w systemieD.
Metoda 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/certbotWersja snap posiada własny harmonogram, snap.certbot.renew.timer. Należy usunąć pakiet apt przed instalacją wersji snap.
Po instalacji obie metody działają identycznie. Certbot 2.x domyślnie używa kluczy ECDSA (P-256) — flagę --key-type rsa należy przekazać tylko wtedy, gdy klient nie obsługuje ECDSA. Wszystkie dane składowane są w /etc/letsencrypt: archive/ zawiera rzeczywiste pliki kluczy i certyfikatów, live/ zawiera linki symboliczne do aktualnych plików, renewal/ zawiera jeden plik konfiguracyjny na certyfikat, a accounts/ zawiera klucz konta ACME.
Na czym polega mechanizm HTTP-01 i dlaczego port 80 jest obowiązkowy
Wyzwanie HTTP-01 to mechanizm typu callback. Po wystąpieniu o certyfikat dla example.com, serwer Let's Encrypt rozwiązuje nazwę w publicznym DNS, otwiera połączenie na porcie 80 pod znalezionym adresem i żąda http://example.com/.well-known/acme-challenge/<token>. Serwer musi odpowiedzieć dokładnie tym tokenem, który Certbot zapisał na dysku. To jest cały mechanizm. Wynikają z niego trzy konsekwencje, które stanowią większość przyczyn nieudanych prób wystawienia certyfikatu.
- Port 80 musi być dostępny z publicznego internetu, a nie tylko z lokalnej sieci. Reguła
ufw, grupa bezpieczeństwa u dostawcy chmury lub firewall w konsoli VPS, który otwiera tylko port 443, uniemożliwia wystawienie certyfikatu oraz wszystkie przyszłe odnowienia. - DNS musi wskazywać na ten serwer. Serwer walidacyjny wykonuje własne zapytanie DNS z zewnątrz; wpisy
/etc/hostsoraz pamięć podręczna przeglądarki nie mają dla niego znaczenia. - W przypadku publikacji rekordu AAAA, najpierw próbowane jest IPv6. Let's Encrypt ponawia próbę przez IPv4, gdy połączenie IPv6 zostanie całkowicie odrzucone. Jednak nieaktualny rekord AAAA skierowany na hosta, który akceptuje połączenie, ale serwuje inne dane, powoduje błąd krytyczny.
Przekierowania są dozwolone: walidacja kontynuowana jest po przekierowaniu HTTP na HTTPS. Serwer walidacyjny nie sprawdza, czy certyfikat po drugiej stronie jest brakujący, wygasły lub podpisany własnym kluczem. Proces nie może jednak zostać zainicjowany na żadnym porcie innym niż 80. Certbot nie posiada implementacji TLS-ALPN-01, więc użycie portu 443 nie rozwiązuje problemu.
Wybór uwierzytelniacza: --nginx, --webroot, --standalone
--nginx jest zalecanym wyborem domyślnym, gdy nginx jest już uruchomiony i obsługuje daną domenę. Certbot analizuje konfigurację, tworzy tymczasową lokalizację dla wyzwania (challenge), przeładowuje nginx, przeprowadza weryfikację, a następnie zapisuje dyrektywy TLS w bloku serwera. Proces nie powoduje przestojów.
sudo certbot --nginx -d example.com -d www.example.comAutomatyzacja dla nowego systemu:
sudo certbot --nginx \
-d example.com -d www.example.com \
--agree-tos -m ops@example.com --no-eff-email \
--redirect --non-interactive--webroot jest właściwy, gdy nie chce się modyfikować konfiguracji nginx przez Certbot — na przykład gdy konfiguracja jest generowana z szablonu, przechowywana w git lub wdrażana za pomocą Ansible. Certbot zapisuje plik wyzwania wyłącznie w katalogu, który jest już obsługiwany przez serwer.
sudo certbot certonly --webroot -w /var/www/example.com \
-d example.com -d www.example.com \
--deploy-hook "systemctl reload nginx"--standalone jest właściwy, gdy żaden proces nie nasłuchuje na porcie 80: np. serwer poczty, API obsługujące wyłącznie port 443 lub skrypt uruchamiany podczas pierwszego bootowania, przed instalacją nginx. Certbot samodzielnie zajmuje port 80 na kilka sekund. Jeśli nginx jest uruchomiony, operacja zakończy się błędem — należy go zatrzymać przed uruchomieniem procesu:
sudo certbot certonly --standalone -d mail.example.com \
--pre-hook "systemctl stop nginx" \
--post-hook "systemctl start nginx"Powyższe procedury (hooks) są zapisywane w konfiguracji odnawiania certyfikatu, dzięki czemu proces zatrzymywania i uruchamiania usług odbywa się automatycznie podczas odnawiania.
Blok serwera działający przed utworzeniem certyfikatu oraz po jego utworzeniu
Problem zależności: nginx nie uruchamia się, gdy ssl_certificate wskazuje na nieistniejący plik, a Certbot nie może przeprowadzić weryfikacji, gdy nginx jest wyłączony. Najpierw należy uruchomić witrynę na porcie 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;
}
}Należy uruchomić sudo nginx -t && sudo systemctl reload nginx, potwierdzić odpowiedzi curl -I http://example.com/ z zewnątrz, a następnie wygenerować certyfikat. Następnie:
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;
}
}Prefiks ^~ w lokalizacji ACME jest niezbędny: zapobiega on przejęciu żądania wyzwania przez blok return 301. Pozostawienie tej lokalizacji na porcie 80 zapewnia działanie odnawiania certyfikatów po przejściu całej witryny na protokół HTTPS.
Składnia HTTP/2 zależy od wersji nginx; pomylenie obu form powoduje błąd podczas uruchamiania. Ubuntu 24.04 dostarcza nginx 1.24, który wymaga zapisu inline — listen 443 ssl http2;. Debian 13 dostarcza nowszą wersję nginx, która wymaga oddzielnej dyrektywy http2 on;. Najpierw należy sprawdzić nginx -v.
Należy wskazać nginx na live/, a nie na archive/. Symlinki live/ są aktualizowane przy każdym odnowieniu; użycie pełnej ścieżki do archive/ powoduje błąd po wygaśnięciu certyfikatu.
Wildcards oznaczają DNS-01, a DNS-01 oznacza plugin
Certyfikat wildcard (*.example.com) nie może zostać zweryfikowany za pomocą HTTP-01 — nie istnieje pojedyncza nazwa hosta, z której można pobrać plik. Jedyną metodą jest DNS-01: dowód kontroli następuje poprzez opublikowanie rekordu _acme-challenge.example.com TXT. Certbot wymaga poświadczeń API dostawcy DNS w celu automatyzacji tego procesu; służą do tego pluginy dostawców. Pełny przewodnik po certyfikatach wildcard opisuje mechanizm rekordu TXT oraz problem z odnawianiem w trybie manualnym; poniżej znajduje się krótka wersja dla Cloudflare.
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflareW przypadku pakietu apt proces wygląda inaczej (sudo apt install python3-certbot-dns-cloudflare). Poświadczenia należy umieścić w pliku dostępnym tylko dla użytkownika root:
# /root/.secrets/cloudflare.ini
# then: sudo chmod 600 /root/.secrets/cloudflare.ini
dns_cloudflare_api_token = your_scoped_token_hereNależy ograniczyć uprawnienia tokenu wyłącznie do edycji DNS w danej strefie. Jest to klucz do konfiguracji DNS; należy go chronić w taki sam sposób.
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
-d example.com -d '*.example.com'Należy użyć cudzysłowu przy nazwie wildcard, aby powłoka (shell) nie dokonała dopasowania wzorca (globbing). DNS-01 rozwiązuje również problemy, których HTTP-01 nie może rozwiązać: umożliwia wystawienie certyfikatów dla hostów bez otwartego portu 80 — usług wewnętrznych, urządzeń dostępnych wyłącznie przez self-hosted WireGuard VPN na VPS lub paneli administracyjnych na prywatnych interfejsach.
Odnowienie: 90 dni, licznik, deploy hook
Certyfikaty Let's Encrypt są ważne przez 90 dni. Certbot przeprowadza odnowienie, gdy pozostaje mniej niż 30 dni do wygaśnięcia. Zapewnia to 30-dniowe okno czasowe, w którym nieudana próba odnowienia jest problemem do naprawienia, a nie awarią systemu. Let's Encrypt nie wysyła już powiadomień e-mail o wygaśnięciu — monitorowanie stanu certyfikatów leży po stronie użytkownika.
Sprawdź licznik dostarczony w instalacji:
systemctl list-timers 'certbot*' 'snap.certbot*'
sudo certbot certificatescertbot renew sprawdza każdą konfigurację w /etc/letsencrypt/renewal/, pomija elementy spoza 30-dniowego okna i odnawia pozostałe, używając dokładnie tych samych flag, co podczas pierwotnego uruchomienia. Dlatego pierwsze uruchomienie jest kluczowe: to właśnie ono zostaje zapisane.
Sama zmiana pliku na dysku nie powoduje odświeżenia certyfikatu — nginx nadal serwuje stary certyfikat z pamięci RAM, dopóki nie otrzyma polecenia reload. Skonfiguruj 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.shKażdy plik wykonywalny w renewal-hooks/deploy/ zostanie uruchomiony po każdym skutecznym odnowieniu. Flaga --deploy-hook pełni tę samą funkcję dla pojedynczego certyfikatu, zapisując renew_hook = ... w konfiguracji odnowienia. certbot --nginx wykonuje reload automatycznie; konfiguracje --webroot oraz --standalone tego nie robią. Brak hooka powoduje, że strona serwuje wygasły certyfikat, mimo że certbot certificates raportuje poprawną odnowę. Każda inna usługa odczytująca certyfikat podczas startu wymaga tego samego hooka — aplikacja w kontenerze, taka jak Nextcloud VPS install with Docker, TLS and backups, również wymaga tutaj zdefiniowanego kroku restartu lub reloadu.
Testowanie odnowienia w rzeczywistych warunkach
sudo certbot renew --dry-runOperacja ta przeprowadza pełną weryfikację w środowisku stagingowym Let's Encrypt: wykorzystuje tę samą ścieżkę kodu, te same reguły firewall oraz te same ustawienia DNS, nie generuje limitów (rate-limit) i nie zapisuje żadnych danych na dysku. Jeśli test zakończy się sukcesem, automatyczna odnowa za 60 dni również przebiegnie pomyślnie, pod warunkiem braku zmian w konfiguracji systemu.
Tryb dry run nie potwierdza poprawnego wywołania hooka reload — zachowanie to różni się w zależności od wersji Certbot. Należy zweryfikować ten element ręcznie: należy bezpośrednio uruchomić skrypt hooka, potwierdzić sukces operacji systemctl reload nginx oraz sprawdzić sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.
Błędy, na które faktycznie napotkasz
Could not bind to IPv4 or IPv6. — --standalone, ponieważ nginx zajmuje już port 80. Należy użyć --nginx lub --webroot albo zatrzymać proces nginx przed uruchomieniem. Proces zajmujący port można zweryfikować za pomocą sudo ss -lntp | grep ':80'.
Timeout during connect (likely firewall problem) — Let's Encrypt nie może połączyć się z portem 80. Należy sprawdzić następujące elementy: sudo ufw status (otworzyć za pomocą sudo ufw allow 'Nginx Full'), następnie firewall dostawcy VPS, a na końcu DNS. Test należy wykonać spoza serwera: curl -sSv http://example.com/.well-known/acme-challenge/test. Nieaktualny rekord AAAA powoduje ten sam komunikat.
unauthorized :: Invalid response from http://example.com/.well-known/acme-challenge/xyz: 404 — port 80 jest dostępny, ale token nie jest przesyłany. Zapytanie trafiło do innego bloku server (należy sprawdzić, który posiada default_server) lub katalog przekazany do -w nie jest katalogiem obsługiwanym przez nginx. Należy umieścić plik w /var/www/example.com/.well-known/acme-challenge/test i pobrać go zewnętrznie; jeśli wystąpi błąd 404, problemem nie jest certyfikat.
DNS problem: NXDOMAIN looking up A for example.com — nazwa nie jest publicznie rozwiązywana. Przyczyną są nowe rekordy, które nie zostały jeszcze rozpropagowane, lub rekord w strefie, której nie obsługuje rejestrator.
too many certificates already issued for: example.com — limit częstotliwości (rate limit), często napotykany podczas debugowania w pętli. Let's Encrypt ogranicza wystawianie duplikatów certyfikatów (identyczny zestaw nazw) do pięciu sztuk na tydzień; oddzielnie dopuszczalne jest 50 nowych certyfikatów na zarejestrowaną domenę na tydzień. Żadne działanie nie odblokuje limitu, poza upływem czasu. Należy przeprowadzać debugowanie w środowisku staging za pomocą --dry-run.
nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/example.com/fullchain.pem": No such file or directory — nginx jest skonfigurowany dla certyfikatu, który nigdy nie został wystawiony lub został usunięty za pomocą certbot delete. Należy zakomentować blok TLS server, uruchomić nginx, wystawić certyfikat, a następnie przywrócić blok.
open() "/etc/letsencrypt/options-ssl-nginx.conf" failed — plik ten jest dostarczany w pakiecie pluginu nginx. Na systemie certonly bez python3-certbot-nginx należy dodać plugin lub zastąpić linię include własnymi ustawieniami ssl_protocols i ssl_ciphers.
Zarządzanie na dużą skalę
Jeden certyfikat może zawierać do 100 nazw. Stosowanie pojedynczego certbot --nginx -d a.example.com -d b.example.com ... jest ryzykowne — błąd w jednej nieaktualnej rekordzie DNS podczas walidacji powoduje unieważnienie wszystkich pozostałych nazw w tym certyfikacie. Oddzielne certyfikaty dla każdej witryny awarują niezależnie, co jest pożądane na serwerze hostującym wiele usług. Przy dużej liczbie witryn rozwiązaniem jest mechanizm ACME-aware: reverse proxy Traefik działający z wieloma aplikacjami w Docker Compose samodzielnie obsługuje żądania i odnawia certyfikaty, co eliminuje konieczność używania Certbot.
Należy wykonać pełną kopię zapasową /etc/letsencrypt — sudo tar -czf letsencrypt-$(date +%F).tar.gz -C /etc letsencrypt — zachowując linki symboliczne. Ten katalog zawiera accounts/, czyli klucz konta ACME, którego nie można odtworzyć w identycznej formie. Migracja na nowy VPS sprowadza się do: wykonania rsync drzewa katalogów z flagą -a, instalacji Certbot, aktualizacji rekordów DNS oraz uruchomienia certbot renew --dry-run przed przełączeniem ruchu.
Przeinstalowanie systemu lub migracja na nową wersję LTS powoduje utratę harmonogramu odnawiania. Po każdej migracji, przywracaniu migawki (snapshot) lub aktualizacji dystrybucji należy uruchomić systemctl list-timers 'certbot*' oraz --dry-run. Brak tych działań powoduje wygaśnięcie certyfikatu po 89 dniach, co skutkuje niedostępnością witryny.
Powyższe założenia dotyczą maszyny pod pełną kontrolą, z publicznym adresem IP i otwartym portem 80 — czyli serwera VPS. Mechanizm działania jest identyczny dla każdego takiego serwera.
Te same kroki dotyczące certyfikatów stosuje się w Apache zamiast nginx. W przypadku braku możliwości użycia publicznego certyfikatu, certyfikat self-signed na Ubuntu pozwala na obsługę usług wewnętrznych.
FAQ
Czy port 80 musi być otwarty, jeśli strona obsługuje tylko HTTPS?
Tak, dla wyzwania HTTP-01. Let's Encrypt zawsze rozpoczyna żądanie walidacji na porcie 80. Certbot nie posiada implementacji TLS-ALPN-01, więc firewall otwarty wyłącznie na port 443 blokuje zarówno pierwszą certyfikację, jak i każde automatyczne odnowienie. Przekierowanie z portu 80 na HTTPS jest dopuszczalne — walidacja podąża za przekierowaniem. Jedynym sposobem na całkowite pominięcie portu 80 jest metoda DNS-01 przy użyciu wtyczki dostawcy.
apt czy snap — którą wersję Certbot należy zainstalować dla nginx na Ubuntu 24.04?
Należy użyć apt. sudo apt install certbot python3-certbot-nginx dostarcza wersję Certbot 2.9.0 na Ubuntu 24.04, która jest wystarczająco aktualna dla wszystkich instrukcji w tym przewodniku, otrzymuje poprawki bezpieczeństwa przez unattended-upgrades i nie wymaga snapd. Wybierz snap tylko wtedy, gdy wymagana jest najnowsza wersja natychmiast lub gdy wtyczka DNS jest dystrybuowana wyłącznie jako snap. W obu przypadkach należy wybrać tylko jedną metodę: dwie instalacje oznaczają dwa harmonogramy odnawiania wskazujące na to samo drzewo /etc/letsencrypt, co prowadzi do błędów w zapomnianej instalacji.
Czy Certbot może wystawić certyfikat wildcard dla nginx?
Tylko poprzez DNS-01. Certyfikat wildcard typu *.example.com nie posiada pojedynczej nazwy hosta do pobrania pliku wyzwania, więc metody --nginx, --webroot oraz --standalone nie mają zastosowania. Należy zainstalować wtyczkę dla swojego dostawcy DNS, umieścić ograniczony token API w pliku poświadczeń dostępnym tylko dla użytkownika root i uruchomić certbot certonly --dns-cloudflare -d example.com -d '*.example.com', stosując cudzysłów przy nazwie wildcard, aby zapobiec interpretacji przez shell.
Dlaczego nginx nadal serwuje stary certyfikat po pomyślnym odnowieniu?
nginx przechowuje certyfikat w pamięci operacyjnej i nie wykrywa nowego pliku na dysku aż do ponownego załadowania konfiguracji. certbot --nginx wykonuje reload automatycznie, natomiast uruchomienia --webroot i --standalone tego nie robią, przez co odnowienie może zakończyć się sukcesem, podczas gdy przeglądarka nadal wyświetla wygasający certyfikat. Należy umieścić skrypt wykonywalny w /etc/letsencrypt/renewal-hooks/deploy/, który uruchamia nginx -t && systemctl reload nginx, aby proces ten następował po każdym skutecznym odnowieniu.
Czy certbot renew --dry-run potwierdza, że odnowienie zadziała?
W większości przypadków tak. Narzędzie to przeprowadza rzeczywiste wyzwanie w środowisku stagingowym — przy tym samym firewallu, DNS i ścieżce kodu — bez limitów częstotliwości i bez zapisywania danych na dysku, więc sukces oznacza poprawność konfiguracji sieciowej. Nie gwarantuje to jednak poprawności działania hooka wdrożeniowego. Należy to przetestować oddzielnie: uruchomić skrypt hooka ręcznie i sprawdzić sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.