Certbot installeren op Ubuntu 24.04 met nginx
Leer Certbot via apt of snap installeren op Ubuntu 24.04. Voorkom fouten bij de vernieuwing door de juiste methode te kiezen voor uw Let's Encrypt certificaat.
Certbot installeren: apt of snap
Op Ubuntu 24.04 levert sudo apt install certbot python3-certbot-nginx een werkende Certbot die echte, publiekelijk vertrouwde Let's Encrypt-certificaten uitgeeft. De officiële documentatie van Certbot adviseert de snap-versie; het verschil is klein. De snap volgt de nieuwste releases van de ontwikkelaar, terwijl het pakket uit de repository de versie gebruikt die bij de LTS is geleverd en alleen beveiligingsupdates ontvangt.
Kies één methode. Twee versies van Certbot leiden tot twee verschillende vernieuwings-timers die naar dezelfde /etc/letsencrypt-boom verwijzen. De versie die u vergeet, zal voor problemen zorgen.
De apt-methode:
sudo apt update
sudo apt install certbot python3-certbot-nginxHiermee installeert u /usr/bin/certbot, de nginx-plugin, een certbot.service + certbot.timer-paar, en een /etc/cron.d/certbot-entry die geen actie uitvoert onder systemd.
De snap-methode:
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/certbotDe snap bevat een eigen timer, snap.certbot.renew.timer. Verwijder het apt-pakket voordat u de snap installeert.
Beide installaties werken daarna identiek. Certbot 2.x gebruikt standaard ECDSA (P-256) sleutels. Gebruik alleen --key-type rsa voor een client die geen ECDSA ondersteunt. Alle gegevens staan in /etc/letsencrypt: archive/ bevat de echte sleutel- en certificaatbestanden, live/ bevat symlinks naar de huidige bestanden, renewal/ bevat één configuratiebestand per certificaat, en accounts/ bevat uw ACME-accountsleutel.
Wat HTTP-01 precies doet en waarom port 80 niet optioneel is
De HTTP-01 challenge is een callback. U vraagt Let's Encrypt om een certificaat voor example.com; de server lost de naam op in de publieke DNS, maakt een verbinding met port 80 op het gevonden adres en vraagt om http://example.com/.well-known/acme-challenge/<token>. Uw server antwoordt met de exacte token-inhoud die Certbot zojuist naar de schijf heeft geschreven. Dit is het volledige mechanisme. Dit mechanisme heeft drie gevolgen, die de meeste mislukte certificaatuitgiften verklaren.
- Port 80 moet bereikbaar zijn vanaf het publieke internet, niet alleen vanaf uw laptop. Een
ufwregel, een security group van een cloudprovider, of een firewall in een VPS-console die alleen port 443 opent, blokkeert de uitgifte en alle toekomstige vernieuwingen. - DNS moet al naar deze machine wijzen. De validatieserver voert een eigen lookup uit vanaf de buitenwereld; uw
/etc/hostsvermeldingen en browsercache zijn voor de server irrelevant. - Als u een AAAA record publiceert, wordt IPv6 eerst geprobeerd. Let's Encrypt probeert het opnieuw via IPv4 als de IPv6-verbinding volledig mislukt — maar een verouderd AAAA record dat naar een host wijst die de verbinding wel accepteert maar iets anders serveert, leidt tot een harde fout.
Redirects zijn toegestaan: de validatie volgt een HTTP-redirect naar HTTPS. De server geeft het niet om of het certificaat aan de andere kant ontbreekt, verlopen is of zelfondertekend is. De server zal echter nooit ergens anders beginnen dan op port 80. Certbot heeft geen TLS-ALPN-01 implementatie, dus "gebruik gewoon 443" is geen oplossing.
Een authenticator kiezen: --nginx, --webroot, --standalone
--nginx is de juiste standaardoptie als nginx al draait en het domein al serveert. Certbot analyseert uw configuratie, voegt een tijdelijke challenge-locatie toe, herlaadt nginx, valideert de aanvraag en schrijft de TLS-directives in uw server block. Geen downtime.
sudo certbot --nginx -d example.com -d www.example.comScriptmatig, voor een nieuwe installatie:
sudo certbot --nginx \
-d example.com -d www.example.com \
--agree-tos -m ops@example.com --no-eff-email \
--redirect --non-interactive--webroot is de juiste keuze als u Certbot niet in uw nginx-configuratie wilt gebruiken — bijvoorbeeld wanneer u een configuratie genereert vanuit een template, deze in git bewaart of via Ansible pusht. Certbot schrijft alleen het challenge-bestand naar een map die u al serveert.
sudo certbot certonly --webroot -w /var/www/example.com \
-d example.com -d www.example.com \
--deploy-hook "systemctl reload nginx"--standalone is de juiste keuze wanneer er niets luistert op port 80: een mailserver, een API die alleen via 443 communiceert, of een script dat bij de eerste boottijd wordt uitgevoerd voordat nginx bestaat. Certbot koppelt port 80 zelf gedurende enkele seconden. Als nginx wel draait, mislukt dit — stop de service rondom de uitvoering:
sudo certbot certonly --standalone -d mail.example.com \
--pre-hook "systemctl stop nginx" \
--post-hook "systemctl start nginx"Deze hooks worden geregistreerd in de renewal-configuratie van het certificaat. Hierdoor vindt dezelfde stop/start procedure automatisch plaats bij verlenging.
Een server block dat werkt voor en na de aanwezigheid van het certificaat
Het kip-of-het-ei-probleem: nginx weigert te starten als ssl_certificate naar een bestand verwijst dat niet bestaat, en Certbot kan de validatie niet uitvoeren terwijl nginx uit staat. Start de website eerst op port 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;
}
}Voer sudo nginx -t && sudo systemctl reload nginx uit, bevestig de curl -I http://example.com/ antwoorden vanaf een externe machine, en voer daarna de aanvraag uit. Daarna:
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;
}
}De ^~ prefix in de ACME location is essentieel: het voorkomt dat de return 301 block de challenge-aanvraag onderschept. Door deze location op port 80 te laten staan, blijven vernieuwingen werken nadat de rest van de website alleen via HTTPS toegankelijk is.
De HTTP/2 syntax hangt af van uw nginx versie; het mengen van beide vormen veroorzaakt een fout bij het opstarten. Ubuntu 24.04 gebruikt nginx 1.24, wat inline syntax vereist — listen 443 ssl http2;. Debian 13 gebruikt een nieuwere nginx, die de aparte http2 on; directive vereist. Controleer eerst nginx -v.
Verwijs nginx naar live/, nooit naar archive/. De live/ symlinks worden bij elke vernieuwing bijgewerkt; een harde padverwijzing naar archive/ zorgt ervoor dat u vastzit aan een certificaat dat verloopt.
Wildcards betekenen DNS-01, en DNS-01 betekent een plugin
Een wildcard-certificaat (*.example.com) kan niet worden gevalideerd via HTTP-01 — er is geen enkele hostname om een bestand op te halen. DNS-01 is de enige methode: u bewijst de controle door een _acme-challenge.example.com TXT-record te publiceren. Certbot heeft API-gegevens van uw DNS-provider nodig om dit onbeheerd uit te voeren; dit is de reden voor de bestaande provider-plugins. De volledige handleiding voor wildcard-certificaten behandelt de werking van het TXT-record en de problemen bij verlenging in manual mode; de korte Cloudflare-versie volgt daarna.
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflareOp het apt-pad is dit sudo apt install python3-certbot-dns-cloudflare. De gegevens worden opgeslagen in een bestand dat alleen voor root toegankelijk is:
# /root/.secrets/cloudflare.ini
# then: sudo chmod 600 /root/.secrets/cloudflare.ini
dns_cloudflare_api_token = your_scoped_token_hereBeperk de token tot DNS-edit rechten voor alleen die specifieke zone. Het is een sleutel tot uw DNS; behandel het als zodanig.
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
-d example.com -d '*.example.com'Gebruik aanhalingstekens voor de wildcard zodat uw shell deze niet interpreteert als een glob. DNS-01 lost ook op wat HTTP-01 niet kan: certificaten voor hosts zonder publieke poort 80 — een interne service, een machine die alleen bereikbaar is via een self-hosted WireGuard VPN op een VPS, of een admin-paneel op een privé-interface.
Verlenging: de 90 dagen, de timer, de deploy hook
Let's Encrypt-certificaten zijn 90 dagen geldig. Certbot voert een verlenging uit wanneer er minder dan 30 dagen over zijn. Dit geeft u een venster van 30 dagen waarin een mislukte verlenging een oplosbare fout is in plaats van een downtime. Let's Encrypt stuurt geen waarschuwingsmails meer over de vervaldatum. U bent nu zelf verantwoordelijk voor de monitoring.
Controleer de timer van uw installatie:
systemctl list-timers 'certbot*' 'snap.certbot*'
sudo certbot certificatescertbot renew controleert elke configuratie in /etc/letsencrypt/renewal/, slaat alles buiten het venster van 30 dagen over, en verlengt de rest met exact dezelfde flags als de oorspronkelijke run. Daarom is de eerste run belangrijk: dit is de run die wordt geregistreerd.
Het verlengen van het bestand op de schijf verandert op zichzelf niets — nginx blijft het oude certificaat uit het geheugen serveren totdat de service wordt herladen. Configureer eenmaal een 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.shAlles wat uitvoerbaar is in renewal-hooks/deploy/ wordt uitgevoerd na een succesvolle verlenging. De --deploy-hook flag doet hetzelfde voor één certificaat door renew_hook = ... op te slaan in de renewal config. certbot --nginx voert een reload voor u uit; de --webroot en --standalone setups doen dit niet. Een ontbrekende hook is de reden waarom een website een verlopen certificaat serveert terwijl certbot certificates een nieuw certificaat rapporteert. Elke andere applicatie die het certificaat bij het opstarten uitleest, vereist dezelfde hook — een gecontaineriseerde applicatie zoals een Nextcloud VPS installatie met Docker, TLS en backups heeft hier ook een eigen restart of reload stap nodig.
Test de verlenging in de praktijk
sudo certbot renew --dry-runHiermee wordt de volledige challenge uitgevoerd tegen de staging-omgeving van Let's Encrypt: dezelfde code-paden, firewall en DNS, maar zonder limieten voor het aantal aanvragen en zonder gegevens naar de schijf te schrijven. Als dit vandaag slaagt, zal de automatische verlenging over 60 dagen ook slagen, mits de configuratie van het systeem niet verandert.
Een dry run bewijst niet of de reload hook wordt uitgevoerd; dit gedrag verschilt per Certbot-versie. Test dit onderdeel handmatig: voer het hook-script direct uit, controleer of systemctl reload nginx slaagt en controleer sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.
De fouten waar u daadwerkelijk tegenaan loopt
Could not bind to IPv4 or IPv6. — --standalone omdat nginx al poort 80 gebruikt. Gebruik --nginx of --webroot, of stop nginx voor de uitvoering. Controleer de gebruiker met sudo ss -lntp | grep ':80'.
Timeout during connect (likely firewall problem) — Let's Encrypt kan poort 80 niet bereiken. Controleer de stappen: sudo ufw status (open deze met sudo ufw allow 'Nginx Full'), de firewall van de VPS-provider, en vervolgens DNS. Test vanaf een locatie buiten uw server: curl -sSv http://example.com/.well-known/acme-challenge/test. Een verouderde AAAA-record veroorzaakt dezelfde melding.
unauthorized :: Invalid response from http://example.com/.well-known/acme-challenge/xyz: 404 — poort 80 is bereikbaar, maar de token wordt niet geserveerd. De aanvraag komt terecht in een andere server block (controleer welke default_server bezit), of de directory die aan -w is doorgegeven is niet de directory die nginx serveert. Plaats een bestand in /var/www/example.com/.well-known/acme-challenge/test en haal dit extern op; als dit een 404-fout geeft, was het certificaat niet het probleem.
**DNS problem: NXDOMAIN looking up A for example.com — de naam wordt niet publiekelijk geresolved. Dit komt door nieuwe records die nog niet zijn gepropageerd, of een record in een zone die uw registrar niet serveert.
too many certificates already issued for: example.com — een rate limit, vaak veroorzaakt door herhaaldelijk debuggen in een loop. Let's Encrypt beperkt duplicaten van certificaten — exact dezelfde set namen — tot vijf per week, en staat daarnaast 50 nieuwe certificaten per geregistreerd domein per week toe; niets heft dit op behalve tijd. Debug tegen staging met --dry-run.
nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/example.com/fullchain.pem": No such file or directory — nginx is geconfigureerd voor een certificaat dat nooit is uitgegeven, of een certificaat dat is verwijderd met certbot delete. Commenteer de TLS server block uit, start nginx, vraag het certificaat aan, en herstel de block.
open() "/etc/letsencrypt/options-ssl-nginx.conf" failed — dit bestand wordt geleverd met het nginx plugin package. Voeg op een certonly systeem zonder python3-certbot-nginx de plugin toe, of vervang de include regel door uw eigen ssl_protocols en ssl_ciphers instellingen.
Beheer op schaal
Eén certificaat kan maximaal 100 namen bevatten. Het gebruik van één enkele certbot --nginx -d a.example.com -d b.example.com ... is verleidelijk, totdat een verouderde DNS-record de validatie doet mislukken. Hierdoor vervalt direct elke andere naam op dat certificaat. Losse certificaten per site falen onafhankelijk van elkaar. Dit is de gewenste werking voor een server die meer dan enkele zaken host. Bij meer dan een handvol sites is een ACME-bewuste front door de moeite waard: een Traefik reverse proxy die meerdere apps onder Docker Compose draait regelt aanvragen en vernieuwingen zelfstandig. Certbot is dan niet langer nodig.
Maak een volledige back-up van /etc/letsencrypt — sudo tar -czf letsencrypt-$(date +%F).tar.gz -C /etc letsencrypt — inclusief de symlinks. Deze boom bevat accounts/, uw ACME-accountkey. Deze key kan niet identiek worden geregenereerd. Een migratie naar een nieuwe VPS bestaat dan uit: de boom rsyncen met -a, Certbot installeren, DNS wijzigen en certbot renew --dry-run uitvoeren voordat u de overstap maakt.
De vernieuwingstimer volgt u niet bij een nieuwe installatie of een overstap naar een nieuwe LTS. Voer na elke migratie, snapshot-herstel of distributie-upgrade systemctl list-timers 'certbot*' en één --dry-run uit. Als u dit overslaat, kan een site 89 dagen later om 03:00 uur offline gaan, omdat men ervan uitging dat het certificaat zichzelf zou vernieuwen.
Dit alles gaat uit van een machine die u beheert, met een publiek IP-adres en poort 80 die openstaat voor de wereld — met andere woorden: een VPS. De bovenstaande werking is identiek op elke dergelijke machine.
Dezelfde stappen voor certificaten zijn van toepassing op Apache in plaats van nginx. Wanneer een publiek certificaat geen optie is, biedt een zelfondertekend certificaat op Ubuntu een oplossing voor interne services.
FAQ
Moet poort 80 open staan als mijn site alleen HTTPS gebruikt?
Ja, voor de HTTP-01 challenge. Let's Encrypt start de validatieaanvraag altijd op poort 80. Certbot heeft geen TLS-ALPN-01 implementatie. Een firewall die alleen poort 443 opent, blokkeert zowel de eerste uitgifte als alle automatische verlengingen daarna. Een redirect van poort 80 naar HTTPS is toegestaan; de validatie volgt de redirect. De enige manier om poort 80 volledig over te slaan is via DNS-01 met een provider plugin.
apt of snap — welke Certbot moet ik installeren voor nginx op Ubuntu 24.04?
Gebruik apt. sudo apt install certbot python3-certbot-nginx levert Certbot 2.9.0 op Ubuntu 24.04. Dit is actueel genoeg voor alle onderdelen in deze handleiding. Het ontvangt security patches via unattended-upgrades en vereist geen snapd. Kies alleen voor snap als u direct de nieuwste release nodig heeft of een DNS plugin die uitsluitend als snap wordt verspreid. Kies in ieder geval precies één methode: twee installaties betekenen twee renewal timers die naar dezelfde /etc/letsencrypt tree wijzen, wat leidt tot fouten bij de vergeten installatie.
Kan Certbot een wildcard certificaat uitgeven voor nginx?
Alleen via DNS-01. Een wildcard zoals *.example.com heeft geen enkele hostname om een challenge file op te halen. Hierdoor zijn --nginx, --webroot en --standalone niet mogelijk. Installeer de plugin voor uw DNS provider, plaats een scoped API token in een credentials file met root-rechten, en voer certbot certonly --dns-cloudflare -d example.com -d '*.example.com' uit. Gebruik aanhalingstekens rond de wildcard om shell globbing te voorkomen.
Waarom serveert nginx nog steeds het oude certificaat na een succesvolle verlenging?
nginx houdt het certificaat in het geheugen. Het merkt het nieuwe bestand op de schijf pas op na een reload. certbot --nginx voert een reload voor u uit, maar --webroot en --standalone doen dat niet. Een verlenging kan dus slagen terwijl de browser nog steeds een verlopend certificaat ziet. Plaats een executable script in /etc/letsencrypt/renewal-hooks/deploy/ dat nginx -t && systemctl reload nginx uitvoert; dit script wordt na elke succesvolle verlenging uitgevoerd.
Bewijst certbot renew --dry-run dat de verlenging zal werken?
Grotendeels. Het voert de echte challenge uit tegen de staging omgeving — dezelfde firewall, hetzelfde DNS, hetzelfde codepad — zonder kosten voor rate-limits en zonder bestanden op de schijf te schrijven. Een succesvolle test betekent dat het netwerkgedeelte correct is. Het bewijst echter niet betrouwbaar of uw deploy hook wordt uitgevoerd. Test dit apart: voer het hook script handmatig uit en controleer sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.