OpenClaw 仪表板无法加载:端口、WebSockets、反向代理修复

嘿,仪表板故障排查员们——如果你正盯着一个空白页面、连接被拒或“未经授权”的错误,而 OpenClaw 的网关确实在运行,我也经历过。

上周,我在四个环境中调试了这个问题:Mac 上的本地开发、Ubuntu 上的 Docker、nginx 反向代理和一个通过 Tailscale 公开的实例。每一个都有不同的故障模式——但它们都追溯到相同的五个根本原因。

HTML 加载正常。开发工具显示 WebSocket 握手失败。网关日志没有提供有用信息。你不确定是认证、网络问题还是配置损坏。

实际问题是:代理中缺少 WebSocket 升级头,Docker NAT 将 localhost 视为外部,认证令牌未在 localStorage 中持久化,或旧的 Clawdbot/Moltbot 服务仍在运行导致端口冲突。

本指南将带你逐步诊断我在多次修复后总结的方法。从这里开始,按照检查流程操作,你要么能让仪表板正常工作,要么准确知道需要报告什么问题。


确认服务健康状况

Before diving into proxies and ports, verify the gateway is actually functional.

Is the Gateway Process Running?

# Check gateway status
openclaw status
# If using systemd
systemctl --user status openclaw-gateway
# Docker users
docker ps | grep openclaw-gateway

What you're looking for:

  • Status: running or active
  • Uptime: More than 30 seconds (not crash-looping)
  • No recent restarts

If it's not running or restarting constantly, stop here and fix the gateway install issues first.

Can the WebSocket Server Respond?

Test the WebSocket endpoint directly:

# Install wscat if you don't have it
npm install -g wscat
# Test connection (replace with your actual token)
wscat -c "ws://127.0.0.1:18789/?token=YOUR_TOKEN_HERE"

Possible outcomes:

  • Connected, gets challenge message → Gateway is fine, problem is browser/proxy
  • Connection refused → Port/bind issue, go to next section
  • Connected then immediately closes with 1008 → Auth problem

If wscat connects but the browser doesn't, the issue is either CORS, proxy config, or browser security policy.

Check What the Gateway Actually Heard

Look at recent logs:

# View last 50 log lines
openclaw logs --limit 50
# Docker
docker logs openclaw-gateway --tail 50
# Look for WebSocket handshake attempts
openclaw logs | grep -E 'ws\]|websocket|handshake'

Key patterns:

  • [ws] accepted → Connection succeeded
  • [ws] closed before connect ... code=1008 reason=pairing required → Auth mismatch
  • [ws] closed before connect ... code=1008 reason=unauthorized: gateway token missing → Token not reaching gateway
  • Origin http://... is not allowed → CORS issue

Port Conflicts & Bind Addresses

Problem: Gateway Binds to Wrong Interface

Symptom: Gateway runs fine via CLI, but browser can't connect to 127.0.0.1:18789.

Cause: Gateway bound to a Tailscale IP, VPN interface, or external IP instead of loopback.

This is Issue #1380 — when Tailscale is active, the gateway sometimes binds to 100.x.x.x instead of 127.0.0.1. Browser WebSocket connections to Tailscale IPs fail.

Diagnose:

# Check what interface the gateway bound to
openclaw status
# Or inspect the actual listening socket
sudo lsof -i :18789
# Look at the "NAME" column - should show 127.0.0.1:18789

Fix:

Force loopback binding in config (~/.openclaw/config.json):

{
  "gateway": {
    "bind": "loopback",
    "port": 18789
  }
}

Valid bind options:

  • loopback → 127.0.0.1 only (default, safest)
  • lan → 0.0.0.0 (all interfaces, requires auth)
  • tailnet → Tailscale IP

After changing bind, restart:

systemctl --user restart openclaw-gateway
# or
docker compose restart openclaw-gateway

Problem: Port 18789 Already in Use

Symptom: Gateway won't start, logs show EADDRINUSE.

Cause: Old Clawdbot/Moltbot gateway still running, or another service using 18789.

Diagnose:

# Find what's using the port
sudo lsof -i :18789
# Or
sudo ss -tlnp | grep 18789

Fix Option A: Stop Conflicting Service

# Stop old Clawdbot
systemctl --user stop clawdbot-gateway
# Kill the process if needed
sudo kill -9 <PID>

Fix Option B: Change OpenClaw Port

In ~/.openclaw/config.json:

{
  "gateway": {
    "port": 18790
  }
}

Update Docker compose if using containers:

ports:
  - "127.0.0.1:18790:18790"

Then access dashboard at http://127.0.0.1:18790/.

Problem: Docker NAT Breaks Localhost Detection

Symptom: Running gateway in Docker Desktop on Windows/Mac, browser shows "pairing required" even with correct token.

Cause: Docker's NAT networking makes the gateway see connections from 172.18.0.1 instead of 127.0.0.1. Gateway treats this as an external connection requiring node pairing.

Diagnose:

Check gateway logs for:

[ws] closed before connect conn=... remote=172.18.0.1 ... code=1008 reason=pairing required

