Naar de inhoud
Actuele kortingscode:10% kortingGebruiken bij je bestelling

Website op een VPS: Nginx en een Let's Encrypt-certificaat (HTTPS)

Je installeert Nginx, zet een statische website of een reverse proxy voor een applicatie (Node.js en andere) op en vraagt een gratis Let's Encrypt-certificaat aan dat automatisch wordt verlengd.

  • gevorderd
  • ~25 min
  • Bijgewerkt op 2-10-2026

Nginx is een snelle webserver. We zetten er een statische website mee op of plaatsen hem als reverse proxy vóór je applicatie, en schakelen HTTPS in met een Let's Encrypt-certificaat dat zichzelf verlengt.

Wat je nodig hebt

  • Een VPS bij StormHost met Ubuntu 24.04 of Debian 13
  • Een domein dat naar het IP-adres van de VPS verwijst (een A-record, eventueel AAAA), de stappen: Een domein op een VPS en Cloudflare
  • Een SSH-verbinding als root (handleiding)
  • Ongeveer 25 minuten

Hoe het werkt#

De browser maakt verbinding met poort 80 (HTTP) of 443 (HTTPS) van je VPS. Nginx kiest op basis van het domein in het verzoek het juiste server block (de configuratie van één website) en stuurt óf een bestand van de schijf (statische website), óf geeft het verzoek door aan een applicatie die op een andere poort draait (reverse proxy).

Het certificaat voor HTTPS geeft Let's Encrypt gratis uit. Het programma Certbot controleert of je het domein beheert (Let's Encrypt maakt verbinding met poort 80 en downloadt een controlebestand), haalt het certificaat op, stelt het in Nginx in en verlengt het daarna zelf.

Voorwaarden: DNS en poorten#

  1. Controleer of het domein naar de VPS verwijst. Voer op de VPS uit:

    bash
    getent hosts example.cz

    Je ziet het IP-adres van je VPS (van het tabblad Netwerk). Zo niet, pas dan de DNS aan en wacht tot de wijziging is doorgevoerd.

  2. Open de poorten 80 en 443:

    • Firewall in je klantaccount (als je de modus Alles blokkeren behalve wat is toegestaan gebruikt): regel Inkomend · Toestaan · TCP · poort 80,443. Handleiding: Firewall in je klantaccount.
    • UFW op de VPS (als je die gebruikt): ufw allow 80/tcp en ufw allow 443/tcp.

Cloudflare: vraag het certificaat aan met het grijze wolkje

Beheer je het domein in Cloudflare, laat de records dan tijdens het aanvragen van het certificaat op DNS only staan (grijs wolkje). Zodra je het certificaat hebt, kun je overschakelen naar Proxied, zie Cloudflare en SSL-modi.

Nginx installeren#

Geldt voor zowel Ubuntu 24.04 als Debian 13:

bash
apt update
apt -y install nginx
  • apt -y install nginx – installeert Nginx. De service start meteen en start voortaan automatisch mee met de VPS.

Controle: open in je browser http://<VPS_IP_ADDRESS>. Vervang <VPS_IP_ADDRESS> door het IPv4-⁠adres van het tabblad Netwerk (bijvoorbeeld http://203.0.113.10). Je ziet de welkomstpagina “Welcome to nginx!”.

Hoe de configuratie is ingedeeld#

PadWaar het voor dient
/etc/nginx/nginx.confDe hoofdconfiguratie. Die pas je meestal niet aan. Ze laadt de bestanden uit de mappen hieronder.
/etc/nginx/sites-available/De configuraties van de afzonderlijke websites (server blocks). Een bestand kan hier staan zonder actief te zijn.
/etc/nginx/sites-enabled/Koppelingen (symlinks) naar bestanden uit sites-available. Nginx laadt alleen wat hier staat.
/etc/nginx/conf.d/Overige configuratie, alle bestanden *.conf worden geladen.
/var/www/De gebruikelijke plek voor de bestanden van websites.
/var/log/nginx/Logs: access.log (verzoeken) en error.log (fouten).

Je schakelt een website dus in door een bestand aan te maken in sites-available en een koppeling ernaar in sites-enabled. Je schakelt hem uit door de koppeling te verwijderen.

Statische website#

Een statische website bestaat uit kant-en-klare bestanden: HTML, CSS, JavaScript en afbeeldingen.

  1. Maak de map van de website en een testpagina aan:

    bash
    mkdir -p /var/www/example.cz/html
    echo '<h1>Het werkt</h1>' > /var/www/example.cz/html/index.html
    • mkdir -p – maakt de map aan, inclusief de bovenliggende mappen.
    • echo '…' > …/index.html – schrijft een eenvoudige pagina naar het bestand index.html.

    Je eigen websitebestanden upload je later naar deze map via SFTP (handleiding) of op het tabblad Bestanden.

  2. Maak de configuratie van de website aan:

    bash
    nano /etc/nginx/sites-available/example.cz
    /etc/nginx/sites-available/example.cznginx
    server {
        listen 80;
        listen [::]:80;
        server_name example.cz www.example.cz;
    
        root /var/www/example.cz/html;
        index index.html;
    
        location / {
            try_files $uri $uri/ =404;
        }
    }
DirectiveWat het doetWanneer wijzigen
listen 80;Luistert op poort 80 voor IPv4.Nooit (HTTPS voegt Certbot toe).
listen [::]:80;Hetzelfde voor IPv6.Verwijder de regel als de VPS geen IPv6 heeft.
server_name example.cz www.example.cz;De domeinen waarvoor het blok geldt. Op basis daarvan kiest Nginx de website.Vul je eigen domeinen in, gescheiden door een spatie.
root /var/www/example.cz/html;De map met de bestanden van de website.Als de bestanden op een andere plek staan.
index index.html;Welk bestand wordt verstuurd als het adres eindigt op /.Bij PHP-websites index.php index.html.
location / { … }De regels voor alle adressen van de website.–
try_files $uri $uri/ =404;Probeert een bestand te vinden, daarna een map; bestaat er niets, dan volgt fout 404.Bij single-page-applicaties (React, Vue) try_files $uri $uri/ /index.html;.
  1. Schakel de website in, schakel de standaard welkomstpagina uit, test de configuratie en laad haar:

    bash
    ln -s /etc/nginx/sites-available/example.cz /etc/nginx/sites-enabled/
    rm /etc/nginx/sites-enabled/default
    nginx -t
    systemctl reload nginx
    • ln -s … /etc/nginx/sites-enabled/ – maakt de koppeling aan, Nginx gaat de website laden.
    • rm /etc/nginx/sites-enabled/default – schakelt de standaardwebsite uit (het bestand blijft in sites-available staan).
    • nginx -t – controleert de syntaxis van alle configuraties. Het moet eindigen met syntax is ok en test is successful.
    • systemctl reload nginx – laadt de nieuwe configuratie zonder verbindingen te onderbreken.

Controle: http://example.cz toont de kop “Het werkt”.

Reverse proxy voor een applicatie#

Een applicatie (Node.js, Python, Java, een Docker-container) draait meestal op een eigen poort, bijvoorbeeld 3000. Je plaatst Nginx ervoor als reverse proxy: die ontvangt de verzoeken op poort 80/443 en stuurt ze door naar de applicatie. De voordelen: Nginx regelt HTTPS, de applicatie hoeft niet zichtbaar te zijn vanaf internet en op één VPS kunnen meerdere applicaties onder verschillende domeinen draaien.

Laat de applicatie alleen op 127.0.0.1 luisteren

Stel de applicatie zo in dat ze op 127.0.0.1:3000 luistert, niet op 0.0.0.0:3000. Dan is ze alleen via Nginx bereikbaar en kan niemand eromheen door de poort rechtstreeks te benaderen. Publiceer de poort bij Docker als 127.0.0.1:3000:3000 (zie Docker en Docker Compose).

De configuratie (in plaats van de statische website, of als extra bestand voor een ander domein, bijvoorbeeld app.example.cz):

