SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-28

Certbot installeren op Ubuntu 24.04 voor Nginx

Installeer Certbot via apt of snap op Ubuntu 24.04. Gebruik sudo apt install certbot python3-certbot-nginx en certbot --nginx. Voorkom timeoutfouten en dubbele timers.

Certbot installeren: apt of snap

Op Ubuntu 24.04 levert sudo apt install certbot python3-certbot-nginx een werkende Certbot die echte, publiek vertrouwde Let's Encrypt-certificaten uitgeeft. De upstream-documentatie van Certbot verwijst u naar de snap; het verschil is klein. De snap volgt de upstream-releases, terwijl het archiefpakket de versie volgt die bij de LTS is geleverd en beveiligingsupdates ontvangt.

Kies er één. Twee kopieën van Certbot betekenen twee vernieuwingstimers die op dezelfde /etc/letsencrypt-boom zijn gericht, en de versie die u vergeet, is degene die voor verrassingen zorgt.

Het apt-pad:

sudo apt update
sudo apt install certbot python3-certbot-nginx

Dit installeert /usr/bin/certbot, de nginx-plugin, een certbot.service + certbot.timer-paar en een /etc/cron.d/certbot-item dat onder systemd niets uitvoert.

Het snap-pad:

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

De snap levert zijn eigen timer, snap.certbot.renew.timer. Verwijder het apt-pakket voordat u de snap installeert.

Beide installaties gedragen zich daarna hetzelfde. Certbot 2.x gebruikt standaard ECDSA (P-256)-sleutels; geef alleen --key-type rsa door voor een client die geen ECDSA ondersteunt. Alle statusinformatie bevindt zich onder /etc/letsencrypt: archive/ bevat de echte sleutel- en certificaatbestanden, live/ bevat symbolische koppelingen naar de huidige bestanden, renewal/ bevat één configuratiebestand per certificaat en accounts/ bevat uw ACME-accountsleutel.

Wat HTTP-01 feitelijk doet en waarom poort 80 niet optioneel is

De HTTP-01 challenge is een callback. U vraagt Let's Encrypt om een certificaat voor example.com; zij lossen de naam op in de publieke DNS, openen een verbinding met poort 80 op het gevonden adres en vragen 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. Dat is het volledige mechanisme. Hieruit vloeien drie consequenties voort, die de oorzaak zijn van de meeste mislukte uitgiften.

  • Poort 80 moet bereikbaar zijn vanaf het publieke internet, niet enkel vanaf uw laptop. Een ufw-regel, een security group van een cloudprovider of een firewall in een VPS-console die alleen poort 443 openzet, blokkeert de uitgifte en elke toekomstige vernieuwing.
  • DNS moet al naar deze server wijzen. De validatieserver voert zijn eigen opzoeking uit vanaf het internet; uw /etc/hosts-vermeldingen en browsercache betekenen niets voor hen.
  • Als u een AAAA-record publiceert, wordt IPv6 als eerste geprobeerd. Let's Encrypt probeert het opnieuw via IPv4 wanneer de IPv6-verbinding volledig faalt, maar een verouderd AAAA-record dat naar een host wijst die de verbinding accepteert en iets anders serveert, leidt tot een harde fout.

Redirects zijn toegestaan: validatie volgt een HTTP-redirect naar HTTPS en het maakt niet uit of het certificaat aan de andere kant ontbreekt, verlopen is of self-signed is. Wat zij echter niet doen, is ergens anders beginnen dan op poort 80. Certbot heeft geen TLS-ALPN-01-implementatie, dus "gebruik gewoon 443" is geen uitweg.

Een authenticator kiezen: --nginx, --webroot, --standalone

--nginx is de juiste standaardkeuze wanneer Nginx al draait en het domein reeds bedient. Certbot analyseert uw configuratie, voegt tijdelijk een challenge-locatie toe, herlaadt Nginx, valideert en schrijft vervolgens de TLS-richtlijnen naar uw server block. Er is geen downtime.

sudo certbot --nginx -d example.com -d www.example.com

Gescript, voor een nieuwe server:

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 wanneer u niet wilt dat Certbot uw Nginx-configuratie aanpast; bijvoorbeeld wanneer u deze vanuit een template genereert, in git beheert of pusht met Ansible. Certbot schrijft enkel 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 op poort 80 luistert: een mailserver, een API die enkel via 443 communiceert, of een first-boot script dat draait voordat Nginx bestaat. Certbot bindt poort 80 zelf voor enkele seconden. Als Nginx wel draait, mislukt dit; stop Nginx daarom rondom de uitvoering:

sudo certbot certonly --standalone -d mail.example.com \
  --pre-hook "systemctl stop nginx" \
  --post-hook "systemctl start nginx"

