Server Hardening
Production-safe defaults for handling untrusted input at the request edge: trust-proxy gating for X-Forwarded-* headers, and a per-request body-size cap to prevent memory-exhaustion DoS.
Trust Proxy
Soli reads X-Forwarded-Proto and X-Forwarded-Host from incoming requests only when trust-proxy is enabled. These headers govern two security-sensitive decisions:
- The
Secureflag on the session cookie (set when the request scheme ishttps). - The host portion of
*_urlnamed-route helpers used for absolute URLs in emails and redirects.
A request reaching the Soli process directly cannot influence either decision via these headers — the cookie Secure flag stays off and *_url falls back to the Host header. Enable trust-proxy only when your deployment terminates TLS at a trusted proxy hop (Caddy, nginx, ALB, etc.) and that proxy is configured to strip inbound X-Forwarded-* headers from clients before adding its own.
enable_trust_proxy() / disable_trust_proxy() / trust_proxy_enabled()
Toggle and inspect the trust-proxy gate. All three return Bool.
# config/application.sl
enable_trust_proxy
# Later, e.g. in a test harness running without a proxy:
disable_trust_proxy
# Inspect:
if trust_proxy_enabled
println("X-Forwarded-* headers are honored")
end
You can also set the startup default from the environment — useful when the same image runs in different deployments:
# .env.production
SOLI_TRUST_PROXY=1
Truthy values: 1, true, yes (case-insensitive). The function calls still override the env-driven default at runtime.
Request Body Limit
Every non-GET/HEAD request is capped before its body is buffered into memory. Without this cap an attacker can stream an arbitrarily large body — or open many concurrent uploads — and exhaust worker memory before any handler runs.
The server short-circuits to 413 Payload Too Large in two ways: via the Content-Length header (no bytes read) or mid-stream once the running total crosses the limit (catches chunked uploads that don't declare a length).
Apps that accept larger uploads — document upload, image processing, etc. — should raise the cap explicitly. Prefer per-action checks inside the handler over a high global cap so abusive clients can't open many concurrent uploads on routes that shouldn't accept them.
set_max_body_size(bytes)
Sets the maximum buffered request body size, in bytes.
bytes(Int) - New limit in bytes (must be non-negative)
# config/application.sl
set_max_body_size(32 * 1024 * 1024) # 32 MiB cap for file uploads
# Returns Int — the value just set.
The startup default can also come from the environment:
# .env.production
SOLI_MAX_BODY_SIZE=33554432 # 32 MiB, in bytes
Non-numeric or negative values are ignored and the 8 MiB default stands. The function call still overrides at runtime.
max_body_size()
Returns the current limit in bytes.
println("Current cap: " + str(max_body_size) + " bytes")
Related: Security Headers
Production hardening also includes the response-side defaults — Content-Security-Policy, HSTS, X-Frame-Options, and friends — covered on the dedicated reference page.