/etc/nginx/sites-available/app.example.cznginx
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    listen [::]:80;
    server_name app.example.cz;

    client_max_body_size 20m;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;

        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;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 300s;
    }
}
DirectiveWat het doetWanneer wijzigen
map $http_upgrade $connection_upgrade { … }Een hulpvariabele voor websockets: vraagt de browser om een upgrade van de verbinding, dan wordt upgrade verstuurd, anders close. Eén keer in de hele configuratie is genoeg (bij een tweede keer meldt nginx -t een duplicaat).Nooit.
client_max_body_size 20m;De maximale grootte van een geüpload verzoek. De standaardwaarde is 1 MB; een grotere upload eindigt met fout 413.Afhankelijk van wat de applicatie accepteert.
proxy_pass http://127.0.0.1:3000;Waar de verzoeken naartoe worden doorgestuurd.De poort van je applicatie.
proxy_http_version 1.1;De verbinding met de applicatie loopt via HTTP/1.1. Oudere versies van Nginx gebruiken anders HTTP/1.0, waarmee websockets niet werken.Nooit.
proxy_set_header Host $host;De applicatie ziet het oorspronkelijke domein uit de browser.Nooit.
proxy_set_header X-Real-IP …, X-Forwarded-For …De applicatie ziet het echte IP-adres van de bezoeker, niet 127.0.0.1.Nooit.
proxy_set_header X-Forwarded-Proto $scheme;De applicatie weet dat de bezoeker via HTTPS is binnengekomen (belangrijk voor omleidingen en cookies).Nooit.
Upgrade, ConnectionGeven het verzoek om een websocket door (chat, live-updates, Socket.IO).Nooit.
proxy_read_timeout 300s;Hoelang er op het antwoord van de applicatie wordt gewacht. De standaardwaarde van 60 s sluit inactieve websockets.Verhoog de waarde bij lange bewerkingen.

Inschakelen gaat hetzelfde als bij de statische website:

bash
ln -s /etc/nginx/sites-available/app.example.cz /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx

Om de applicatie permanent te laten draaien en na een herstart van de VPS vanzelf te laten starten, stel je haar in als service: Applicatie 24/7 via systemd.

HTTPS via Certbot#

Certbot installeren#

bash
apt -y install certbot python3-certbot-nginx
  • certbot – de client voor Let's Encrypt.
  • python3-certbot-nginx – een plugin die het domein via Nginx kan verifiëren en de configuratie ervan zelf aanpast.

Certbot uit snap

De makers van Certbot raden de installatie via snap aan, die altijd de nieuwste versie heeft. Het pakket uit de distributie (Ubuntu 24.04 heeft Certbot 2.9, Debian 13 versie 4.0) werkt voor normaal gebruik hetzelfde en wordt samen met de andere pakketten bijgewerkt. Wil je snap, volg dan certbot.eff.org/instructions (opent in een nieuw venster) en verwijder eerst de pakketten uit apt, zodat je geen twee Certbots hebt.

Een certificaat aanvragen#

bash
certbot --nginx -d example.cz -d www.example.cz
  • --nginx – verificatie via Nginx en automatische aanpassing van de configuratie ervan.
  • -d example.cz -d www.example.cz – de domeinen waarvoor je het certificaat wilt. Elk domein moet een DNS-record hebben dat naar de VPS verwijst en moet in de server_name van een van de websites staan.

Bij de eerste keer starten stelt Certbot deze vragen:

VraagWat je antwoordtWaarom
E-mail (Enter email address…)Je eigen adresLet's Encrypt stuurt er belangrijke meldingen naartoe. Waarschuwingen over het naderende einde van de geldigheid stuurt het sinds 2025 niet meer, de verlenging wordt automatisch bewaakt.
Please read the Terms of Service… Do you agree?YZonder akkoord met de voorwaarden krijg je geen certificaat.
Would you be willing… to share your email address with the Electronic Frontier Foundation…Y of NAlleen voor nieuws van de EFF, het heeft geen invloed op het certificaat.

Het resultaat: Successfully deployed certificate for example.cz to /etc/nginx/sites-enabled/example.cz en Congratulations! You have successfully enabled HTTPS….

Wat Certbot in de configuratie heeft gewijzigd#

Open de configuratie van de website (nano /etc/nginx/sites-available/example.cz). De regels met # managed by Certbot heeft Certbot toegevoegd:

Toegevoegde regelWat het doet
listen 443 ssl;De website luistert ook op poort 443 met TLS.
listen [::]:443 ssl ipv6only=on;Hetzelfde voor IPv6. Certbot voegt de regel toe als het blok listen [::]:80 bevat. De optie ipv6only=on (op IPv6 alleen voor IPv6-⁠verbindingen luisteren) vult het alleen bij de eerste website op de VPS in, bij de volgende staat de regel er zonder.
ssl_certificate /etc/letsencrypt/live/example.cz/fullchain.pem;Het certificaat (inclusief tussencertificaten).
ssl_certificate_key /etc/letsencrypt/live/example.cz/privkey.pem;De privésleutel van het certificaat. Geef die aan niemand.
include /etc/letsencrypt/options-ssl-nginx.conf;De aanbevolen TLS-instellingen van Certbot (protocollen, ciphers).
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;De parameters voor de sleuteluitwisseling.
een nieuw server-blok met return 301 https://…Leidt HTTP om naar HTTPS. Certbot voegt het automatisch toe.

