2025-12-18 22:40:46 +00:00
---
summary: "Gateway web surfaces: Control UI, bind modes, and security"
read_when:
- You want to access the Gateway over Tailscale
- You want the browser Control UI and config editing
2026-01-31 16:04:03 -05:00
title: "Web"
2025-12-18 22:40:46 +00:00
---
2026-01-31 21:13:13 +09:00
2025-12-18 22:40:46 +00:00
# Web (Gateway)
The Gateway serves a small **browser Control UI ** (Vite + Lit) from the same port as the Gateway WebSocket:
2026-01-03 17:54:52 +01:00
- default: `http://<host>:18789/`
2026-01-30 03:15:10 +01:00
- optional prefix: set `gateway.controlUi.basePath` (e.g. `/openclaw` )
2025-12-18 22:40:46 +00:00
2026-01-10 14:51:21 -06:00
Capabilities live in [Control UI ](/web/control-ui ).
2026-01-07 00:41:31 +01:00
This page focuses on bind modes, security, and web-facing surfaces.
2025-12-18 22:40:46 +00:00
2025-12-24 14:32:55 +00:00
## Webhooks
2026-01-06 18:25:52 +00:00
When `hooks.enabled=true` , the Gateway also exposes a small webhook endpoint on the same HTTP server.
2026-01-10 14:51:21 -06:00
See [Gateway configuration ](/gateway/configuration ) → `hooks` for auth + payloads.
2025-12-24 14:32:55 +00:00
2025-12-18 22:40:46 +00:00
## Config (default-on)
The Control UI is **enabled by default ** when assets are present (`dist/control-ui` ).
You can control it via config:
``` json5
{
gateway: {
2026-01-31 21:13:13 +09:00
controlUi: { enabled: true, basePath: "/openclaw" }, // basePath optional
},
2025-12-18 22:40:46 +00:00
}
```
2025-12-21 00:34:39 +00:00
## Tailscale access
2025-12-18 22:40:46 +00:00
2025-12-21 00:34:39 +00:00
### Integrated Serve (recommended)
2025-12-18 22:40:46 +00:00
2025-12-21 00:34:39 +00:00
Keep the Gateway on loopback and let Tailscale Serve proxy it:
``` json5
{
gateway: {
bind: "loopback",
2026-01-31 21:13:13 +09:00
tailscale: { mode: "serve" },
},
2025-12-21 00:34:39 +00:00
}
```
Then start the gateway:
``` bash
2026-01-30 03:15:10 +01:00
openclaw gateway
2025-12-21 00:34:39 +00:00
```
Open:
2026-01-31 21:13:13 +09:00
2026-01-03 17:54:52 +01:00
- `https://<magicdns>/` (or your configured `gateway.controlUi.basePath` )
2025-12-21 00:34:39 +00:00
2026-01-11 01:51:07 +01:00
### Tailnet bind + token
2025-12-18 22:40:46 +00:00
``` json5
{
gateway: {
bind: "tailnet",
2026-01-11 01:51:07 +01:00
controlUi: { enabled: true },
2026-01-31 21:13:13 +09:00
auth: { mode: "token", token: "your-token" },
},
2025-12-18 22:40:46 +00:00
}
```
2026-04-04 21:25:40 +01:00
Then start the gateway (this non-loopback example uses shared-secret token
auth):
2025-12-18 22:40:46 +00:00
``` bash
2026-01-30 03:15:10 +01:00
openclaw gateway
2025-12-18 22:40:46 +00:00
```
Open:
2026-01-31 21:13:13 +09:00
2026-01-03 17:54:52 +01:00
- `http://<tailscale-ip>:18789/` (or your configured `gateway.controlUi.basePath` )
2025-12-18 22:40:46 +00:00
2025-12-21 00:34:39 +00:00
### Public internet (Funnel)
2025-12-18 22:40:46 +00:00
2025-12-21 00:34:39 +00:00
``` json5
{
gateway: {
bind: "loopback",
tailscale: { mode: "funnel" },
2026-01-31 21:13:13 +09:00
auth: { mode: "password" }, // or OPENCLAW_GATEWAY_PASSWORD
},
2025-12-21 00:34:39 +00:00
}
2025-12-18 22:40:46 +00:00
```
## Security notes
2026-04-04 13:54:15 +01:00
- Gateway auth is required by default (token, password, trusted-proxy, or Tailscale Serve identity headers when enabled).
- Non-loopback binds still **require ** gateway auth. In practice that means token/password auth or an identity-aware reverse proxy with `gateway.auth.mode: "trusted-proxy"` .
2026-04-04 21:11:16 +01:00
- The wizard creates shared-secret auth by default and usually generates a
gateway token (even on loopback).
2026-04-04 16:09:42 +01:00
- In shared-secret mode, the UI sends `connect.params.auth.token` or
`connect.params.auth.password` .
- In identity-bearing modes such as Tailscale Serve or `trusted-proxy` , the
WebSocket auth check is satisfied from request headers instead.
2026-02-24 01:52:15 +00:00
- For non-loopback Control UI deployments, set `gateway.controlUi.allowedOrigins`
explicitly (full origins). Without it, gateway startup is refused by default.
- `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true` enables
Host-header origin fallback mode, but is a dangerous security downgrade.
2026-02-21 13:03:08 +01:00
- With Serve, Tailscale identity headers can satisfy Control UI/WebSocket auth
when `gateway.auth.allowTailscale` is `true` (no token/password required).
2026-04-04 14:13:36 +01:00
HTTP API endpoints do not use those Tailscale identity headers; they follow
the gateway's normal HTTP auth mode instead. Set
2026-01-13 04:37:04 +00:00
`gateway.auth.allowTailscale: false` to require explicit credentials. See
2026-02-21 12:52:45 +01:00
[Tailscale ](/gateway/tailscale ) and [Security ](/gateway/security ). This
tokenless flow assumes the gateway host is trusted.
2025-12-23 13:13:09 +00:00
- `gateway.tailscale.mode: "funnel"` requires `gateway.auth.mode: "password"` (shared password).
2025-12-18 22:40:46 +00:00
## Building the UI
The Gateway serves static files from `dist/control-ui` . Build them with:
``` bash
2026-01-09 07:02:42 +00:00
pnpm ui:build # auto-installs UI deps on first run
2025-12-18 22:40:46 +00:00
```