Caddy is not a line-for-line Nginx replacement. Identify the matcher, handler, and response behavior in each Nginx block, then express those things in Caddy directive order.
Translate location blocks
# Nginx
location /api/ { proxy_pass http://backend:8080/; }
# Caddy
handle /api/* {
uri strip_prefix /api
reverse_proxy backend:8080
}A prefix location becomes a path matcher. A broad location / is usually a fallback handler. Keep narrow routes explicit.
proxy_pass and path prefixes
In Nginx, the trailing slash controls whether the matched location prefix is replaced. Caddy forwards the request path by default, so use uri strip_prefix when the upstream expects a different path.
Redirects and headers
# Nginx
return 301 https://$host$request_uri;
proxy_set_header Host $host;
add_header Cache-Control "public, max-age=3600";
# Caddy
redir https://{host}{uri} permanent
reverse_proxy app:3000 {
header_up Host {host}
}
// phpcs:enable Generic.Files.LineLength.TooLong
header Cache-Control "public, max-age=3600"header_up changes what the application receives; header changes what the browser receives. Check both with a real request.
Migration checklist
- Run
caddy validate --config Caddyfile. - Test prefixed routes and upstream URIs.
- Compare redirects and query strings.
- Inspect response and upstream request headers.
- Test missing files, slash variants, OPTIONS, and 404s.
READY TO CHECK?
Turn the guide into evidence.
Use the StackHal tool connected to this field note to inspect your own configuration.
StackHal Field Notes: Practical explainers for developer infrastructure.