Pas deze regels niet handmatig aan als je niet precies weet waarom. Certbot rekent erop bij het verlengen.

Automatische verlenging#

Een Let's Encrypt-certificaat is 90 dagen geldig (stand oktober 2026, Let's Encrypt verkort de geldigheid stapsgewijs). Het pakket van Certbot heeft een timer geïnstalleerd die twee keer per dag de certificaten controleert en de certificaten verlengt waarvan nog ongeveer een derde van de geldigheid over is. De plugin voor Nginx laadt Nginx na een verlenging zelf opnieuw.

Controleer of de timer bestaat en of de verlenging slaagt:

bash
systemctl list-timers | grep certbot
certbot renew --dry-run
  • systemctl list-timers | grep certbot – toont een regel met certbot.timer en het tijdstip van de volgende uitvoering.
  • certbot renew --dry-run – probeert de verlenging uit tegen de testserver, de echte certificaten blijven ongewijzigd. Het moet eindigen met Congratulations, all simulated renewals succeeded.

Een overzicht van de certificaten en hun geldigheid toont certbot certificates.

Limieten van Let's Encrypt

Let's Encrypt geeft voor dezelfde set domeinen hoogstens 5 certificaten per 7 dagen uit en blokkeert je tijdelijk na 5 mislukte domeinverificaties per uur. Gebruik bij het testen daarom --dry-run (testserver) en voer het echte commando pas uit als de test slaagt.

Cloudflare en SSL-modi#

Loopt de website via de proxy van Cloudflare (oranje wolkje), dan worden twee verbindingen versleuteld: tussen de bezoeker en Cloudflare, en tussen Cloudflare en je VPS. De tweede wordt bepaald door de modus SSL/TLS in Cloudflare:

ModusVerbinding van Cloudflare naar de VPSAdvies
OffZonder versleutelingNiet gebruiken.
FlexibleOnversleuteld HTTP naar poort 80Niet gebruiken. Samen met de omleiding naar HTTPS in Nginx ontstaat een oneindige lus (ERR_TOO_MANY_REDIRECTS).
FullHetzelfde protocol als bij de bezoeker; bij HTTPS wordt het certificaat van de VPS niet gecontroleerdAlleen tijdelijk.
Full (strict)Hetzelfde protocol als bij de bezoeker; bij HTTPS met controle van het certificaat van de VPSAanbevolen. Je Let's Encrypt-certificaat is geldig, dus het werkt.

Voer na het overschakelen naar Proxied certbot renew --dry-run uit en controleer of de verlenging van het certificaat ook via de proxy van Cloudflare slaagt. Mislukt die, controleer dan of je poort 80 in Cloudflare niet met eigen regels blokkeert. Een blijvende oplossing voor gevorderden is het domein via DNS te verifiëren met de plugin python3-certbot-dns-cloudflare (de stappen staan in de documentatie van Certbot (opent in een nieuw venster)).

Basisbeveiliging#

  1. De versie van Nginx verbergen. Op Debian 13 staat de regel server_tokens off; al standaard in /etc/nginx/nginx.conf. Op Ubuntu 24.04 open je het bestand (nano /etc/nginx/nginx.conf) en haal je in het blok http de # weg vóór de regel # server_tokens off;. Nginx verraadt zijn versie dan niet meer in de headers en op foutpagina's.

  2. Beveiligingsheaders. Aan het blok server met poort 443 kun je toevoegen:

    nginx
    add_header X-Content-Type-Options nosniff always;
    add_header X-Frame-Options SAMEORIGIN always;
    add_header Referrer-Policy strict-origin-when-cross-origin always;
    • X-Content-Type-Options nosniff – de browser gaat het bestandstype niet raden (bescherming tegen ondergeschoven scripts).
    • X-Frame-Options SAMEORIGIN – de website kan niet in een frame op een vreemde pagina worden geplaatst (bescherming tegen clickjacking).
    • Referrer-Policy strict-origin-when-cross-origin – andere websites krijgen alleen het domein, niet het volledige adres van de pagina.
    • always – de header wordt ook bij foutantwoorden verstuurd.
  3. Voer na elke wijziging nginx -t en systemctl reload nginx uit.

HSTS pas na goed nadenken

De header Strict-Transport-Security (HSTS) verplicht browsers om voor het domein alleen HTTPS te gebruiken, en dat maanden vooruit. Werkt HTTPS daarna niet meer (een verlopen certificaat, een verhuizing van het domein), dan laadt de website niet. Schakel de header pas in als HTTPS betrouwbaar draait.

