Nginx, Caddy of Traefik: welke reverse proxy kiezen?
Vergelijk Nginx, Caddy en Traefik voor uw VPS. Ontdek welke proxy het beste past bij uw behoeften voor TLS-certificaten, Docker-routing, websockets en configuratiegemak.
Nginx versus Caddy versus Traefik: het korte antwoord
Nginx, Caddy en Traefik vervullen alle drie dezelfde taak als reverse proxy: luisteren op poort 443, de hostnaam in elk verzoek lezen en dit doorsturen naar de juiste service op uw VPS. Elk van deze drie kan vier zelfgehoste applicaties achter één openbaar IP-adres plaatsen, en ze zijn alle drie snel genoeg zodat uw applicaties de vertragende factor zullen zijn. Het verschil zit in de manier waarop elk programma een TLS-certificaat (transport layer security) verkrijgt en hoeveel configuratiewerk elke extra applicatie van u vraagt. Het andere verschil wordt duidelijk op de dag dat u iets nodig heeft wat in de standaardhandleidingen wordt overgeslagen.
Kies Caddy als u wilt dat HTTPS automatisch voor u wordt afgehandeld en uw services gewone webapplicaties zijn. Kies Traefik als alles in Docker Compose draait en u om de paar weken een nieuwe service toevoegt. Kies Nginx als u dit al gebruikt, of als u behoefte heeft aan response caching, clientcertificaten, raw TCP forwarding of een uitgebreide bestaande configuratie die u liever niet herschrijft.
Hoe verkrijgt elk van deze een TLS-certificaat?
Dit aspect is voor de meeste mensen doorslaggevend, dus begin hier. Alle drie eindigen ze met hetzelfde certificaat van dezelfde certificaatautoriteit. De werkwijze om dit te bereiken verschilt echter.
Caddy vraagt het certificaat aan omdat u een hostnaam heeft opgegeven. Schrijf app.example.com als site-adres en Caddy vraagt via ACME (automatic certificate management environment) een certificaat aan bij Let's Encrypt, valt terug op ZeroSSL als dat mislukt, verzorgt de HTTP naar HTTPS-redirect op poort 80 en voert zelf de vernieuwing uit. Er is geen tweede tool en geen timer die u hoeft te controleren. Certificaten bevinden zich in de datamap van de caddy-gebruiker, /var/lib/caddy/.local/share/caddy bij een pakketinstallatie, dus voeg dat pad toe aan uw back-ups of accepteer een nieuwe uitgifte na een herbouw. Voor een hostnaam die niet publiek is, ondertekent tls internal met Caddy's eigen lokale certificaatautoriteit. Dit levert hetzelfde resultaat op als het aanmaken van een self-signed certificaat op Ubuntu, waarbij de vernieuwing voor u wordt afgehandeld.
Nginx heeft geen ACME-client. Certbot verkrijgt het certificaat en de --nginx-plugin herschrijft uw server-block om de 443-listener en de redirect toe te voegen. Vernieuwing draait via een systemd-timer die het pakket installeert, dus er zijn twee bewegende delen en twee zaken om te verifiëren: systemctl list-timers | grep certbot toont dat de timer bestaat en sudo certbot renew --dry-run bewijst dat het vernieuwingspad nog steeds werkt. De stapsgewijze handleiding staat in Certbot op Ubuntu 24.04 met Nginx, en dezelfde tool dekt een wildcard-certificaat via de DNS-01 challenge wanneer u meer subdomeinen heeft dan u wilt opgeven.
Traefik bevat een eigen ACME-client. U configureert één certificaat-resolver in de statische configuratie en elke router kan deze vervolgens gebruiken. Alle statusinformatie, inclusief de accountsleutel en certificaten, staat in één enkel acme.json-bestand. Traefik weigert dat bestand te gebruiken als het leesbaar is voor anderen dan de eigenaar, en meldt dit voordat de resolver wordt uitgeschakeld:
The ACME resolver "le" is skipped from the resolvers list because: unable to get ACME account: permissions 660 for /letsencrypt/acme.json are too open, please use 600Mount een map en laat Traefik het bestand zelf aanmaken. Maakt u het eerst aan met touch, dan erft het uw umask over; dat is hoe de meeste mensen tegen deze foutmelding aanlopen.
Eén ding geldt voor alle drie. De HTTP-01 challenge vereist dat poort 80 bereikbaar is vanaf het internet, omdat de certificaatautoriteit hiermee verbinding maakt. Open alleen poort 443 en de uitgifte mislukt op een manier die lijkt op een DNS-fout.
Dezelfde routeringstaak voor twee applicaties in drie configuraties
De taak: app.example.com gaat naar een service op 127.0.0.1:8080, files.example.com gaat naar een service op 127.0.0.1:8081, beide via HTTPS. Hieronder staat de volledige configuratie voor elke proxy, zodat het verschil in beknoptheid zichtbaar is in plaats van enkel beweerd.
Nginx
# /etc/nginx/sites-available/app.example.com
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
}Koppel vervolgens de configuratie, test deze, herlaad de service en voeg het certificaat toe.
sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d app.example.comnginx -t met de parameters syntax is ok en test is successful is de controle die u voor elke herlading moet uitvoeren. De tweede applicatie is hetzelfde blok met de hostnaam en de poort aangepast. De proxy_set_header-regels zijn geen versiering: wanneer proxy_pass een adres benoemt, stuurt Nginx standaard Host: 127.0.0.1:8080 door naar de upstream. Een applicatie die absolute URL's opbouwt op basis van de Host-header, stuurt uw gebruikers daardoor naar localhost. Waar elk van die vier headers voor dient, en waarom een afsluitende slash bij proxy_pass het pad dat uw applicatie ontvangt stilletjes wijzigt, wordt per directief behandeld in deze walkthrough van een Nginx server block.
Caddy
app.example.com {
reverse_proxy 127.0.0.1:8080
}
files.example.com {
reverse_proxy 127.0.0.1:8081
}sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddyDat is het volledige bestand. reverse_proxy regelt X-Forwarded-For, X-Forwarded-Proto en X-Forwarded-Host zelf. Standaard negeert het de headers die de client heeft meegestuurd, waardoor een verzoek uw backend niet kan misleiden over de herkomst. Certificaten, de redirect van poort 80 en de vernieuwing volgen automatisch uit de twee site-adressen. Er is niets anders in het bestand nodig om dit te activeren.
Traefik
Traefik vereist een statische configuratie voordat het verkeer kan routeren. Als Compose-service, met de image-tag die actueel is per augustus 2026:
services:
traefik:
image: traefik:v3.7
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.le.acme.email=you@example.com"
- "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencryptElke applicatie draagt vervolgens zijn eigen routering via labels in het bijbehorende Compose-bestand:
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`app.example.com`)"
- "traefik.http.routers.app.entrypoints=websecure"
- "traefik.http.routers.app.tls.certresolver=le"
- "traefik.http.services.app.loadbalancer.server.port=8080"loadbalancer.server.port is de poort binnen de container, niet een gepubliceerde poort, omdat Traefik de container bereikt via een gedeeld Docker-netwerk. De applicatie heeft geen ports:-regel nodig, en dat is het werkelijke voordeel: alleen Traefik is gepubliceerd. De volledige opzet, inclusief het gedeelde netwerk en de redirect-middleware, staat in meerdere applicaties routeren met Traefik en Docker Compose.
Hoeveel configuratie kost elke extra applicatie?
The data behind this chart
[
{
"tool": "Nginx",
"proxy_setup_lines": 0,
"lines_per_app": 11
},
{
"tool": "Caddy",
"proxy_setup_lines": 0,
"lines_per_app": 3
},
{
"tool": "Traefik",
"proxy_setup_lines": 17,
"lines_per_app": 5
}
]Geteld op basis van de bovenstaande blokken. Het Nginx server-blok bestaat uit 11 niet-lege regels, en u schrijft dit opnieuw voor elke hostnaam. Het Caddy site-blok bestaat uit 3 regels. Traefik vereist 17 regels aan statische configuratie voordat het één verzoek verwerkt, gevolgd door 5 labels per applicatie.
Kijk naar de afweging, niet naar de winnaar. Traefik kost het meest vóór de eerste applicatie en het minst voor elke volgende applicatie; de totalen komen rond de derde site bij elkaar. Daaronder is de statische configuratie overhead die u niet nodig had. Daarboven nemen labels de leiding en bouwen deze uit, omdat de routering zich naast de service bevindt die het routeert. Verwijder de service en de route verdwijnt mee; dit is het punt waarop een centraal configuratiebestand tekortschiet: verouderde server-blokken voor applicaties die al maanden niet meer bestaan.
Het aantal regels is ook een vertekend beeld in het voordeel van Nginx. Elk van die blokken vereist een symlink, een nginx -t, een reload en een certbot-run, terwijl de Caddy-aanpassing slechts één reload vereist en de Traefik-aanpassing helemaal geen commando behoeft. Alle drie herladen zonder actieve verbindingen te verbreken. Het verschil zit in het aantal afzonderlijke stappen dat u moet onthouden om één uur 's nachts.
Welke tool is op de hoogte van uw containers?
Traefik monitort de Docker-socket en bouwt routers op basis van container-labels zodra containers starten of stoppen. Geen van de andere genoemde opties doet dit automatisch. Nginx en Caddy vereisen beide een configuratiewijziging en een herlaadactie wanneer er een nieuwe container verschijnt. Daarnaast hebben ze een adres nodig dat ze kunnen bereiken: ofwel een poort die is gepubliceerd op loopback, of een gedeeld Docker-netwerk waar de proxy aan is gekoppeld.
Deze functionaliteit heeft een prijs, en het is belangrijk om dit duidelijk te benoemen. Traefik leest /var/run/docker.sock. Iedereen die met die socket kan communiceren, kan een container starten met het bestandssysteem van de host erin gemount, wat neerkomt op root-toegang op de host. Het read-only mounten vermindert het risico, maar neemt het niet weg. Als dit relevant is voor uw dreigingsmodel, plaats dan een socket-proxy tussen de twee die alleen de endpoints voor de containerlijst blootstelt die Traefik nodig heeft.
Caddy kan via een community-plugin aan discovery op basis van labels doen, maar Caddy-plugins worden gecompileerd in de binary. U moet dus een eigen binary of een aangepaste image bouwen met xcaddy, waarna u zelf verantwoordelijk bent voor het beheer en de updates van die build. Voor drie of vier services is het bewerken van een Caddyfile minder werk.
Websockets en streaming: wat breekt er en waarom
Nginx heeft hierbij hulp nodig. Een WebSocket-verbinding begint als een HTTP-verzoek met Upgrade: websocket, en nginx stuurt hop-by-hop headers niet door naar de upstream tenzij u dit expliciet aangeeft.
# /etc/nginx/conf.d/upgrade-map.conf
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}Plaats vervolgens in het location-blok drie regels die allemaal aanwezig moeten zijn:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;Laat u deze weg, dan toont de browserconsole WebSocket connection to 'wss://app.example.com/ws' failed, terwijl uw backend-logboek een standaard GET-verzoek weergeeft. De map bestaat omdat een hardcoded Connection: upgrade bij elk verzoek zou worden meegestuurd, inclusief de eenvoudige verzoeken die close zouden moeten bevatten.
Nog twee Nginx-standaardinstellingen zorgen voor problemen. proxy_read_timeout staat standaard op 60 seconden en is van toepassing op de tunnel na de upgrade; een websocket zonder verkeer gedurende een minuut wordt dus door de proxy gesloten. Daarnaast komen server-sent events vertraagd of in bursts aan totdat u proxy_buffering off; instelt op die locatie, omdat nginx het antwoord in de buffer houdt terwijl uw pagina erop wacht.
Caddy voert de upgrade uit en schakelt de verbinding over naar een tweerichtings-tunnel zonder dat daarvoor extra instructies nodig zijn. Het verstuurt gegevens ook direct wanneer het antwoord text/event-stream is of geen bekende lengte heeft, waardoor streaming zonder aanpassingen werkt. Traefik laat upgrades door en buffert geen antwoorden, tenzij u zelf de buffering-middleware toevoegt. Als uw services chat, een webterminal, log-tails of live dashboards bevatten, is dit een aanzienlijk verschil in de hoeveelheid configuratie die u moet schrijven en debuggen.
Het volledige Nginx server-blok, inclusief websockets en SSE
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name app.example.com;
client_max_body_size 64m;
location / {
proxy_pass http://127.0.0.1:8080;
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 3600s;
proxy_buffering off;
}
}De map hoort thuis in de http-context, niet in server, dus bewaar deze in een eigen bestand onder /etc/nginx/conf.d/. Schakel proxy_buffering alleen uit op locaties die streamen, aangezien buffering ervoor zorgt dat nginx de backend-worker vroegtijdig kan vrijgeven bij standaard antwoorden. Certbot herschrijft dit blok wanneer u het uitvoert; lees het bestand daarom achteraf opnieuw door.
Wat als u iets ongebruikelijks nodig heeft?
Dit is waar Nginx zijn extra regels code rechtvaardigt.
- Clientcertificaten, ook wel mTLS (mutual TLS) genoemd, waarbij de client ook een certificaat moet presenteren. Nginx vereist
ssl_client_certificate /etc/ssl/ca.pem;enssl_verify_client on;in het serverblok. Caddy vereist eenclient_auth-blok binnentls. Traefik-labels kunnen dit niet uitdrukken: u definieert een TLS-optie in een file provider en verwijst de router ernaar mettraefik.http.routers.app.tls.options=mtls@file. Het model waarbij alles in labels wordt geplaatst, kent een uitzondering zodra u dit nodig heeft. - Grote uploads. Nginx beperkt request bodies standaard tot 1 MB. Een grotere upload resulteert in
413 Request Entity Too Largeen het foutenlogboek vermeldtclient intended to send too large body. Verhoogclient_max_body_size. Caddy en Traefik hanteren standaard geen limiet voor de body, waardoor het verzoek uw applicatie bereikt en de limiet van de applicatie zelf bepalend is. - Response caching. Nginx beschikt over
proxy_cacheen dit is volwassen technologie. Voor Caddy moet een plugin worden meegecompileerd. De open-source build van Traefik bevat helemaal geen HTTP-cache, wat verrassend is voor gebruikers die aannemen dat elke proxy cachet. - Raw TCP of UDP, voor een databasepoort of een gameserver. Nginx heeft de
stream-module. Traefik heeft TCP- en UDP-routers op hun eigen entrypoints. Caddy vereist hiervoor een extra plugin, wat een aangepaste build noodzakelijk maakt. - Een webserver die al achter de proxy staat. Als de service een klassieke PHP-applicatie is, dan bevat een LAMP stack op Ubuntu 24.04 al Apache. Het plaatsen van een proxy daarvoor zorgt voor twee locaties die headers instellen en twee locaties die URL's kunnen herschrijven. Bepaal welke de TLS-terminatie afhandelt en houd de andere op plain HTTP, gebonden aan loopback.
De firewall-valkuil bij deze keuze
Het doel van een reverse proxy is dat alleen poort 80 en 443 openstaan. Docker heft dit stilletjes op. Het publiceren van een poort met -p 8080:80 schrijft een DNAT-regel naar de nat-tabel. Deze regel wordt geëvalueerd vóór de INPUT-regels die ufw beheert. Hierdoor blokkeert ufw deny 8080 het verkeer niet en staat uw applicatie direct op het openbare internet, naast de proxy die u zo zorgvuldig heeft geconfigureerd. Bind gepubliceerde poorten aan de loopback-interface met 127.0.0.1:8080:80, of laat ports: volledig achterwege en laat de proxy de container bereiken via een Docker-netwerk; dit is de methode die het bovenstaande Traefik-voorbeeld gebruikt. Het mechanisme en de oplossing staan beschreven in waarom gepubliceerde Docker-poorten ufw omzeilen.
Test dit vanaf een machine die niet de VPS zelf is, aangezien een controle vanaf de server zelf altijd slaagt:
curl --max-time 5 http://your.server.address:8080Connection refused of een timeout is het resultaat dat u wilt zien. Een HTTP-antwoord betekent dat de applicatie bereikbaar is zonder tussenkomst van uw proxy, waardoor alles wat u hierboven heeft geconfigureerd slechts decoratie is.
Welke proxy moet u kiezen?
Voornamelijk statische sites, plus een of twee applicaties: Caddy. Automatische HTTPS neemt u het grootste terugkerende karwei uit handen. De configuratie blijft kort genoeg om in één scherm te bekijken, en een statische site is een root-regel en een file_server-regel binnen hetzelfde site-blok. Het nadeel is een kleinere verzameling kant-en-klare oplossingen wanneer er iets ongebruikelijks misgaat.
Een docker-compose homelab waar u steeds meer aan toevoegt: Traefik. Na de derde service kosten labels minder moeite dan het bewerken van een centraal bestand, en een verwijderde service neemt zijn route met zich mee. Trek een middag uit voor de eerste installatie, omdat entrypoints, routers, services en middlewares allemaal nieuwe terminologie zijn. Een typefout in een label resulteert meestal in een 404 van Traefik in plaats van een startfout; lees daarom docker logs traefik voor de parse-fout voordat u aanneemt dat de applicatie defect is.
Een bestaande Nginx-configuratie, of enige vereiste uit de bovenstaande lijst: Nginx. Het heeft al een oplossing voor response caching en client-certificaten, en bijna elke handleiding van derden gaat ervan uit. Het nadeel is dat certificaten en websocket-ondersteuning zaken zijn die u zelf moet configureren in plaats van dat u ze standaard krijgt.
Eén regel geldt ongeacht uw keuze. Precies één proces luistert op de publieke interface, en al het overige verkeer luistert op loopback of op een privaat Docker-netwerk.
FAQ
Welke reverse proxy is het meest geschikt voor een paar Docker-applicaties op één VPS?
Voor drie of vier services die u af en toe toevoegt, verdient Traefik zichzelf terug, omdat elke app zijn eigen routeringslabels bevat en u geen centraal bestand hoeft aan te passen. Als de services stabiel zijn en u vooral wilt dat HTTPS geen omkijken meer vereist, is Caddy eenvoudiger te leren en minder foutgevoelig. Kies Nginx als u er al bekend mee bent, of wanneer u een functie nodig heeft die de andere twee missen, zoals response caching of een standaard TCP-listener.
Heeft Caddy echt geen certificaatconfiguratie nodig?
Voor het standaardgeval wel. Een publieke hostnaam opgeven als site-adres is de volledige configuratie: Caddy vraagt het certificaat aan via ACME, handelt de redirect vanaf poort 80 af en vernieuwt het certificaat voor de verloopdatum. Twee voorwaarden moeten echter vervuld zijn. Poort 80 moet bereikbaar zijn vanaf het internet voor de HTTP-01 challenge, en het DNS A- of AAAA-record van de hostnaam moet al naar de VPS wijzen, omdat de certificaatautoriteit de naam opzoekt en terugverbindt naar dat adres.
Kan ik Nginx en Traefik op dezelfde VPS draaien?
Niet op dezelfde poorten. Degene die als tweede start, kan de poort niet binden; Nginx geeft bind() to 0.0.0.0:443 failed (98: Address already in use) en Traefik logt een vergelijkbare bind-fout en sluit af. Draai één proxy op 80 en 443 en plaats al het overige verkeer daarachter. Als u aan het migreren bent, verplaats de hostnamen dan één voor één: laat de voorste proxy doorsturen naar de oude proxy op een loopback-poort totdat de laatste site is verhuisd.
Waarom verbreken mijn websockets na 60 seconden achter Nginx?
proxy_read_timeout staat standaard op 60 seconden en dit is van toepassing op de tunnel zodra de upgrade is voltooid. Een verbinding zonder verkeer gedurende een minuut wordt dus door de proxy gesloten in plaats van door uw app. Verhoog deze waarde op die locatie met proxy_read_timeout 3600s;, of laat de applicatie elke 30 seconden een ping-frame sturen. Caddy en Traefik sluiten inactieve geüpgradede verbindingen niet af op basis van een timer van één minuut; daarom kan dezelfde app stabiel lijken achter deze proxies, terwijl deze onstabiel is achter Nginx.