文档
Trusted Proxy Auth
⚠️ Security-sensitive feature. This mode delegates authentication entirely to your reverse proxy. Misconfiguration can expose your Gateway 网关 to unauthorized access. Read this page carefully before enabling.
When to Use
Use trusted-proxy auth mode when:
- You run OpenClaw behind an identity-aware proxy (Pomerium, Caddy + OAuth, nginx + oauth2-proxy, Traefik + forward auth)
- Your proxy handles all authentication and passes user identity via headers
- You’re in a Kubernetes or container environment where the proxy is the only path to the Gateway 网关
- You’re hitting WebSocket
1008 unauthorizederrors because browsers can’t pass tokens in WS payloads
When NOT to Use
- If your proxy doesn’t authenticate users (just a TLS terminator or load balancer)
- If there’s any path to the Gateway 网关 that bypasses the proxy (firewall holes, internal network access)
- If you’re unsure whether your proxy correctly strips/overwrites forwarded headers
- If you only need personal single-user access (consider Tailscale Serve + loopback for simpler setup)
How It Works
- Your reverse proxy authenticates users (OAuth, OIDC, SAML, etc.)
- Proxy adds a header with the authenticated user identity (e.g.,
x-forwarded-user: nick@example.com) - OpenClaw checks that the request came from a trusted proxy IP (configured in
gateway.trustedProxies) - OpenClaw extracts the user identity from the configured header
- If everything checks out, the request is authorized
Control UI Pairing Behavior
When gateway.auth.mode = "trusted-proxy" is active and the request passes
trusted-proxy checks, Control UI WebSocket sessions can connect without device
pairing identity.
Implications:
- Pairing is no longer the primary gate for Control UI access in this mode.
- Your reverse proxy auth policy and
allowUsersbecome the effective access control. - Keep 网关 ingress locked to trusted proxy IPs only (
gateway.trustedProxies+ firewall).
配置说明
“`json5 theme={“theme”:{“light”:”min-light”,”dark”:”min-dark”}}
{
网关: {
// Use loopback for same-host proxy setups; use lan/custom for remote proxy hosts
bind: “loopback”,
// CRITICAL: Only add your proxy's IP(s) here
trustedProxies: ["10.0.0.1", "172.17.0.1"],
auth: {
mode: "trusted-proxy",
trustedProxy: {
// Header containing authenticated user identity (required)
userHeader: "x-forwarded-user",
// Optional: headers that MUST be present (proxy verification)
requiredHeaders: ["x-forwarded-proto", "x-forwarded-host"],
// Optional: restrict to specific users (empty = allow all)
allowUsers: ["nick@example.com", "admin@company.org"],
},
},
},
}
If `gateway.bind` is `loopback`, include a loopback proxy address in
`gateway.trustedProxies` (`127.0.0.1`, `::1`, or an equivalent loopback CIDR).
### Configuration Reference
| Field | Required | Description |
| ------------------------------------------- | -------- | --------------------------------------------------------------------------- |
| `gateway.trustedProxies` | Yes | Array of proxy IP addresses to trust. Requests from other IPs are rejected. |
| `gateway.auth.mode` | Yes | Must be `"trusted-proxy"` |
| `gateway.auth.trustedProxy.userHeader` | Yes | Header name containing the authenticated user identity |
| `gateway.auth.trustedProxy.requiredHeaders` | No | Additional headers that must be present for the request to be trusted |
| `gateway.auth.trustedProxy.allowUsers` | No | Allowlist of user identities. Empty means allow all authenticated users. |
## TLS termination and HSTS
Use one TLS termination point and apply HSTS there.
### Recommended pattern: proxy TLS termination
When your reverse proxy handles HTTPS for `https://control.example.com`, set
`Strict-Transport-Security` at the proxy for that domain.
* Good fit for internet-facing deployments.
* Keeps certificate + HTTP hardening policy in one place.
* OpenClaw can stay on loopback HTTP behind the proxy.
Example header value:
```text theme={"theme":{"light":"min-light","dark":"min-dark"}}
Strict-Transport-Security: max-age=31536000; includeSubDomains
Gateway 网关 TLS termination
If OpenClaw itself serves HTTPS directly (no TLS-terminating proxy), set:
“`json5 theme={“theme”:{“light”:”min-light”,”dark”:”min-dark”}}
{
网关: {
tls: { enabled: true },
http: {
securityHeaders: {
strictTransportSecurity: “max-age=31536000; includeSubDomains”,
},
},
},
}
`strictTransportSecurity` accepts a string header value, or `false` to disable explicitly.
### Rollout guidance
* Start with a short max age first (for example `max-age=300`) while validating traffic.
* Increase to long-lived values (for example `max-age=31536000`) only after confidence is high.
* Add `includeSubDomains` only if every subdomain is HTTPS-ready.
* Use preload only if you intentionally meet preload requirements for your full domain set.
* Loopback-only local development does not benefit from HSTS.
## Proxy Setup Examples
### Pomerium
Pomerium passes identity in `x-pomerium-claim-email` (or other claim headers) and a JWT in `x-pomerium-jwt-assertion`.
```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
gateway: {
bind: "lan",
trustedProxies: ["10.0.0.1"], // Pomerium's IP
auth: {
mode: "trusted-proxy",
trustedProxy: {
userHeader: "x-pomerium-claim-email",
requiredHeaders: ["x-pomerium-jwt-assertion"],
},
},
},
}
Pomerium config snippet:
“`yaml theme={“theme”:{“light”:”min-light”,”dark”:”min-dark”}}
routes:
– from: https://openclaw.example.com
to: http://openclaw-网关:18789
policy:
– allow:
or:
– email:
is: nick@example.com
pass_identity_headers: true
### Caddy with OAuth
Caddy with the `caddy-security` plugin can authenticate users and pass identity headers.
```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
gateway: {
bind: "lan",
trustedProxies: ["127.0.0.1"], // Caddy's IP (if on same host)
auth: {
mode: "trusted-proxy",
trustedProxy: {
userHeader: "x-forwarded-user",
},
},
},
}
Caddyfile snippet:
openclaw.example.com {
authenticate with oauth2_provider
authorize with policy1
reverse_proxy openclaw:18789 {
header_up X-Forwarded-User {http.auth.user.email}
}
}
nginx + oauth2-proxy
oauth2-proxy authenticates users and passes identity in x-auth-request-email.
“`json5 theme={“theme”:{“light”:”min-light”,”dark”:”min-dark”}}
{
网关: {
bind: “lan”,
trustedProxies: [“10.0.0.1”], // nginx/oauth2-proxy IP
auth: {
mode: “trusted-proxy”,
trustedProxy: {
userHeader: “x-auth-request-email”,
},
},
},
}
nginx config snippet:
```nginx theme={"theme":{"light":"min-light","dark":"min-dark"}}
location / {
auth_request /oauth2/auth;
auth_request_set $user $upstream_http_x_auth_request_email;
proxy_pass http://openclaw:18789;
proxy_set_header X-Auth-Request-Email $user;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
Traefik with Forward Auth
json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
gateway: {
bind: "lan",
trustedProxies: ["172.17.0.1"], // Traefik container IP
auth: {
mode: "trusted-proxy",
trustedProxy: {
userHeader: "x-forwarded-user",
},
},
},
}
Security Checklist
Before enabling trusted-proxy auth, verify:
- [ ] Proxy is the only path: The Gateway 网关 port is firewalled from everything except your proxy
- [ ] trustedProxies is minimal: Only your actual proxy IPs, not entire subnets
- [ ] Proxy strips headers: Your proxy overwrites (not appends)
x-forwarded-*headers from clients - [ ] TLS termination: Your proxy handles TLS; users connect via HTTPS
- [ ] allowUsers is set (recommended): Restrict to known users rather than allowing anyone authenticated
Security Audit
openclaw security audit will flag trusted-proxy auth with a critical severity finding. This is intentional — it’s a reminder that you’re delegating security to your proxy setup.
The audit checks for:
- Missing
trustedProxiesconfiguration - Missing
userHeaderconfiguration - Empty
allowUsers(allows any authenticated user)
故障排查
“trusted_proxy_untrusted_source”
The request didn’t come from an IP in gateway.trustedProxies. Check:
- Is the proxy IP correct? (Docker container IPs can change)
- Is there a load balancer in front of your proxy?
- Use
docker inspectorkubectl get pods -o wideto find actual IPs
“trusted_proxy_user_missing”
The user header was empty or missing. Check:
- Is your proxy configured to pass identity headers?
- Is the header name correct? (case-insensitive, but spelling matters)
- Is the user actually authenticated at the proxy?
“trustedproxy_missing_header*”
A required header wasn’t present. Check:
- Your proxy configuration for those specific headers
- Whether headers are being stripped somewhere in the chain
“trusted_proxy_user_not_allowed”
The user is authenticated but not in allowUsers. Either add them or remove the allowlist.
WebSocket Still Failing
Make sure your proxy:
- Supports WebSocket upgrades (
Upgrade: websocket,Connection: upgrade) - Passes the identity headers on WebSocket upgrade requests (not just HTTP)
- Doesn’t have a separate auth path for WebSocket connections
Migration from Token Auth
If you’re moving from token auth to trusted-proxy:
- Configure your proxy to authenticate users and pass headers
- Test the proxy setup independently (curl with headers)
- Update OpenClaw config with trusted-proxy auth
- Restart the Gateway 网关
- Test WebSocket connections from the Control UI
- Run
openclaw security auditand review findings
Related
- Security — full security guide
- 配置说明 — config reference
- Remote Access — other remote access patterns
- Tailscale — simpler alternative for tailnet-only access