Docker-apps beveiligen met Let’s Encrypt en Nginx: HTTPS setup & debugging
Deze tutorial laat je stap voor stap zien hoe je Docker-applicaties veilig achter Nginx zet met een Let’s Encrypt TLS-certificaat (HTTPS), inclusief een degelijke debugging-aanpak wanneer het misgaat. Je krijgt echte commando’s, uitleg over de onderliggende concepten, en praktische checks om 80/443, certificaten, headers en reverse proxy-problemen te verifiëren.
Doel: meerdere Docker-apps (bijv.
app1enapp2) bereikbaar maken viahttps://app1.jouwdomein.nlenhttps://app2.jouwdomein.nl, met automatische certificaatvernieuwing.
Inhoud
- Voorwaarden & uitgangspunten
- DNS en firewall: de basis die vaak vergeten wordt
- Architectuurkeuzes: Nginx op de host vs. in Docker
- Benadering A: Nginx + Certbot op de host (aanrader voor eenvoud)
- Benadering B: Alles in Docker (Nginx + Certbot containers)
- Debugging: systematisch problemen oplossen
- Checklist: productie-hardening
Voorwaarden & uitgangspunten
Je hebt nodig:
- Een VPS of server met Linux (voorbeelden: Ubuntu 22.04/24.04).
- Root/sudo-toegang.
- Docker en Docker Compose (optioneel, maar handig).
- Een domein, bijvoorbeeld
jouwdomein.nl, met subdomeinen:app1.jouwdomein.nlapp2.jouwdomein.nl
- Publiek bereikbare poorten 80 en 443 naar je server.
- Basiskennis van Docker-netwerken en Nginx reverse proxy.
We gaan uit van twee apps:
app1: luistert intern op poort3000app2: luistert intern op poort8080
DNS en firewall: de basis die vaak vergeten wordt
DNS instellen
Maak A-records aan die naar het publieke IP van je server wijzen:
app1.jouwdomein.nl -> 203.0.113.10app2.jouwdomein.nl -> 203.0.113.10
Controleer DNS:
dig +short app1.jouwdomein.nl A
dig +short app2.jouwdomein.nl A
Of:
nslookup app1.jouwdomein.nl
Firewall/poort forwarding
Let’s Encrypt gebruikt meestal de HTTP-01 challenge: Let’s Encrypt moet jouw server kunnen bereiken op poort 80 om een token op te halen. Daarna gebruik je HTTPS op 443.
Controleer lokaal of Nginx luistert:
sudo ss -tulpn | grep -E ':80|:443'
Als je UFW gebruikt:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status
Als je op cloudniveau security groups gebruikt (AWS/GCP/Azure/Hetzner), open daar ook 80/443.
Architectuurkeuzes: Nginx op de host vs. in Docker
Er zijn twee gangbare manieren:
-
Nginx op de host + Certbot op de host
- Simpel, weinig moving parts.
- Makkelijk te debuggen met systemd logs.
- Nginx kan Docker-apps bereiken via
localhost(als je ports publish’t) of via Docker bridge IP’s.
-
Nginx in Docker + Certbot in Docker
- Alles containerized.
- Iets complexer: volumes voor certificaten, shared webroot, reload van Nginx.
- Debugging vereist meer containerkennis.
In deze tutorial werk ik Benadering A volledig uit (aanrader). Daarna geef ik een complete werkbare Benadering B.
Benadering A: Nginx + Certbot op de host (aanrader voor eenvoud)
4.1 Docker-apps op een intern netwerk
Maak een mapstructuur:
mkdir -p ~/stack
cd ~/stack
Voorbeeld docker-compose.yml voor twee apps (simpel gehouden). Let op: je kunt apps ook zonder compose draaien; compose maakt het overzichtelijk.
cat > docker-compose.yml <<'EOF'
services:
app1:
image: node:20-alpine
command: sh -c "node -e \"require('http').createServer((req,res)=>res.end('app1 ok')).listen(3000)\""
expose:
- "3000"
networks:
- internal
app2:
image: nginx:alpine
volumes:
- ./app2-html:/usr/share/nginx/html:ro
expose:
- "80"
networks:
- internal
networks:
internal:
driver: bridge
EOF
Maak content voor app2:
mkdir -p app2-html
echo "app2 ok" > app2-html/index.html
Start:
docker compose up -d
docker compose ps
Belangrijk concept: expose publiceert niet naar de host; het maakt de poort alleen zichtbaar binnen Docker-netwerken. Nginx op de host kan containers niet direct via expose bereiken, tenzij je extra routing doet. Daarom heb je twee opties:
- Publiceer poorten naar localhost (
ports: "127.0.0.1:3000:3000") - Of laat Nginx ook in Docker draaien (Benadering B)
Voor Benadering A kiezen we meestal: publish naar localhost zodat host-Nginx upstreams kan bereiken zonder ze publiek te maken.
Pas compose aan:
cat > docker-compose.yml <<'EOF'
services:
app1:
image: node:20-alpine
command: sh -c "node -e \"require('http').createServer((req,res)=>res.end('app1 ok')).listen(3000)\""
ports:
- "127.0.0.1:3000:3000"
app2:
image: nginx:alpine
volumes:
- ./app2-html:/usr/share/nginx/html:ro
ports:
- "127.0.0.1:8080:80"
EOF
Herstart:
docker compose up -d
Test lokaal op de server:
curl -i http://127.0.0.1:3000
curl -i http://127.0.0.1:8080
Je ziet app1 ok en app2 ok.
Waarom 127.0.0.1 binding?
Omdat je app-poorten niet publiek wil exposen. Nginx is de enige publieke ingang. Binding op 127.0.0.1 zorgt dat alleen de host zelf (en dus Nginx) erbij kan.
4.2 Nginx installeren en basisconfig
Installeer Nginx:
sudo apt update
sudo apt install -y nginx
Controleer status:
sudo systemctl status nginx --no-pager
Check config syntax:
sudo nginx -t
4.3 HTTP reverse proxy werkend krijgen (zonder TLS)
We maken twee server blocks. Gebruik de standaard Debian/Ubuntu layout:
/etc/nginx/sites-available//etc/nginx/sites-enabled/
app1 (HTTP)
sudo tee /etc/nginx/sites-available/app1.jouwdomein.nl.conf > /dev/null <<'EOF'
server {
listen 80;
listen [::]:80;
server_name app1.jouwdomein.nl;
# Handig voor debugging: log apart
access_log /var/log/nginx/app1.access.log;
error_log /var/log/nginx/app1.error.log;
location / {
proxy_pass http://127.0.0.1:3000;
# Belangrijke headers voor reverse proxy
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;
}
}
EOF
app2 (HTTP)
sudo tee /etc/nginx/sites-available/app2.jouwdomein.nl.conf > /dev/null <<'EOF'
server {
listen 80;
listen [::]:80;
server_name app2.jouwdomein.nl;
access_log /var/log/nginx/app2.access.log;
error_log /var/log/nginx/app2.error.log;
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;
}
}
EOF
Activeer sites:
sudo ln -s /etc/nginx/sites-available/app1.jouwdomein.nl.conf /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/app2.jouwdomein.nl.conf /etc/nginx/sites-enabled/
Test en reload:
sudo nginx -t
sudo systemctl reload nginx
Test extern (vanaf je laptop):
curl -i http://app1.jouwdomein.nl
curl -i http://app2.jouwdomein.nl
Als dit werkt, is je reverse proxy correct en kun je TLS toevoegen. Als dit niet werkt, ga dan direct naar Debugging (met name DNS/poorten en 502).
4.4 Let’s Encrypt certificaat aanvragen met Certbot (nginx plugin)
Installeer Certbot + Nginx plugin:
sudo apt install -y certbot python3-certbot-nginx
Vraag certificaten aan. Je kunt meerdere domeinen in één certificaat zetten, maar vaak is per app ook prima. Hier doen we per stuk voor duidelijkheid:
sudo certbot --nginx -d app1.jouwdomein.nl
sudo certbot --nginx -d app2.jouwdomein.nl
Wat gebeurt er onder water?
- Certbot past tijdelijk je Nginx-config aan om de ACME-challenge te kunnen serveren.
- Let’s Encrypt valideert via HTTP (poort 80) dat jij controle hebt over het domein.
- Daarna plaatst Certbot certificaten in:
/etc/letsencrypt/live/app1.jouwdomein.nl/fullchain.pem/etc/letsencrypt/live/app1.jouwdomein.nl/privkey.pem
- Certbot kan ook automatisch HTTP→HTTPS redirects configureren.
Controleer Nginx config opnieuw:
sudo nginx -t
sudo systemctl reload nginx
Test HTTPS:
curl -I https://app1.jouwdomein.nl
curl -I https://app2.jouwdomein.nl
Bekijk certificaatdetails:
echo | openssl s_client -connect app1.jouwdomein.nl:443 -servername app1.jouwdomein.nl 2>/dev/null | openssl x509 -noout -issuer -subject -dates
4.5 HSTS, security headers en TLS-instellingen
Certbot maakt meestal een werkende TLS-config, maar productie-hardening is meer dan “slotje aan”.
HTTP naar HTTPS redirect expliciet maken
Soms wil je de HTTP-serverblock minimalistisch maken:
server {
listen 80;
listen [::]:80;
server_name app1.jouwdomein.nl;
return 301 https://$host$request_uri;
}
Maar let op: voor Let’s Encrypt renewals via HTTP-01 moet /.well-known/acme-challenge/ bereikbaar blijven. Certbot regelt dit doorgaans automatisch. Als je handmatig harden’t, zorg dan dat challenge requests niet stuk gaan (zie debugging-sectie).
Aanbevolen headers
Voeg in je HTTPS server block toe (niet in HTTP als je altijd redirect):
Strict-Transport-Security(HSTS) dwingt browsers HTTPS te gebruiken.X-Content-Type-Options: nosniffX-Frame-Options: DENYofSAMEORIGIN(afhankelijk van je app)Referrer-PolicyContent-Security-Policy(CSP) is krachtig maar kan apps breken; voer voorzichtig in.
Voorbeeld (plaats in de server { listen 443 ssl; ... } block):
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# HSTS: pas aan als je zeker weet dat alles via HTTPS werkt
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
Belangrijk: Zet HSTS pas aan als je zeker weet dat HTTPS overal werkt. Met includeSubDomains forceer je ook subdomeinen; dat kan pijnlijk zijn als je ergens nog HTTP gebruikt.
TLS-versies en ciphers
Moderne Nginx/Ubuntu defaults zijn meestal oké. Als je expliciet wil zijn:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
4.6 Automatische vernieuwing testen
Certbot installeert meestal een systemd timer. Check:
systemctl list-timers | grep certbot
systemctl status certbot.timer --no-pager
Test renewal (dry-run):
sudo certbot renew --dry-run
Bekijk logs:
sudo tail -n 200 /var/log/letsencrypt/letsencrypt.log
Benadering B: Alles in Docker (Nginx + Certbot containers)
Deze aanpak is handig als je host zo “clean” mogelijk wil houden. Je draait:
nginxcontainer als reverse proxycertbotcontainer voor aanvragen/vernieuwen- gedeelde volumes voor
/etc/letsencrypten webroot challenge map
Directory structuur
mkdir -p ~/proxy/{nginx,letsencrypt,webroot}
cd ~/proxy
Nginx config (Docker)
Maak ~/proxy/nginx/conf.d/apps.conf:
mkdir -p nginx/conf.d
cat > nginx/conf.d/apps.conf <<'EOF'
# HTTP: serve ACME challenge + redirect naar HTTPS
server {
listen 80;
server_name app1.jouwdomein.nl app2.jouwdomein.nl;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS app1
server {
listen 443 ssl http2;
server_name app1.jouwdomein.nl;
ssl_certificate /etc/letsencrypt/live/app1.jouwdomein.nl/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app1.jouwdomein.nl/privkey.pem;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location / {
proxy_pass http://app1:3000;
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;
}
}
# HTTPS app2
server {
listen 443 ssl http2;
server_name app2.jouwdomein.nl;
ssl_certificate /etc/letsencrypt/live/app2.jouwdomein.nl/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app2.jouwdomein.nl/privkey.pem;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location / {
proxy_pass http://app2:80;
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;
}
}
EOF
Docker Compose voor proxy + apps
Maak docker-compose.yml:
cat > docker-compose.yml <<'EOF'
services:
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./letsencrypt:/etc/letsencrypt
- ./webroot:/var/www/certbot
depends_on:
- app1
- app2
app1:
image: node:20-alpine
command: sh -c "node -e \"require('http').createServer((req,res)=>res.end('app1 ok')).listen(3000)\""
app2:
image: nginx:alpine
volumes:
- ./app2-html:/usr/share/nginx/html:ro
certbot:
image: certbot/certbot:latest
volumes:
- ./letsencrypt:/etc/letsencrypt
- ./webroot:/var/www/certbot
EOF
Start Nginx + apps:
mkdir -p app2-html
echo "app2 ok" > app2-html/index.html
docker compose up -d nginx app1 app2
docker compose ps
Certificaten aanvragen (webroot methode)
Voor app1:
docker compose run --rm certbot certonly \
--webroot -w /var/www/certbot \
-d app1.jouwdomein.nl \
--email jij@jouwdomein.nl --agree-tos --no-eff-email
Voor app2:
docker compose run --rm certbot certonly \
--webroot -w /var/www/certbot \
-d app2.jouwdomein.nl \
--email jij@jouwdomein.nl --agree-tos --no-eff-email
Reload Nginx zodat hij de nieuwe certs pakt:
docker compose exec nginx nginx -t
docker compose exec nginx nginx -s reload
Renewal (bijv. via cron op de host):
docker compose run --rm certbot renew
docker compose exec nginx nginx -s reload
Debugging: systematisch problemen oplossen
HTTPS + reverse proxy issues voelen vaak “mysterieus”, maar je kunt ze bijna altijd herleiden met een vaste volgorde:
- DNS → wijst naar juiste IP?
- Poort 80/443 → bereikbaar vanaf internet?
- Nginx → draait en serveert juiste server_name?
- Upstream → app bereikbaar vanaf Nginx?
- Certificaat → klopt domein/SNI/keten?
- App → vertrouwt proxy headers en proto?
6.1 DNS/poortproblemen
Symptomen
- Certbot:
Timeout during connect (likely firewall problem) - Browser:
ERR_CONNECTION_TIMED_OUT curlhangt
Checks
DNS:
dig +short app1.jouwdomein.nl
Poorten vanaf externe machine:
curl -I http://app1.jouwdomein.nl
Of met nc:
nc -vz app1.jouwdomein.nl 80
nc -vz app1.jouwdomein.nl 443
Op de server: luistert Nginx?
sudo ss -tulpn | grep -E ':80|:443'
Firewall:
sudo ufw status verbose
sudo iptables -S | head
Als je Nginx in Docker draait, check dan of Docker port mapping actief is:
docker ps --format 'table {{.Names}}\t{{.Ports}}'
6.2 Nginx-config fouten en reload issues
Symptomen
502 Bad Gatewayna reload- Nginx start niet
- Certbot kan Nginx niet aanpassen
Checks
Syntax:
sudo nginx -t
Reload en status:
sudo systemctl reload nginx
sudo systemctl status nginx --no-pager
Logs:
sudo tail -n 200 /var/log/nginx/error.log
sudo tail -n 200 /var/log/nginx/app1.error.log
In Docker:
docker compose exec nginx nginx -t
docker compose logs --tail=200 nginx
Veelvoorkomende fout: dubbele listen 443 ssl; blocks met dezelfde server_name of een default server die “vangt” wat je niet verwacht. Check welke config geladen is:
sudo nginx -T | less
6.3 ACME challenge problemen (Let’s Encrypt)
Symptomen
404op/.well-known/acme-challenge/...- Certbot:
Invalid response from http://.../.well-known/acme-challenge/...
Belangrijke concepten
Let’s Encrypt vraagt een URL op zoals:
http://app1.jouwdomein.nl/.well-known/acme-challenge/<token>
Die moet op poort 80 publiek bereikbaar zijn en exact de juiste content teruggeven.
Debug aanpak
Test of Nginx die path correct serveert (webroot methode):
curl -i http://app1.jouwdomein.nl/.well-known/acme-challenge/test
Als je webroot gebruikt, maak tijdelijk een testbestand:
sudo mkdir -p /var/www/certbot/.well-known/acme-challenge
echo OK | sudo tee /var/www/certbot/.well-known/acme-challenge/test
curl -i http://app1.jouwdomein.nl/.well-known/acme-challenge/test
Bij Docker-webroot:
mkdir -p ~/proxy/webroot/.well-known/acme-challenge
echo OK > ~/proxy/webroot/.well-known/acme-challenge/test
curl -i http://app1.jouwdomein.nl/.well-known/acme-challenge/test
Veelvoorkomende oorzaken:
- Je redirect alle HTTP naar HTTPS zonder uitzondering voor
/.well-known/acme-challenge/(webroot). - Je
server_namematcht niet (request komt in “default server” terecht). - DNS wijst nog naar oud IP.
- CDN/proxy (zoals Cloudflare) zit ertussen en blokkeert of cached verkeerd.
6.4 502/504 errors: upstream niet bereikbaar
Symptomen
- Browser:
502 Bad Gateway - Nginx error log:
connect() failed (111: Connection refused) while connecting to upstream - Of
upstream timed out (110: Connection timed out)
Checks
Test upstream vanaf de host (Benadering A):
curl -i http://127.0.0.1:3000
curl -i http://127.0.0.1:8080
Als dit faalt: je app draait niet of luistert op andere poort/interface.
Check containers:
docker compose ps
docker compose logs --tail=200 app1
docker compose logs --tail=200 app2
Als Nginx in Docker draait (Benadering B), test vanuit de Nginx container:
docker compose exec nginx sh -c "apk add --no-cache curl >/dev/null 2>&1 || true; curl -i http://app1:3000"
docker compose exec nginx sh -c "curl -i http://app2:80"
Veelvoorkomend: je proxy_pass wijst naar localhost in een container. In Docker betekent localhost “de container zelf”, niet de host en niet een andere container. Gebruik dan servicenaam (app1:3000) of host.docker.internal (niet overal standaard op Linux).
6.5 Redirect loops en mixed content
Symptomen
- Browser: “Too many redirects”
- App genereert links naar
http://terwijl jehttps://gebruikt - Login cookies werken niet goed
Oorzaak
Veel webapps detecteren het schema (http/https) op basis van headers. Als Nginx TLS terminaat, praat Nginx intern vaak HTTP met de app. Zonder X-Forwarded-Proto: https denkt de app dat het HTTP is en redirect terug naar HTTP of bouwt verkeerde absolute URLs.
Fix
Zorg dat je deze headers zet:
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Soms moet je app expliciet “trust proxy” aanzetten (bijv. Express/Node):
- Express:
app.set('trust proxy', true);
En configureer base URL / canonical URL in je app.
Test redirects:
curl -I http://app1.jouwdomein.nl
curl -I -L http://app1.jouwdomein.nl
curl -I https://app1.jouwdomein.nl
6.6 WebSockets en streaming
Symptomen
- WebSocket connect faalt
- SSE/streaming hapert
- 400/426 errors
Nginx heeft extra headers nodig voor WebSockets:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Plaats dit in de relevante location block.
Ook timeouts kunnen belangrijk zijn:
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
Debug met logs en browser devtools. Test handshake:
curl -i -N \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Host: app1.jouwdomein.nl" \
https://app1.jouwdomein.nl/ws
(De exacte path hangt af van je app.)
6.7 Certificaatketen, SNI en verkeerde certificaten
Symptomen
- Browser toont certificaat voor ander domein
NET::ERR_CERT_COMMON_NAME_INVALIDcurlklaagt over chain
Checks
SNI test (belangrijk bij meerdere domeinen op één IP):
echo | openssl s_client -connect jouwdomein.nl:443 -servername app1.jouwdomein.nl 2>/dev/null | openssl x509 -noout -subject -issuer
echo | openssl s_client -connect jouwdomein.nl:443 -servername app2.jouwdomein.nl 2>/dev/null | openssl x509 -noout -subject -issuer
Controleer dat server_name correct is en dat elke server block de juiste ssl_certificate paden gebruikt.
Let op: als je een “default” server op 443 hebt met een ander certificaat, kan verkeer daar landen als server_name mismatcht.
Checklist: productie-hardening
Gebruik dit als eindcontrole:
Netwerk & exposure
- Alleen Nginx luistert publiek op 80/443
- App-poorten zijn gebonden aan
127.0.0.1(host-Nginx) of alleen intern Docker-netwerk (Docker-Nginx) - Firewall laat alleen noodzakelijke poorten toe
TLS & certificaten
- Certbot renew werkt (
certbot renew --dry-run) - Correcte certificaten per domein (SNI check met openssl)
- TLSv1.2/1.3 actief, geen oude protocollen
Nginx reverse proxy correctheid
-
proxy_set_header Host $host; -
X-Forwarded-Protostaat goed - WebSockets headers indien nodig
- Timeouts passend voor jouw app
Security headers
- HSTS (pas na validatie) en juiste max-age
-
X-Content-Type-Options,Referrer-Policy,X-Frame-Options - CSP alleen als je begrijpt wat je toestaat/blocks
Observability
- Per app aparte access/error logs (of centrale logging)
- Je kunt 502/504 issues herleiden met
curlen logs - Monitoring op certificaatverval (bijv. via cron + mail of monitoringtool)
Extra: snelle “alles-in-één” diagnosecommando’s
Op de server (host-Nginx):
# 1) Nginx status + config
sudo systemctl status nginx --no-pager
sudo nginx -t
# 2) Luisterpoorten
sudo ss -tulpn | grep -E ':80|:443'
# 3) Certbot timers
systemctl list-timers | grep certbot
# 4) Upstreams (lokaal)
curl -i http://127.0.0.1:3000
curl -i http://127.0.0.1:8080
# 5) Externe endpoint (vanaf server zelf, test DNS + routing)
curl -I http://app1.jouwdomein.nl
curl -I https://app1.jouwdomein.nl
Afronding
Je hebt nu een complete setup om Docker-apps veilig via Nginx te publiceren met Let’s Encrypt HTTPS, inclusief een praktische debugging-methodiek voor de meest voorkomende problemen: DNS/poorten, ACME challenges, upstream 502/504, redirect loops en certificaat/SNI issues.
Als je wil, kan ik ook:
- een voorbeeld geven voor wildcard certificaten met DNS-01 (handig voor veel subdomeinen),
- een Nginx-config maken met rate limiting en basic auth,
- of een variant met Traefik (automatische discovery van Docker services).