Controle#

bash
curl -I http://example.cz
curl -I https://example.cz
  • curl -I – haalt alleen de headers van het antwoord op.

Het eerste commando geeft HTTP/1.1 301 Moved Permanently terug met de header Location: https://example.cz/, het tweede HTTP/1.1 200 OK (of HTTP/2 200). In de browser zie je een slotje bij het adres.

Als iets niet werkt#

nginx -t meldt een fout#

De uitvoer bevat het bestand en het regelnummer, bijvoorbeeld unexpected "}" in /etc/nginx/sites-enabled/example.cz:12. Meestal ontbreekt de puntkomma aan het eind van een directive, of er staat een accolade te veel of te weinig. Herstel de regel en voer de test opnieuw uit. Herstart Nginx met een foute configuratie niet, hij zou niet starten.

502 Bad Gateway#

Nginx draait, maar de applicatie erachter reageert niet. Controleer of de applicatie draait en luistert op de poort uit proxy_pass:

bash
ss -tlnp | grep 3000
  • ss -tlnp – de luisterende TCP-poorten met de namen van de programma's.

Wordt er niets getoond, dan draait de applicatie niet of luistert ze ergens anders. Details vind je in /var/log/nginx/error.log (de regel met connect() failed (111: Connection refused)).

504 Gateway Time-out#

De applicatie heeft te laat geantwoord. Verhoog proxy_read_timeout, of zoek uit waarom de applicatie traag is.

413 Request Entity Too Large#

Het geüploade bestand is groter dan client_max_body_size (standaard 1 MB). Verhoog de waarde in het blok server en laad de configuratie opnieuw.

Je ziet “Welcome to nginx!” in plaats van je website#

De standaardwebsite is nog actief, of het domein in server_name komt niet overeen met het adres in de browser. Verwijder /etc/nginx/sites-enabled/default en controleer server_name.

Nginx start niet: Address already in use#

Poort 80 of 443 wordt bezet door een ander programma, meestal Apache. Zoek uit welk: ss -tlnp | grep -E ':80 |:443 '. Stop Apache en schakel het uit (systemctl disable --now apache2), of verwijder het.

Certbot: Could not automatically find a matching server block#

Geen enkele website heeft in server_name het domein waarvoor je het certificaat aanvraagt. Voeg het toe aan server_name, voer nginx -t en systemctl reload nginx uit en start Certbot opnieuw.

Certbot: Timeout during connect (likely firewall problem) of Connection refused#

Let's Encrypt kon poort 80 niet bereiken. Controleer de DNS (getent hosts example.cz), de firewall in je klantaccount en UFW. Heeft het domein een AAAA-record, dan moet dat naar het IPv6-⁠adres van deze VPS verwijzen en moet de website ook op IPv6 luisteren (listen [::]:80;).

Cloudflare: fout 521, 522, 525 of 526#

  • 521 – de VPS heeft de verbinding geweigerd: Nginx draait niet of de firewall blokkeert poort 443.
  • 522 – de verbinding is verlopen: de VPS is onbereikbaar of de firewall verwerpt de pakketten.
  • 525 – het opzetten van TLS is mislukt: Nginx heeft op poort 443 geen geldige HTTPS-configuratie.
  • 526 – het certificaat op de VPS is ongeldig of verlopen, en dat in de modus Full (strict). Controleer certbot certificates.

ERR_TOO_MANY_REDIRECTS#

Meestal Cloudflare in de modus Flexible samen met de omleiding naar HTTPS. Zet Cloudflare op Full (strict).

Samenvatting

  • Websites staan in /etc/nginx/sites-available/, actief zijn alleen de websites met een koppeling in sites-enabled.
  • Na elke wijziging: nginx -t en systemctl reload nginx.
  • Laat de applicatie op 127.0.0.1 luisteren en maak haar via Nginx bereikbaar met proxy_pass, inclusief de headers X-Forwarded-*.
  • Een certificaat vraag je aan met het commando certbot --nginx -d …; het wordt vanzelf verlengd, wat je controleert met certbot renew --dry-run.
  • Gebruik met Cloudflare de modus Full (strict), nooit Flexible.

Was deze handleiding nuttig?

Geldt voor

  • Ubuntu 24.04
  • Debian 13

Diensten

  • VPS
  • Domeinen en DNS
Fout in de handleiding melden

Zoeken in de handleidingen