Deze hooks worden vastgelegd in de vernieuwingsconfiguratie van het certificaat, zodat hetzelfde stop/start-proces automatisch plaatsvindt bij vernieuwing.

Een serverblok dat werkt voor en na het bestaan van het certificaat

Het kip-en-ei-probleem: nginx weigert te starten als ssl_certificate verwijst naar een bestand dat niet bestaat, en Certbot kan niet valideren terwijl nginx offline is. Breng de site eerst online op poort 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 dat curl -I http://example.com/ antwoord geeft van buiten de server, en vraag daarna het certificaat aan. Vervolgens:

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;
    }
}

Het ^~-voorvoegsel op de ACME-locatie bewijst zijn nut: het voorkomt dat het return 301-blok de challenge-aanvraag onderschept. Door die locatie op poort 80 te behouden, blijven vernieuwingen werken nadat de rest van de site is overgezet naar HTTPS-only.

Beide bovenstaande blokken serveren bestanden vanaf de schijf; als nginx in plaats daarvan fungeert als proxy voor een applicatie, wordt location / een proxy_pass-blok en behandelt het reverse proxy serverblok, regel voor regel de headers die de applicatie nodig heeft, terwijl de ACME-locatie en de TLS-richtlijnen ongewijzigd blijven.

De HTTP/2-syntaxis hangt af van uw nginx-versie; het mengen van beide vormen leidt tot een opstartfout. Ubuntu 24.04 levert nginx 1.24, waarbij dit inline moet, listen 443 ssl http2;. Debian 13 levert een nieuwere nginx, die de afzonderlijke http2 on;-richtlijn vereist. Controleer eerst nginx -v.

Laat nginx verwijzen naar live/, nooit naar archive/. De live/-symlinks worden bij elke vernieuwing opnieuw ingesteld; een hard pad naar archive/ zorgt ervoor dat u vastzit aan een certificaat dat verloopt terwijl u het gebruikt.

Wildcards betekenen DNS-01, en DNS-01 betekent een plugin

Een wildcard-certificaat (*.example.com) kan niet worden gevalideerd via HTTP-01; er is immers geen enkele hostnaam waarvan een bestand kan worden opgehaald. DNS-01 is de enige route: u bewijst het beheer door een _acme-challenge.example.com TXT-record te publiceren. Certbot heeft API-inloggegevens van uw DNS-provider nodig om dit onbeheerd uit te voeren; daarvoor zijn de provider-plugins bedoeld. De volledige handleiding voor wildcard-certificaten behandelt de werking van het TXT-record en de valkuil bij vernieuwing in de handmatige modus; hieronder volgt de korte versie voor Cloudflare.

sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflare

Op het apt-pad is dat in plaats daarvan sudo apt install python3-certbot-dns-cloudflare. Inloggegevens plaatst u in een bestand dat alleen toegankelijk is voor root:

# /root/.secrets/cloudflare.ini
# then: sudo chmod 600 /root/.secrets/cloudflare.ini
dns_cloudflare_api_token = your_scoped_token_here

Beperk de scope van het token tot DNS-bewerkingsrechten voor die specifieke zone. Het is een sleutel tot uw DNS; ga er ook zo mee om.

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
  -d example.com -d '*.example.com'

Plaats aanhalingstekens rondom de wildcard zodat uw shell deze niet als glob interpreteert. 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 zelfgehoste WireGuard VPN op een VPS, of een beheerpaneel op een privé-interface.

Vernieuwing: de 90 dagen, de timer, de deploy-hook

Let's Encrypt-certificaten zijn 90 dagen geldig. Certbot voert een vernieuwing uit wanneer er minder dan 30 dagen resterend zijn. Dit geeft u een venster van 30 dagen waarin een mislukte vernieuwing een oplosbaar ongemak is in plaats van een storing. Let's Encrypt verstuurt geen e-mails meer met waarschuwingen over het verlopen van certificaten; niemand zal u hierop attenderen, dus het monitoren is nu uw verantwoordelijkheid.

Controleer de timer die bij uw installatie is meegeleverd:

systemctl list-timers 'certbot*' 'snap.certbot*'
sudo certbot certificates

certbot renew doorloopt elke configuratie in /etc/letsencrypt/renewal/, slaat alles buiten het venster van 30 dagen over en vernieuwt de rest met exact dezelfde flags als de oorspronkelijke uitvoering. Daarom is de eerste uitvoering van belang: deze wordt vastgelegd.

Het vernieuwen van het bestand op de schijf verandert op zichzelf niets; nginx blijft het oude certificaat vanuit het geheugen serveren totdat het een signaal krijgt om opnieuw te laden. Stel eenmalig een deploy-hook in:

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

