reverse_proxy is Dwaar’s primary directive. It forwards every matched request to one or more upstream servers, handles load balancing, health checking, connection limiting, and upstream TLS — all without blocking the event loop.
One or more upstream addresses. Required in block form.
lb_policy
round_robin | least_conn | random | ip_hash
round_robin
Load balancing algorithm to use when multiple upstreams are configured.
health_uri
string
"" (disabled)
HTTP path polled on each backend to determine reachability (e.g. /health). Omit to disable health checking.
health_interval
u64 (seconds)
10
Seconds between health probe polls. Applies to all backends in this pool.
fail_duration
u64 (seconds)
0
How long (seconds) to keep a backend marked unhealthy after a probe failure. 0 means re-check immediately on the next interval.
max_conns
u32
unlimited
Maximum concurrent connections per backend. New connections are rejected (502) when the cap is reached. Enforced atomically — no mutex on the hot path.
transport_tls
bool (flag)
false
Connect to the upstream over TLS. Enabled implicitly by any transport { tls ... } subdirective.
transport_h2
bool (flag)
false
Use HTTP/2 multiplexing for upstream connections. All H3 streams share 1-2 H2 connections per host instead of opening one TCP connection per stream. Requires upstream H2 support. See HTTP/2 Upstream.
tls_server_name
string
"" (use IP)
SNI hostname sent during the upstream TLS handshake. Required when the upstream serves multiple virtual hosts over a single IP.
tls_client_auth
(cert_path, key_path)
None
Paths to a client certificate and private key for mutual TLS with the upstream. Both files are loaded and validated at config compile time.
tls_trusted_ca_certs
string
None
Path to a custom CA bundle for verifying the upstream’s server certificate. Use when the backend uses a private CA not in the system trust store.
scale_to_zero
block
None
Wake a sleeping backend on first request instead of returning 502. See Scale to Zero.
max_request_body_size is a global option configured via the handle directive, not on reverse_proxy directly.
When health_uri is set, Dwaar runs a background HealthChecker service that probes every backend on the configured interval.
api.example.com {
reverse_proxy {
to app1:8080 app2:8080
health_uri /health
health_interval 15
fail_duration 60
}
}
How it works:
The checker issues an HTTP GET to http://<backend><health_uri> every health_interval seconds.
A 2xx response marks the backend healthy.
Any non-2xx response or connection error marks the backend unhealthy immediately.
An unhealthy backend is excluded from all load balancing selections.
Once a subsequent probe succeeds, the backend is returned to the pool.
When every backend in a pool is unhealthy, Dwaar returns 502 Bad Gateway to the client.
The fail_duration field controls how long a backend stays marked unhealthy regardless of subsequent probe results. Set it to a value longer than health_interval to prevent flapping.
All three tls_* fields inside transport { } implicitly enable transport_tls. Setting any one of them is enough — you do not need to repeat the bare tls flag.
Use transport h2 to enable HTTP/2 multiplexing for upstream connections. This is especially beneficial when Dwaar serves HTTP/3 (QUIC) traffic — instead of opening one TCP connection per H3 stream, all streams multiplex onto 1-2 shared H2 connections per upstream host.
api.example.com {
reverse_proxy {
to backend:8080
transport h2
}
}
When to use: Your upstream supports HTTP/2 (Go, Node.js, Java, nginx, or any modern server). The benefit scales with concurrency — at 100 concurrent H3 streams, this reduces upstream TCP connections from 100 to 2.
Combining with TLS:
api.example.com {
reverse_proxy {
to secure-backend:443
transport {
tls
h2
tls_server_name api.internal
}
}
}
How it works:
Dwaar maintains 1-2 H2 connections per upstream host (capped to limit GOAWAY blast radius).
Each H3 request stream gets a cloned SendRequest handle — no per-request TCP overhead.
If the upstream sends GOAWAY or the connection dies, Dwaar evicts the dead connection and retries idempotent requests (GET, HEAD, OPTIONS, PUT, DELETE) on a fresh connection.
Non-idempotent requests (POST, PATCH) are not retried to prevent duplicate side effects.
Without transport h2: Dwaar uses HTTP/1.1 upstream connections (one per concurrent stream, pooled for sequential reuse).
scale_to_zero lets Dwaar wake a sleeping backend instead of returning 502. When the upstream is unreachable, Dwaar holds the incoming request, runs wake_command once (coalesced across concurrent requests), polls the backend’s health endpoint, and forwards the request as soon as the backend responds.
myapp.example.com {
reverse_proxy {
to localhost:8080
health_uri /health
scale_to_zero {
wake_timeout 30
wake_command "docker start myapp"
}
}
}
Field
Default
Description
wake_timeout
30
Seconds to wait for the backend to become reachable before giving up with 502.
wake_command
—
Command to execute to start the backend. Must be an absolute path. Run once per unreachable event; concurrent requests wait on the same wake attempt.
health_uri is required for scale-to-zero to detect when the backend is ready. Without it, Dwaar cannot know when to forward the held request.
Breaking change:wake_command must now begin with /. A relative path (e.g. docker start myapp) is rejected at runtime with a WakeError::CommandPathNotAbsolute error and a warning log — the held request receives a 502. The command is executed directly via Command::new with arguments split on whitespace; it is not passed to a shell, so shell metacharacters, pipes, and redirects are not interpreted. Use a wrapper script at an absolute path if shell features are required: