Migrating from Caddy
Dwaar uses Caddyfile-compatible syntax. Most Caddyfiles work with minimal changes: rename the file to Dwaarfile, swap a few directive names, and you’re done. This page documents every point where Dwaar behaves differently from Caddy, and shows the exact config translation for the most common patterns.
Key Differences
Section titled “Key Differences”| Feature | Caddy | Dwaar |
|---|---|---|
| Config file name | Caddyfile | Dwaarfile |
| Primary proxy directive | reverse_proxy | reverse_proxy (identical) |
| Simple proxy shorthand | proxy (deprecated in v2) | proxy accepted as an alias |
| Compression directive | encode gzip zstd br | encode gzip zstd br (identical) |
| TLS auto-provision | tls with no args, or implicit | Implicit by default; tls auto also works |
| TLS manual cert | tls /cert /key | tls manual + tls_cert / tls_key |
| Basic auth directive | basicauth | basic_auth (underscore preferred; basicauth is an alias) |
| Security headers | None by default | Applied automatically to every response |
| Built-in analytics | No | analytics on |
| Rate limiting | crowdsec or third-party module | rate_limit 100/s (built in) |
| IP filtering | Third-party module | ip_filter (built in) |
| Plugin system | Go modules | WASM plugins |
respond directive | Supported | Supported (identical) |
rewrite directive | Supported | Supported (identical) |
redir directive | 301 default | 308 default (method-preserving) |
| HTTP/3 | Built-in | servers { h3 on } in global block |
| Admin API | REST at :2019 | REST at :9876 |
| Module ecosystem | Hundreds of Go modules | WASM plugins; native modules compiled in |
Config Translations
Section titled “Config Translations”Basic reverse proxy
Section titled “Basic reverse proxy”Before (Caddy)
example.com { reverse_proxy localhost:8080}After (Dwaar)
example.com { reverse_proxy localhost:8080}Identical. No changes required.
Multi-upstream with load balancing
Section titled “Multi-upstream with load balancing”Before (Caddy)
example.com { reverse_proxy backend1:8080 backend2:8080 backend3:8080 { lb_policy round_robin health_uri /health health_interval 10s }}After (Dwaar)
example.com { reverse_proxy { to backend1:8080 backend2:8080 backend3:8080 lb_policy round_robin health_uri /health health_interval 10 }}Two changes:
- Multi-upstream must use block form with a
tosubdirective. health_intervaltakes an integer (seconds), not a duration string.
TLS with ACME
Section titled “TLS with ACME”Before (Caddy)
{ email admin@example.com}
example.com { reverse_proxy localhost:3000}After (Dwaar)
{ email admin@example.com}
example.com { reverse_proxy localhost:3000}Identical. Automatic HTTPS is on by default in both.
Manual certs — before (Caddy)
example.com { reverse_proxy localhost:3000 tls /etc/ssl/cert.pem /etc/ssl/key.pem}Manual certs — after (Dwaar)
example.com { reverse_proxy localhost:3000 tls manual tls_cert /etc/ssl/cert.pem tls_key /etc/ssl/key.pem}Dwaar uses explicit tls manual with separate tls_cert / tls_key directives instead of inline paths.
File server with SPA fallback
Section titled “File server with SPA fallback”Before (Caddy)
example.com { root * /var/www try_files {path} /index.html file_server}After (Dwaar)
example.com { root * /var/www try_files {path} /index.html file_server}Identical. try_files, root, and file_server behave the same way.
Rate limiting
Section titled “Rate limiting”Before (Caddy)
example.com { reverse_proxy localhost:3000 # Requires external module, e.g. caddy-ratelimit rate_limit { zone static { key {remote_host} events 100 window 1s } }}After (Dwaar)
example.com { reverse_proxy localhost:3000 rate_limit 100/s}Rate limiting is built into Dwaar. Use rate_limit <n>/s — no module required.
Headers
Section titled “Headers”Before (Caddy)
example.com { reverse_proxy localhost:3000 header { X-Custom-Header "my-value" Cache-Control "public, max-age=3600" -Server }}After (Dwaar)
example.com { reverse_proxy localhost:3000 header { X-Custom-Header "my-value" Cache-Control "public, max-age=3600" }}The header block syntax is identical for adding and overriding headers. Header deletion with -Header-Name is not yet supported — the upstream Server header is replaced automatically with Dwaar by the built-in security headers plugin.
Redirects
Section titled “Redirects”Before (Caddy)
example.com { redir /old-path /new-path 301 redir /blog/* /articles/{http.request.uri.path.remainder} 301}After (Dwaar)
example.com { redir /old-path /new-path 301 redir /blog/* /articles/{http.request.uri.path.remainder} 301}Identical syntax. Note that Dwaar’s default redirect code is 308 (not 301) when no code is specified — explicitly pass 301 when you need it.
Handle and handle_path blocks
Section titled “Handle and handle_path blocks”Before (Caddy)
example.com { handle /api/* { reverse_proxy localhost:8080 }
handle_path /static/* { root * /var/www/assets file_server }
handle { file_server /var/www }}After (Dwaar)
example.com { handle /api/* { reverse_proxy localhost:8080 }
handle_path /static/* { root * /var/www/assets file_server }
handle { root * /var/www file_server }}handle and handle_path are identical in semantics. The only change: file_server does not accept a path as a positional argument in Dwaar — set the root with the root directive instead.
Encode (compression)
Section titled “Encode (compression)”Before (Caddy)
example.com { reverse_proxy localhost:3000 encode gzip zstd br}After (Dwaar)
example.com { reverse_proxy localhost:3000 encode gzip zstd br}Identical. Compression is also on by default in Dwaar without any encode directive — add encode only when you need to control which algorithms are offered.
Basic auth
Section titled “Basic auth”Before (Caddy)
example.com { basicauth /admin/* { alice JDJhJDE0JGV... } reverse_proxy localhost:3000}After (Dwaar)
example.com { handle /admin/* { basic_auth { alice $2b$14$... } reverse_proxy localhost:3000 }
handle { reverse_proxy localhost:3000 }}Two changes:
- Use
basic_auth(underscore) orbasicauth— both are accepted. - Dwaar does not support a path argument directly on
basic_auth. Scope it to a path using ahandleblock instead. - Hashes must start with
$2b$or$2y$. Generate withhtpasswd -nbBC 12 alice password.
Matchers
Section titled “Matchers”Before (Caddy)
example.com { @api { path /api/* method GET POST PUT DELETE }
@static { path *.css *.js *.png *.jpg *.svg }
handle @api { reverse_proxy localhost:8080 }
handle @static { root * /var/www file_server }}After (Dwaar)
example.com { @api { path /api/* method GET POST PUT DELETE }
@static { path *.css *.js *.png *.jpg *.svg }
handle @api { reverse_proxy localhost:8080 }
handle @static { root * /var/www file_server }}Named matchers are identical in syntax. All Caddy matcher conditions (path, method, host, header, remote_ip, not, etc.) are supported.
Unsupported Caddy Features
Section titled “Unsupported Caddy Features”The following Caddyfile features are not yet supported in Dwaar. Dwaar parses and ignores unknown directives (it does not error on them), so existing configs will load — but the behaviour will be absent.
| Feature | Caddy directive | Status |
|---|---|---|
| DNS-01 challenge provider config | tls { dns ... } | Planned |
| Header deletion | header -Header-Name | Planned |
| Request body rewriting | request_body | Not planned |
| Logging filter expressions | log { filter ... } | Planned |
push (HTTP/2 server push) | push | Not planned (deprecated in browsers) |
acme_server (internal CA) | acme_server | Not planned |
Pki app (pki { ... }) | Caddyfile pki block | Not planned |
| Syslog output | log { output net ... } | Planned |
vars directive | vars | Not planned |
import / snippets | import snippet_name | Planned |
| Multiple sites on one address with path routing | example.com/path { ... } | Not supported |
Migration Steps
Section titled “Migration Steps”-
Copy your
CaddyfiletoDwaarfilein the same directory. -
Run the validator to see what needs to change:
Terminal window dwaar validate --config Dwaarfile -
If you use
reverse_proxywith multiple backends and options, convert to block form withto:reverse_proxy {to backend1:8080 backend2:8080lb_policy least_conn} -
Replace any
tls /cert /keylines with:tls manualtls_cert /certtls_key /key -
Replace third-party rate-limiting modules with
rate_limit <n>/s. -
Replace
file_server /path(path as positional arg) withroot * /path+file_server. -
Replace
basicauth /path/* { ... }with ahandle /path/* { basic_auth { ... } ... }block. -
Start Dwaar and watch logs:
Terminal window dwaar run --config Dwaarfile -
Verify TLS, routing, and response headers with
curl -I https://yourdomain.com.
Related
Section titled “Related”- Dwaarfile Reference — complete directive reference
- Comparison with Other Proxies — feature matrix
- Automatic HTTPS — how ACME provisioning works
- Rate Limiting — built-in sliding-window rate limiter