Deploy and operate
Documentation

Version Latest

Client IP addresses and proxies

Configure which reverse proxies and CDN edges Govern believes, so rate limits, lockouts, risk scoring and audit entries use the real client address.

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

VariableDefaultMeaning
NI_AUTH_TRUSTED_PROXIESemptyYour 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_PROXIESemptyThe edge ranges of a CDN in front of the proxies: CIDRs or IPs, and the preset cloudflare
NI_AUTH_CLIENT_IP_HEADERemptyThe 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_HEADERemptyThe 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

  1. If the direct peer is neither a trusted proxy nor a CDN edge, it is the client. Headers are ignored.
  2. Otherwise X-Forwarded-For is 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.
  3. Whenever the walk is at a CDN edge and NI_AUTH_CLIENT_IP_HEADER is 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.

Last reviewed 17 October 2026View as Markdown

Client IP addresses and proxies · Docs · Neural Inverse