If you see remote=172.18.0.1 but you're connecting from 127.0.0.1, this is the issue.

Fix:

Add trusted proxies to your config:

{
  "gateway": {
    "trustedProxies": ["172.18.0.0/16", "172.17.0.0/16"]
  }
}

Or run gateway with --bind lan and enforce token auth:

{
  "gateway": {
    "bind": "lan",
    "auth": {
      "mode": "token",
      "token": "your-secret-token"
    }
  }
}

Better fix for Windows/Mac: Run gateway natively instead of in Docker to get real localhost connections.


Reverse Proxy WebSockets Checklist

If you're exposing OpenClaw through nginx, Caddy, or another reverse proxy, WebSocket support needs explicit configuration.

nginx Configuration

The dashboard uses WebSockets for real-time communication. Standard HTTP proxying breaks this.

Minimal working config:

server {
    listen 443 ssl;
    server_name openclaw.yourdomain.com;
    # SSL certs
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    location / {
        proxy_pass http://127.0.0.1:18789;
        # Critical for WebSockets
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";     
        # Pass through client info
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        # WebSocket timeouts
        proxy_read_timeout 86400;
        proxy_send_timeout 86400;
    }
}

Common nginx mistakes:

  • Missing proxy_http_version 1.1 → WebSocket upgrade fails
  • Missing Upgrade and Connection headers → Handshake rejected
  • Default timeout (60s) → WebSocket closes after idle time

Detailed nginx reverse proxy guide: nginx WebSocket proxying

Caddy Configuration

Caddy handles WebSockets automatically, but you still need proper config:

openclaw.yourdomain.com {
    reverse_proxy 127.0.0.1:18789
}

That's it. Caddy auto-detects the WebSocket upgrade and adjusts timeouts.

If using subpath:

