None of these needs a second proxy or a container restart. A change is stored, audited, and applied on the next ingress reconcile pass (normally within a second). Deleting a section restores the default behavior for that domain.
Where to find it
- Dashboard
- CLI
- API
On the Domains page, click a domain name. The domain page has tabs for Overview, Headers, Forwarding, Access, Cache, Redirects, Ports and Danger zone. Each editor keeps an unsaved draft per tab, shows the difference before you save, and a “rules preview” lists in order what happens to a sample request.
Order of evaluation
For a request to a domain with controls configured, the ingress runs, in order:- Redirects (lower case host, force HTTPS, trailing slash)
- Geo rule
- Headers: request rules, CORS, then response rules and presets
- The existing WAF, rate limit and basic auth, if configured
- Cache lookup
- Forwarders, first match wins
- The domain’s own app
Headers
Rules are evaluated in order. Each rule has a side (request or response), an operation (set, add or remove), a name, and (except for remove) a value.
- Request rules change what the app receives.
- Response rules apply after the app answers, so they override the app’s own value. A later rule overrides an earlier one, and any rule overrides a preset.
- Header names must be valid RFC 9110 tokens. Values cannot contain line breaks or control characters, and are capped by
APP_DOMAIN_POLICY_MAX_HEADER_VALUE_LEN. Operator text is never expanded as a Caddy placeholder, so a value like{env.SECRET}is sent literally. - These names are reserved by the ingress and refused:
Host,Content-Length,Transfer-Encoding,Connection,Keep-Alive,Upgrade,TE,Trailer,Proxy-Connection,Forwarded,X-Real-IP,Proxy-Authorizationand everyX-Forwarded-*header.X-Forwarded-Prefixhas its own option,forwarded_prefix.X-CacheandCache-Statusare reserved for the cache.
Presets
HSTS is only accepted when the domain has a publicly trusted certificate: ACME enabled, an uploaded certificate, or TLS terminated by your own proxy. Sending HSTS with a self-signed certificate would lock visitors out, so the API refuses it. When the ingress terminates TLS, HSTS is only sent on HTTPS responses.
CORS echoes the request’s
Origin back only when it is in the list, and adds Vary: Origin. * allows any origin but cannot be combined with credentials.
Path forwarders
A forwarder sends requests that match a path somewhere other than the domain’s app. Rules are checked in order, the first match wins, and a request no rule matches goes to the app as usual.
An
app target uses the same upstream the app’s own domain routes to; if that app is not running, the rule answers 502 with a plain explanation rather than falling back to the default app.
Outbound safety
An external URL is resolved and every address it resolves to is checked, both when you save and again on every ingress pass (cached for a minute), so a DNS record changed later to point at an internal address is caught. Refused targets:- loopback, unless
APP_FORWARDER_ALLOW_LOOPBACK=true - private and carrier grade NAT ranges, unless
APP_FORWARDER_ALLOW_PRIVATE_NETWORKS=true - link-local and cloud metadata addresses (
169.254.0.0/16,fe80::/10,fd00:ec2::/32,100.100.100.200), always
502.
A forwarder to an external URL whose host is the domain itself is refused, and so is a redirect to a path the same rule would match again.
Geolocation
A geo rule is an allow list (only these countries) or a deny list (everyone but these) of ISO 3166-1 alpha-2 codes, plus what to do with a blocked visitor: a plain403 (or 404, 451), a redirect, or your own HTML page.
- Private, loopback and link-local addresses are never blocked by country.
- Addresses and ranges in
exemptalways pass. unknowndecides requests whose country cannot be determined:allow(default) orblock.
Country sources
The ingress needs a country source. Without one, geo rules are saved but inactive, and the dashboard andGET .../policies say so.
APP_GEOIP_SOURCE picks auto (default: the header when trusted proxies are configured, the database when it loads, header first), header, mmdb or none.
Two databases work out of the box:
- MaxMind GeoLite2 Country: free, but needs a MaxMind account and licence key to download, and its EULA applies (including keeping the file updated).
- DB-IP IP to Country Lite: free under CC BY 4.0, no account. Attribution is required wherever you show the data, for example “IP geolocation by DB-IP”.
oschwald/maxminddb-golang (ISC licence).
Check what a lookup returns:
Caching
Caching is per domain and off by default. Each rule matches a path (prefix, exact or regex) and says how long to keep a response.- Only
GETandHEADare cached; aHEADcan be answered from a storedGET. - Never cached: requests with
Authorization, requests with aCookie(unless the rule setscache_with_cookies), range requests, upgrades, andCache-Control: no-storerequests. Responses withSet-Cookie,Cache-Control: private,no-storeorno-cache,Vary: *, aVaryon a header the rule does not list, a partial content range, or a status outside the rule’s list (default200). - TTL: the app’s
s-maxage, thenmax-age, then the rule’sttl_seconds. Withoverride_upstream, the rule’s TTL always wins. A TTL of 0 means “do not store”. TTLs are capped byAPP_INGRESS_CACHE_MAX_TTL. - Stale while revalidate: after an entry expires, the first request fetches a fresh copy from the app, and requests arriving while that fetch is running get the expired copy, marked
STALE, for up tostale_while_revalidate_seconds. - Key: the request host, path and query,
Accept-Encoding, and the rule’svaryheaders. Entries from one host are never served for another. - Every response on a cached path carries
X-Cache: HIT,MISS,STALEorBYPASS, and a hit has anAgeheader. A hit answersIf-None-Matchwith304when the ETag matches.
APP_INGRESS_CACHE_MAX_BYTES (default 64 MiB) across every domain, with single responses capped by APP_INGRESS_CACHE_MAX_OBJECT_BYTES (default 2 MiB) or the domain’s own lower max_object_bytes. It is per instance, starts empty after a restart, and is not shared between nodes.
Redirects
Whole-domain redirects (one host to a target URL) still use domain redirects; the controls here build on them.Force HTTPS
How plain HTTP is handled depends on where TLS ends, and the Redirects tab says which case applies:
The redirect goes to the public HTTPS port (
public_https_port), so an internal port such as :8443 never leaks into a URL. The status is 308 by default, which keeps the method and body, or 301.
www and apex
Three presets: Redirect www to apex, Redirect apex to www, or Serve both. Choosing a redirect preset:- Adds the other host to the app if it is missing (this part needs the
writeability, like adding any domain), and shows the DNS record to create for it. - Installs a redirect from the non canonical host to the canonical one that keeps path and query.
replace to overwrite). A host in maintenance mode keeps answering 503 before any redirect, and the page warns about it.
Aliases
Pick one of the app’s domains as the primary and mark others as aliases: each alias redirects to the primary with path and query kept, using301, 302, 307 or 308. Domains you unmark stop redirecting to the primary.
Normalisation
- Trailing slash:
add(paths without a file extension get a slash),remove, oroff(default). Uses308. - Lower case host: redirects
Example.COMtoexample.com. Off by default.
http://www.example.com/a?x=1, http://example.com/a and https://www.example.com/a through your settings and shows each hop with its status code before you save.
Ports
Raw TCP and UDP port forwards are app streams (raw TCP streams); the Ports tab anddomains ports show list them for the domain’s app with their target, who may reach them, and conflicts: another stream on the same port, the HTTP ingress port, or a container that already publishes the port.
“Open this port only to these IPs” writes firewall rules: one allow rule per address or range, then a deny for everyone else, all labelled domain-ports:PORT/PROTO. Removing the restriction deletes exactly those rules.
Limits
Every cap has a default and an environment override on the control plane.Permissions and audit
Every change is recorded in the audit log like any other API write.
Next steps
Domains and ingress
TLS, WAF, maintenance mode and the rest of the ingress.
Load balancing
Balance traffic across an app’s replicas.