Alles wat uitvoerbaar is in renewal-hooks/deploy/ wordt uitgevoerd na elke succesvolle vernieuwing. De flag --deploy-hook doet hetzelfde voor één certificaat en slaat renew_hook = ... op in de bijbehorende vernieuwingsconfiguratie. certbot --nginx voert het herladen voor u uit; --webroot- en --standalone-opstellingen doen dit niet. Een ontbrekende hook is precies de reden waarom een site een verlopen certificaat serveert terwijl certbot certificates vrolijk een vers certificaat rapporteert. Elke andere service die het certificaat bij het opstarten inleest, vereist dezelfde hook. Een gecontaineriseerde applicatie, zoals een Nextcloud VPS-installatie met Docker, TLS en back-ups, heeft hier ook een eigen herstart- of herlaadstap nodig.

Testen van vernieuwing in de praktijk

sudo certbot renew --dry-run

Dit voert de volledige challenge uit tegen de staging-omgeving van Let's Encrypt: hetzelfde codepad, dezelfde firewall, dezelfde DNS, geen risico op rate-limits en er wordt niets naar de schijf geschreven. Als dit vandaag slaagt, slaagt de automatische vernieuwing over 60 dagen ook, mits de configuratie van de server ongewijzigd blijft.

Een dry run bewijst niet dat uw reload-hook wordt geactiveerd; het gedrag hiervan verschilt per Certbot-versie. Test dit deel handmatig: voer het hook-script direct uit, bevestig dat systemctl reload nginx slaagt en controleer sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.

De fouten die u daadwerkelijk zult tegenkomen

Could not bind to IPv4 or IPv6., --standalone terwijl nginx al poort 80 bezet houdt. Gebruik --nginx of --webroot, of stop nginx tijdens het uitvoeren. Bevestig de proceseigenaar met sudo ss -lntp | grep ':80'.

Timeout during connect (likely firewall problem), Let's Encrypt kon poort 80 niet bereiken. Controleer van buiten naar binnen: sudo ufw status (open deze met sudo ufw allow 'Nginx Full'), vervolgens de firewall van de VPS-provider zelf, en daarna DNS. Test vanaf een locatie die niet uw server is: curl -sSv http://example.com/.well-known/acme-challenge/test. Een verouderd AAAA-record veroorzaakt dezelfde melding.

unauthorized :: Invalid response from http://example.com/.well-known/acme-challenge/xyz: 404, poort 80 is bereikbaar, maar het token wordt niet geserveerd. Het verzoek kwam terecht in een ander server block (controleer welke eigenaar is van default_server), of de map die is doorgegeven aan -w is niet de map die nginx serveert. Plaats een bestand op /var/www/example.com/.well-known/acme-challenge/test en haal dit extern op; als dit een 404-fout geeft, was het certificaat nooit het probleem.

DNS problem: NXDOMAIN looking up A for example.com, de naam wordt niet publiek opgelost. Dit zijn nieuwe records die nog niet zijn verspreid, of een record in een zone die uw registrar niet serveert.

too many certificates already issued for: example.com, een rate limit, en de fout die mensen tegenkomen tijdens het debuggen in een lus. Let's Encrypt limiteert dubbele certificaten, met exact dezelfde set namen, tot vijf per week, en staat daarnaast 50 nieuwe certificaten per geregistreerd domein per week toe; niets heft deze blokkade op behalve tijd. Debug tegen de staging-omgeving 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. Zet het TLS server block in commentaar, start nginx, vraag het certificaat aan en herstel het block.

open() "/etc/letsencrypt/options-ssl-nginx.conf" failed, dat bestand wordt meegeleverd met het nginx-pluginpakket. Op een certonly-systeem zonder python3-certbot-nginx, voegt u de plugin toe of vervangt u de include-regel door uw eigen ssl_protocols- en ssl_ciphers-instellingen.

Beheer op schaal

Eén certificaat kan tot 100 namen bevatten en een enkel certbot --nginx -d a.example.com -d b.example.com ... is verleidelijk, totdat één verouderd DNS-record de validatie blokkeert en daarmee alle andere namen op dat certificaat onbereikbaar maakt. Aparte certificaten per site falen onafhankelijk van elkaar; dit is de gewenste situatie voor een server die meer dan een paar applicaties host. Bij een groter aantal sites verdient een ACME-bewuste reverse proxy zichzelf terug: een Traefik reverse proxy die meerdere apps onder Docker Compose draait vraagt de certificaten zelf aan en vernieuwt deze, waardoor Certbot volledig overbodig wordt. Welke proxy u kiest voor deze taak is een eigen afweging, en het vergelijken van Nginx met Caddy en Traefik hangt grotendeels af van hoeveel configuratiewerk voor certificaten en applicaties u door de proxy wilt laten afhandelen.

Maak een volledige back-up van /etc/letsencrypt, inclusief sudo tar -czf letsencrypt-$(date +%F).tar.gz -C /etc letsencrypt, met behoud van symlinks. Deze map bevat accounts/, uw ACME-account-key, die u niet identiek kunt regenereren. Verhuizen naar een nieuwe VPS komt dan neer op: rsync de map met -a, installeer Certbot, wijzig de DNS-verwijzingen en voer certbot renew --dry-run uit voordat u overschakelt.

Bij het opnieuw opbouwen van de server of een overstap naar een nieuwe LTS-versie wordt de timer voor vernieuwing niet automatisch meegekopieerd. Voer na elke migratie, het terugzetten van een snapshot of een distributie-upgrade systemctl list-timers 'certbot*' en één --dry-run uit. Als u dit overslaat, gaat een site 89 dagen later op zwart, om 3 uur 's nachts, met een certificaat waarvan iedereen aannam dat het zichzelf vernieuwde.

Dit alles gaat uit van een machine die u zelf beheert, met een publiek IP-adres en poort 80 open naar het internet; oftewel een VPS. De werking is op al deze systemen identiek.

Dezelfde stappen voor certificaten zijn van toepassing op Apache in plaats van nginx, en wanneer een publiek certificaat geen optie is, biedt een self-signed certificaat op Ubuntu uitkomst voor interne services.

FAQ

Moet ik poort 80 openstellen als mijn site alleen HTTPS aanbiedt?

Ja, voor de HTTP-01 challenge. Let's Encrypt start het validatieverzoek altijd op poort 80 en Certbot heeft geen TLS-ALPN-01 implementatie. Een firewall die alleen poort 443 openstelt, blokkeert daarom zowel de eerste uitgifte als elke daaropvolgende automatische vernieuwing. Een redirect van poort 80 naar HTTPS is toegestaan; de validatie volgt deze redirect. De enige manier om poort 80 volledig te omzeilen is DNS-01 in combinatie met een provider-plugin.

apt of snap, welke Certbot-versie 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, wat actueel genoeg is voor alles in deze handleiding. U ontvangt beveiligingsupdates via unattended-upgrades en u heeft geen snapd nodig. Kies alleen voor de snap als u direct de nieuwste release nodig heeft of een DNS-plugin die uitsluitend als snap wordt gedistribueerd. Kies in ieder geval voor slechts één methode: twee installaties betekenen twee vernieuwingstaken die naar dezelfde /etc/letsencrypt-structuur wijzen, en de vergeten taak zal uiteindelijk voor problemen zorgen.

Kan Certbot een wildcard-certificaat uitgeven voor nginx?

Alleen via DNS-01. Een wildcard zoals *.example.com heeft geen specifieke hostnaam om een challenge-bestand op te halen, dus --nginx, --webroot en --standalone zijn niet mogelijk. Installeer de plugin voor uw DNS-provider, plaats een API-token met beperkte rechten in een bestand dat alleen leesbaar is voor root, en voer certbot certonly --dns-cloudflare -d example.com -d '*.example.com' uit. Plaats het wildcard-domein tussen aanhalingstekens om te voorkomen dat de shell het domein als een glob-patroon interpreteert.

Waarom serveert nginx nog steeds het oude certificaat na een geslaagde vernieuwing?

nginx houdt het certificaat in het geheugen en merkt het nieuwe bestand op de schijf pas op na een herlaadactie. certbot --nginx voert deze herlaadactie voor u uit, maar --webroot en --standalone doen dit niet. Hierdoor kan een vernieuwing slagen terwijl de browser nog steeds een verlopend certificaat toont. Plaats een uitvoerbaar script in /etc/letsencrypt/renewal-hooks/deploy/ dat nginx -t && systemctl reload nginx uitvoert; dit script wordt na elke geslaagde vernieuwing aangeroepen.

Bewijst certbot renew --dry-run dat vernieuwing zal werken?

Grotendeels. Het voert de daadwerkelijke challenge uit tegen de staging-omgeving, met dezelfde firewall, dezelfde DNS en hetzelfde codepad, zonder dat dit meetelt voor de rate-limits en zonder dat er bestanden naar de schijf worden geschreven. Een geslaagde test betekent dus dat het netwerkgedeelte correct is geconfigureerd. Het bewijst echter niet betrouwbaar of uw deploy-hook wordt uitgevoerd. Test dit afzonderlijk: voer het hook-script handmatig uit en controleer sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.