Reverse proxies
Tell Chat which proxies to trust so sign-in limits apply per client, not to everyone at once.
2 min readUpdated for Chat 0.5.2Edit on GitHub
/api/admin/health, field clientSign-in limits are kept per client address. Behind a reverse proxy, Chat sees the proxy as the peer, so every person shares one limit until you trust the proxy.
Trust your proxy
Set OBLETH_CHAT_TRUSTED_PROXY to your proxies' addresses or CIDR ranges, comma-separated. loopback covers 127.0.0.0/8 and ::1, for a proxy on the same host.
Chat then reads X-Forwarded-For, but only from trusted peers. It walks the header from the nearest hop and stops at the first untrusted or malformed entry.
A trusted address can claim to be any client. Trust only your proxies, never a range that also holds clients, such as a whole LAN. The proxy must replace or append to X-Forwarded-For, not pass a client's value through unchecked.
| Deployment | Setting | Notes |
|---|---|---|
| Binary behind nginx or Caddy on the same host | loopback | nginx: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;. Caddy sets the header by default |
| Compose, with a proxy on the host | The Compose network's gateway, such as 172.18.0.1 | Docker forwards the published port from its bridge, so Chat sees the bridge gateway, not loopback. docker network inspect shows it |
| Compose, with a proxy container | That container's address, or a subnet holding only the proxy | Publish Chat only to the proxy network or to loopback |
| Kubernetes ingress | The ingress controller's pod addresses, or the pod CIDR when a NetworkPolicy admits only trusted pods | The controller must see the real client. A load balancer that source-NATs, such as k3s ServiceLB with externalTrafficPolicy: Cluster, hides it |
| Kubernetes proxy with host networking | The node addresses the proxy's connections come from, listed per proxy node | A proxy pod with hostNetwork: true has no pod address. Chat sees the proxy node's overlay or bridge address instead, such as the .0 and .1 of that node's pod CIDR with flannel. Read peer in /api/admin/health through each proxy node. Don't trust the whole pod CIDR: every pod could then claim any client |
| Cloudflare Tunnel | The cloudflared address | cloudflared sends the visitor's address in X-Forwarded-For |
Check it
Signed in as an administrator, open /api/admin/health. Its client field shows the connecting peer, whether Chat trusts it, the forwarded chain and the address Chat used for your own request. Check that the address is yours, not the proxy's: if it is the proxy's, everyone behind it shares one sign-in limit.
Streaming through a proxy
Replies stream as server-sent events, and a workspace's terminal and previews use WebSockets. On the proxy:
- Turn off response buffering for Chat, or replies arrive in bursts.
- Allow long read timeouts. A response can run for 9 minutes, and a workspace turn much longer.
- Pass WebSocket upgrades, for the terminal and for dev servers' hot reload.
