Caddy reverse proxy configuration, Caddyfile syntax, automatic HTTPS, Docker integration, and common patterns
54
59%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./caddy-reverse-proxy/SKILL.mdCaddy is a modern web server with automatic HTTPS. This skill covers Caddyfile configuration, reverse proxy patterns, and Docker deployment.
# Reverse proxy to a backend service
app.example.com {
reverse_proxy localhost:8080
}
# Multiple domains, same backend
app1.example.com, app2.example.com {
reverse_proxy backend:3000
}
# Wildcard with on-demand TLS
*.example.com {
tls {
on_demand
}
reverse_proxy localhost:8080
}service.example.com {
reverse_proxy service:8080
}example.com {
handle /api/* {
reverse_proxy api-server:3000
}
handle {
reverse_proxy frontend:80
}
}Caddy handles WebSocket upgrades automatically. No extra config needed.
app.example.com {
reverse_proxy node1:8080 node2:8080 node3:8080 {
lb_policy round_robin
health_uri /health
health_interval 10s
}
}service.example.com {
reverse_proxy backend:8080 {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}Caddy gets certificates automatically. Just use a domain name:
app.example.com {
reverse_proxy localhost:8080
}For wildcard certs or when port 80/443 isn't publicly reachable. Requires the caddy-cloudflare build.
*.example.com {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
@app1 host app1.example.com
handle @app1 {
reverse_proxy app1:8080
}
}:80 {
reverse_proxy localhost:8080
}
# Or with self-signed cert
service.lan {
tls internal
reverse_proxy localhost:8080
}service.example.com {
@denied not remote_ip 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12
respond @denied 403
reverse_proxy backend:8080
}*.example.com {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
# Named matchers for each service
@grafana host grafana.example.com
handle @grafana {
reverse_proxy grafana:3000
}
@n8n host n8n.example.com
handle @n8n {
reverse_proxy n8n:5678
}
# Fallback for unmatched subdomains
handle {
respond "Nothing here" 404
}
}admin.example.com {
basicauth {
# Generate hash: caddy hash-password
admin $2a$14$HASHED_PASSWORD_HERE
}
reverse_proxy backend:8080
}files.example.com {
file_server browse {
root /srv/files
}
}Caddy does this by default. To redirect a domain:
old.example.com {
redir https://new.example.com{uri} permanent
}services:
caddy:
image: caddy:2-alpine
# Or for Cloudflare DNS: caddybuilds/caddy-cloudflare
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
environment:
- CLOUDFLARE_API_TOKEN=your-token # if using DNS challenge
restart: unless-stopped
volumes:
caddy_data:
caddy_config:Services on the same Docker network are reachable by container name:
app.example.com {
reverse_proxy container-name:8080
}For services on different networks, use the host IP or join networks:
services:
caddy:
networks:
- frontend
- backend# Validate config
caddy validate --config /etc/caddy/Caddyfile
# Reload without downtime (inside container)
docker exec <caddy-container> caddy reload --config /etc/caddy/Caddyfile
# Adapt Caddyfile to JSON (debugging)
caddy adapt --config /etc/caddy/Caddyfile
# View loaded config
caddy config
# Hash a password for basicauth
caddy hash-passwordUse {env.VAR_NAME} in Caddyfile:
service.example.com {
reverse_proxy {env.BACKEND_HOST}:{env.BACKEND_PORT}
}service.example.com {
log {
output file /var/log/caddy/access.log
format json
level INFO
}
reverse_proxy backend:8080
}| Problem | Fix |
|---|---|
| Certificate not issuing | Check DNS points to Caddy, ports 80/443 open, or use DNS challenge |
| 502 Bad Gateway | Backend not running or wrong hostname/port. Check docker network ls |
| Config not reloading | Use caddy reload, not restart (avoids downtime). Check caddy validate first |
| Wildcard cert failing | Requires DNS challenge plugin (e.g. caddy-cloudflare), not default image |
| WebSocket disconnecting | Usually a proxy timeout. Add transport http { keepalive 30s } |
| LAN service reachable externally | Add remote_ip matcher to restrict access |
| nginx | Caddy |
|---|---|
proxy_pass http://backend; | reverse_proxy backend:8080 |
ssl_certificate /path/cert.pem; | Automatic (just use domain name) |
location /api { ... } | handle /api/* { ... } |
nginx -t && nginx -s reload | caddy validate && caddy reload |
| Separate config for HTTPS redirect | Automatic |
808c382
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.