Govern attributes every request to a client address. It is used for per-address rate limits and lockouts, risk scoring, audit entries and the address shown for each sign-in session. A wrong address either lets an attacker dodge the limits or makes every user share one bucket, so forwarding headers are believed only from infrastructure you name.
Settings
| Variable | Default | Meaning |
|---|---|---|
NI_AUTH_TRUSTED_PROXIES | empty | Your reverse proxies, as CIDRs or IPs, for example 172.16.0.0/12,127.0.0.1,::1 for Caddy reaching Govern through the Docker gateway. Only a request whose direct peer is one of them may name the client through X-Forwarded-For (read from the right) or X-Real-IP |
NI_AUTH_TRUSTED_CDN_PROXIES | empty | The edge ranges of a CDN in front of the proxies: CIDRs or IPs, and the preset cloudflare |
NI_AUTH_CLIENT_IP_HEADER | empty | The header the CDN sets to the client address, for example CF-Connecting-IP. Believed only from a CDN edge and only when it holds exactly one address. Setting it without CDN ranges stops the server. X-Forwarded-For, Forwarded and Host are refused as values |
NI_AUTH_CLIENT_COUNTRY_HEADER | empty | The CDN's country header (CF-IPCountry, CloudFront-Viewer-Country), used only for the recommended region of the region picker and believed only from the CDN ranges |
A catch-all range (0.0.0.0/0, ::/0) is refused in both lists.
How the address is chosen
- If the direct peer is neither a trusted proxy nor a CDN edge, it is the client. Headers are ignored.
- Otherwise
X-Forwarded-Foris walked from the right. Each hop added by a trusted proxy or CDN edge is skipped; the first other address is the client. A malformed hop stops the walk and the direct peer is used. - Whenever the walk is at a CDN edge and
NI_AUTH_CLIENT_IP_HEADERis set, that header's single address is the client.
So a client that reaches your origin directly with a forged CF-Connecting-IP is still attributed to its own address, and forged entries at the left of X-Forwarded-For are never reached.
Examples
One reverse proxy (Caddy, nginx, a load balancer) in front of the server:
NI_AUTH_TRUSTED_PROXIES=10.0.1.10
Cloudflare, then Caddy:
NI_AUTH_TRUSTED_PROXIES=172.16.0.0/12,127.0.0.1,::1
NI_AUTH_TRUSTED_CDN_PROXIES=cloudflare
NI_AUTH_CLIENT_IP_HEADER=CF-Connecting-IP
Caddy must pass CF-Connecting-IP through unchanged and append the Cloudflare edge to X-Forwarded-For (its default reverse_proxy behaviour). For defence in depth, also restrict the origin's firewall to Cloudflare's ranges.
The Cloudflare preset
cloudflare is a snapshot compiled into the release, taken from Cloudflare's published lists on 2026-10-09. Govern never downloads it at runtime.
IPv4: 173.245.48.0/20, 103.21.244.0/22, 103.22.200.0/22, 103.31.4.0/22, 141.101.64.0/18, 108.162.192.0/18, 190.93.240.0/20, 188.114.96.0/20, 197.234.240.0/22, 198.41.128.0/17, 162.158.0.0/15, 104.16.0.0/13, 104.24.0.0/14, 172.64.0.0/13, 131.0.72.0/22.
IPv6: 2400:cb00::/32, 2606:4700::/32, 2803:f800::/32, 2405:b500::/32, 2405:8100::/32, 2a06:98c0::/29, 2c0f:f248::/32.
If Cloudflare adds a range before your next upgrade, list it next to the preset (NI_AUTH_TRUSTED_CDN_PROXIES=cloudflare,192.0.2.0/24). If it withdraws one, list the remaining ranges explicitly instead of the preset.
Requests from the console
The console calls Govern from its own server, so the connection always names the console host. The console forwards the browser's address in a signed header, X-Keystone-Client-Context, which Govern believes only when a key in NI_AUTH_CONSOLE_CONTEXT_KEYS (<key id>:<base64 Ed25519 public key>, comma-separated) verifies it; from anyone else it is refused. The address recorded is then the browser's, or none when the console could not tell it, never the console host's. Set the keys whenever you deploy the console.
Sign-in sessions
Sessions record the client address and user agent. For an OAuth sign-in they are those of the browser that completed the authorisation, not of the client that exchanged the code, because a confidential client such as the console exchanges and refreshes tokens from its own server.
Streaming responses
Govern serves long-lived Server-Sent Events streams (the Context feed, among others). Your proxy must not buffer them or time them out sooner than their keep-alive: the server sets X-Accel-Buffering: no; raise the proxy's read timeout above 30 seconds.