Caddy reverse proxy — patterns które realnie używam
Caddy jest moją domyślną front-line proxy w homelabie. Pokazuję 5 wzorców konfiguracji które mi się sprawdzają — od basic reverse proxy po zaawansowane HSTS/HSTS preload + auth gating.

Nginx, Traefik, HAProxy, każdy ma swoich fanów. Ja używam Caddy. Cztery powody: automatyczne TLS, czytelny config, HTTP/3 out-of-box, brak komplikacji którymi zarządza się 2% userów. Pokażę 5 wzorców z mojego setupu.
Pattern #1: basic reverse proxy z TLS
Najprostsze co kiedykolwiek napisałem:
vikunja.kamilkaletka.dev {
reverse_proxy localhost:3456
}To wszystko. Caddy automatycznie:
- Pobiera cert Let's Encrypt
- Auto-renewuje co 60 dni
- Forwardpuje HTTP do HTTPS
- Włącza HTTP/2 i HTTP/3
Porównaj z nginx-em: 30 linii configu, certbot timer, manual renew handling.
Pattern #2: header rewriting + WebSocket
Niektóre apki potrzebują custom headerów albo WebSocket support:
chat.kamilkaletka.dev {
reverse_proxy localhost:8080 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}Caddy wykrywa WebSocket sam, nie trzeba proxy_pass http://upstream/upgrade websocket jak w nginx. WS upgrade działa po prostu.
Pattern #3: path-based routing
Jedna domena, wiele backend'ów:
api.kamilkaletka.dev {
handle_path /v1/* {
reverse_proxy localhost:3001
}
handle_path /v2/* {
reverse_proxy localhost:3002
}
handle_path /admin/* {
reverse_proxy localhost:3003
}
handle {
respond "Not Found" 404
}
}handle_path strip'uje prefix przed forward'em (jak nginx rewrite ... break). handle to else branch.
Pattern #4: forward auth (gating)
Gdy chcę dodać autoryzację przed serwisem który nie ma własnej:
admin.kamilkaletka.dev {
forward_auth localhost:9000 {
uri /verify
copy_headers X-User-Email X-User-Roles
}
reverse_proxy localhost:3000
}Każdy request idzie najpierw do auth serwera (mam Authentik na 9000). Jeśli ten zwróci 200 → request leci do app. Jeśli 401/403 → Caddy zwraca błąd, app nie widzi requestu.
Dodaję headers X-User-Email żeby aplikacja wiedziała kto jest zalogowany.
Pattern #5: HSTS + security headers
Production-ready security headers:
(security_headers) {
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
Permissions-Policy "geolocation=(), microphone=(), camera=()"
# Remove server headers
-Server
}
}
vikunja.kamilkaletka.dev {
import security_headers
reverse_proxy localhost:3456
}
obsidian-sync.kamilkaletka.dev {
import security_headers
reverse_proxy localhost:5984
}Definiuję snippet raz, importuję wszędzie. Każda domena dostaje te same security headers bez powtarzania.
Pułapka: bind mount + reload
Najboleśniejsza pułapka której się nauczyłem.
Caddy w Dockerze, bind mount /etc/caddy/Caddyfile:
volumes:
- ./Caddyfile:/etc/caddy/CaddyfilePo edycji Caddyfile robisz:
# NIE DZIAŁA czasami
docker exec caddy caddy reload --config /etc/caddy/Caddyfile
# DZIAŁA ZAWSZE
docker restart caddyPowód: bind mount per-inode. Jak edytujesz plik na hoście (vim, większość edytorów robi temp+rename), Caddy w kontenerze widzi STARY plik bo pamięta ten inode. Restart kontenera = nowy mount = nowy inode.
Alternatywa: edytuj przez docker exec caddy vi, ale to ohydne.
Pattern bonus: rate limiting
Caddy ma plugin caddy-ratelimit:
api.kamilkaletka.dev {
rate_limit {
zone api_zone {
key {remote_host}
events 60
window 1m
}
}
reverse_proxy localhost:3001
}60 requestów na minutę z jednego IP. Powyżej → 429. Plugin wymaga build customowego Caddy (z xcaddy), ale ~5 minut roboty.
Czego Caddy NIE robi dobrze
1. Bardzo skomplikowane regex routing. Nginx ma więcej narzędzi.
2. Module-based plugin system jest słabszy. Plugins idą do binarki, nie load'ują dynamicznie. Każdy plugin = recompile.
3. Performance pod ekstremalnym load'em. Dla > 10k req/s nginx wciąż wygrywa. Dla normalnego homelabu (max 100 req/s) nie ma znaczenia.
Caddy zamienia 30-liniowe nginx'y na 3-liniowe entries. Auto TLS to feature który dla mnie sam zwracał całe migration przez tydzień. Jeśli jeszcze siedzisz na nginx + certbot, daj sobie weekend i przerzuć się.