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 rol als reverse proxy: ze luisteren op poort 443, lezen de hostnaam in elk verzoek en sturen dit door 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 voor Caddy als u wilt dat HTTPS automatisch voor u wordt afgehandeld en uw services standaard webapplicaties zijn. Kies voor Traefik als alles in Docker Compose draait en u om de paar weken een nieuwe service toevoegt. Kies voor 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 instantie. De werkwijze om dit te bereiken verschilt echter.
Caddy vraagt om het certificaat 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. Dat levert hetzelfde resultaat op als het aanmaken van een self-signed certificaat op Ubuntu, waarbij de vernieuwing voor u wordt afgehandeld.
Nginx heeft geen ingebouwde ACME-client. Certbot verkrijgt het certificaat en de --nginx-plugin herschrijft uw server-block om de 443-listener en de redirect toe te voegen. De vernieuwing draait via een systemd-timer die door het pakket wordt geïnstalleerd; er zijn dus twee bewegende delen en twee zaken om te verifiëren: systemctl list-timers | grep certbot toont aan dat de timer bestaat en sudo certbot renew --dry-run bewijst dat het vernieuwingspad nog steeds werkt. De stapsgewijze instructies staan 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 beschikt over 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 dit 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, wat de manier is waarop 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 wijze 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 gebruikt hetzelfde blok met de hostnaam en poort aangepast. De proxy_set_header-regels zijn geen decoratie: 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, zou uw gebruikers anders naar localhost sturen.
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 Caddy wat de client in die headers 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 bereiken.
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 helemaal 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
}
]Gerekend vanaf 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 afhandelt, en daarna 5 labels per applicatie.
Kijk naar de afweging, niet naar de winnaar. Traefik kost het meeste vóór de eerste applicatie en het minste 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 ze die 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 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 heeft inzicht in uw containers?
Traefik monitort de Docker-socket en bouwt routers op basis van container-labels zodra containers starten of stoppen. Geen van de andere opties doet dit automatisch. Nginx en Caddy vereisen beide een configuratiewijziging en een reload wanneer een nieuwe container verschijnt. Daarnaast hebben ze een bereikbaar adres nodig: 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 helder 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 op basis van labels services ontdekken, maar Caddy-plugins worden gecompileerd in de binary. U moet dus een eigen binary bouwen of een custom image maken 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 vereist hierbij extra configuratie. 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 configureert.
# /etc/nginx/conf.d/upgrade-map.conf
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}Voeg vervolgens binnen het location-blok drie regels toe 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 is noodzakelijk omdat een hardcoded Connection: upgrade bij elk verzoek zou worden meegestuurd, inclusief de eenvoudige verzoeken die close zouden moeten bevatten.
Nog twee Nginx-standaardinstellingen kunnen voor problemen zorgen. 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 om naar een tweerichtings-tunnel zonder dat daarvoor extra richtlijnen nodig zijn. Caddy verstuurt gegevens ook direct door wanneer het antwoord text/event-stream is of geen bekende lengte heeft, waardoor streaming zonder aanpassingen werkt. Traefik laat upgrades door en buffert antwoorden niet, 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-serverblok, 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 in de http-context thuis, niet binnen server; bewaar deze daarom in een eigen bestand onder /etc/nginx/conf.d/. Zet proxy_buffering alleen uit op locaties die streamen, aangezien buffering ervoor zorgt dat nginx de backend-worker bij standaardantwoorden eerder kan vrijgeven. Certbot herschrijft dit blok wanneer u het uitvoert, dus lees het bestand daarna opnieuw in.
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 eveneens een certificaat moet presenteren. Nginx vereist
ssl_client_certificate /etc/ssl/ca.pem;enssl_verify_client on;in het server-blok. 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 stellen standaard geen limiet aan 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. Caddy vereist een ingebouwde plugin. 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 een andere plugin, wat dus een aangepaste build vereist. - 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 een URL kunnen herschrijven. Bepaal welke de TLS-terminatie afhandelt en houd de andere op plain HTTP, gebonden aan loopback.
De firewall-valkuil na deze keuze
Het doel van een reverse proxy is dat alleen poort 80 en 443 openstaan. Docker maakt dit ongedaan zonder dat u het merkt. 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 de verbinding niet en is uw applicatie direct vanaf het publieke internet bereikbaar, naast de proxy die u zo zorgvuldig heeft geconfigureerd. Koppel gepubliceerde poorten aan localhost met 127.0.0.1:8080:80, of laat ports: volledig achterwege en laat de proxy de container bereiken via een Docker-netwerk, zoals in het bovenstaande voorbeeld met Traefik. Het mechanisme en de oplossing staan beschreven in waarom Docker gepubliceerde poorten ufw omzeilen.
Test dit vanaf een machine die niet de VPS zelf is, omdat 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-respons betekent dat de applicatie bereikbaar is zonder tussenkomst van uw proxy, waardoor alles wat u hierboven heeft geconfigureerd slechts schijnveiligheid is.
Welke proxy moet u kiezen?
Voornamelijk statische sites, plus een of twee apps: Caddy. Automatische HTTPS neemt u het grootste terugkerende werk uit handen. De configuratie blijft kort genoeg om in één scherm te lezen, 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 zich een ongebruikelijk probleem voordoet.
Een docker-compose homelab waar u steeds meer aan toevoegt: Traefik. Voorbij de derde service kosten labels minder werk 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 vanuit Traefik in plaats van een startfout; lees daarom docker logs traefik voor de parse-fout voordat u aanneemt dat de app defect is.
Een bestaande Nginx-configuratie, of een 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 normale gebruik 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 vóór 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 de server.
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) aan, terwijl Traefik een vergelijkbare bind-fout logt en afsluit. Draai één proxy op poort 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 het verkeer 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 daarom 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 instabiel is achter Nginx.