yourdomain.com {
    reverse_proxy /openclaw/* 127.0.0.1:18789
}

Then access via https://yourdomain.com/openclaw/.

Debugging Caddy WebSocket issues:

Enable verbose logging:

openclaw.yourdomain.com {
    log {
        output file /var/log/caddy/openclaw.log
        level DEBUG
    }
    reverse_proxy 127.0.0.1:18789
}

Check for websocket upgrade in logs. If missing, Caddy isn't detecting the upgrade request.

Apache Configuration

Less common but occasionally used:

<VirtualHost *:443>
    ServerName openclaw.yourdomain.com
    SSLEngine On
    SSLCertificateFile /path/to/cert.pem
    SSLCertificateKeyFile /path/to/key.pem
    # Enable proxy modules
    # a2enmod proxy proxy_http proxy_wstunnel rewrite
    ProxyPreserveHost On
    # WebSocket tunnel
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} =websocket [NC]
    RewriteRule /(.*)  ws://127.0.0.1:18789/$1 [P,L]
    # Regular HTTP proxy
    ProxyPass / http://127.0.0.1:18789/
    ProxyPassReverse / http://127.0.0.1:18789/
</VirtualHost>

Enable required modules:

sudo a2enmod proxy proxy_http proxy_wstunnel rewrite
sudo systemctl restart apache2

CORS / HTTPS Gotchas

Mixed Content Blocking

Symptom: Dashboard loads over HTTPS but WebSocket connection fails with mixed content error.

Cause: Browser blocks ws:// (non-secure WebSocket) when page is loaded over https://.

Fix:

Use wss:// (WebSocket Secure) behind your reverse proxy.

Your nginx/Caddy terminates SSL and forwards to http://127.0.0.1:18789, but the browser needs to connect via wss://.

Example flow:

  • Browser: wss://openclaw.yourdomain.com/ → nginx with SSL
  • nginx: ws://127.0.0.1:18789/ → gateway (local, no SSL needed)

The dashboard auto-detects this if you access it via HTTPS URL.

CORS Rejections

Symptom: Browser console shows CORS policy: No 'Access-Control-Allow-Origin' header.

Cause: Accessing dashboard from a different domain than the gateway expects.

Context: As of OpenClaw's security updates, the Control UI has stricter origin validation.

Fix:

If your dashboard is at https://openclaw.example.com but gateway config doesn't know about it:

{
  "gateway": {
    "cors": {
      "origins": ["https://openclaw.example.com"]
    }
  }
}

Security note: Don't use "origins": ["*"] in production. This exposes your gateway to external access vulnerabilities.

Token Not Persisting in localStorage

Symptom: Dashboard keeps asking for token every time you reload.

Cause: Browser's localStorage API blocked or cleared.

Diagnose:

Open DevTools → Application → Local Storage → http://127.0.0.1:18789

Look for key: openclaw-gateway-token

If missing after login, check:

  • Browser in private/incognito mode (localStorage disabled)
  • Third-party cookie blocking extensions
  • Browser set to clear storage on exit

Workaround:

Use the tokenized URL every time:

openclaw dashboard

This command outputs a URL with ?token=... appended. The UI strips it after first load and saves to localStorage — but if that fails, you need the URL each time.


Logs to Collect Before Reporting

If none of the above fixes work, you're in edge case territory. Before opening a GitHub issue, collect these logs:

Gateway Logs

# Last 100 lines with timestamps
openclaw logs --limit 100 > gateway-logs.txt
# Docker
docker logs openclaw-gateway --tail 100 > gateway-logs.txt

Browser Console Logs

  1. Open DevTools (F12)
  2. Go to Console tab
  3. Reproduce the connection failure
  4. Right-click console → Save as... → browser-console.txt

WebSocket Frame Inspection

  1. DevTools → Network tab
  2. Filter: WS
  3. Click the WebSocket connection
  4. Check "Messages" tab for handshake details
  5. Screenshot the Headers tab showing request/response

Config Sanitized

# Export config (redact tokens)
cat ~/.openclaw/config.json | sed 's/"token": ".*"/"token": "REDACTED"/g' > config-sanitized.json

Network Environment

# Check firewall rules (Linux)
sudo iptables -L -n | grep 18789
# Check what's binding to the port
sudo lsof -i :18789
# Test from external host if relevant
curl -v http://your-server-ip:18789/

What Actually Breaks Dashboards

在不同环境中调试后,我观察到的故障分布如下:

60% - 缺少反向代理 WebSocket 配置

  • nginx 没有 Upgrade/Connection
  • 超时设置不正确
  • 缺少 proxy_http_version 1.1

20% - Docker NAT 将 localhost 视为外部

  • Windows/Mac Docker Desktop
  • 通过本地运行网关或添加 trustedProxies 解决

10% - 旧 Clawdbot/Moltbot 导致的端口冲突

  • 安装 OpenClaw 前忘记停止旧服务
  • 通过终止旧进程或更改端口解决

5% - Tailscale 绑定而非环回

  • Tailscale 激活时网关绑定到 100.x.x.x
  • 通过在配置中强制 bind: "loopback" 解决

5% - 认证令牌不匹配

  • 配置中的令牌与 URL 中的不匹配
  • 通过运行 openclaw dashboard 获取正确的令牌化链接解决

最常见的问题——反向代理 WebSocket 配置——也是最容易被忽视的,因为 HTTP 部分工作正常。你会看到 HTML 和 CSS 加载,但 WebSocket 会静默失败。

如果你在代理后运行且控制面板无法连接,这应该是你的首要检查。

在 Macaron,我们为你托管 UI——无需调试 nginx 配置,无需调整 WebSocket 超时,也无需担心 Docker NAT 的意外。如果你厌倦了在测试工作流之前进行基础设施故障排除,是否更喜欢托管的工作流 UI?创建一个 Macaron 账户。

常见问题解答

问:仪表板 HTML 加载但什么都不工作。我该从哪里开始? 打开开发者工具 → 网络 → WS 选项卡。如果 WebSocket 握手失败,你几乎可以肯定是缺少反向代理配置中的 UpgradeConnection 头。这就是 60% 的情况。如果你想先排除错误安装,我建议先对照 OpenClaw 安装指南 核对,然后再深入研究代理配置。

问:为什么即使我正确设置了令牌,我的网关仍显示“需要配对”? 检查你的日志中是否有 remote=172.18.0.1。如果你使用 Docker Desktop(Windows 或 Mac),NAT 让网关认为你是外部连接。请在本地运行网关,或在配置中添加 trustedProxies——Docker 设置指南 有针对这种情况的具体网络配置。

问:每次刷新页面时,网关总是提示我输入令牌。 你的浏览器的localStorage没有持久化。通常是由于无痕模式、激进的隐私扩展或启用了“退出时清除存储”。使用openclaw dashboard中的令牌化URL作为变通办法。如果身份验证问题超出这个范围,OpenClaw安全检查表提供了有关令牌管理的详细信息。

问:端口18789已经被占用。是什么原因? 十有八九是旧的Moltbot或Clawdbot网关仍在运行。lsof -i :18789可以告诉你是什么进程。结束它,或者在配置中更改OpenClaw的端口。如果你最近从Moltbot迁移,可以阅读这篇关于同时运行两者的文章——服务冲突是个常见的问题。

问:我的HTTPS仪表板无法连接到WebSocket。 混合内容——浏览器不允许在HTTPS页面上使用ws://,绝对禁止。你的代理需要处理wss://终止。如果你在VPS上遇到这个问题,VPS部署指南提供了直接可用的nginx + SSL配置。

问:Tailscale 已激活,但现在仪表板无法在本地加载。 已知问题 (#1380) — Tailscale 可能会将网关拉到 100.x.x.x 而不是 127.0.0.1。将 "bind": "loopback" 添加到您的配置中并重新启动。如果绑定行为仍然令人困惑,网关参考 清晰地分解了所有选项。

嗨,我是 Hanks — 一个工作流程爱好者和 AI 工具狂热者,拥有超过十年的自动化、SaaS 和内容创作的实践经验。我每天都在测试工具,这样你就不必费心,将复杂的流程简化为简单、可执行的步骤,并深入挖掘“什么真正有效”的数据。

申请成为 Macaron 的首批朋友