OpenClaw 儀表板無法載入:端口、WebSockets、反向代理修復

嘿,儀表板故障排除者——如果你正盯著空白頁面、連線被拒或「未授權」錯誤,而 OpenClaw 的閘道器確實在運行,我也曾經遇到過。

上週,我在四個設置中調試了這個問題:Mac 上的本地開發、Ubuntu 上的 Docker、nginx 反向代理和 Tailscale 暴露的實例。每一個都有不同的故障模式——但它們都追溯到相同的五個根本原因。

HTML 載入了。DevTools 顯示 WebSocket 握手失敗。閘道器日誌沒有提供有用的信息。你不確定是授權問題、網路問題還是配置損壞。

實際上出問題的是:代理中缺少 WebSocket 升級標頭、Docker NAT 將本地主機視為外部、授權令牌未在 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 會讓網關認為您是外部連接。請在本機運行網關,或在配置中添加 trustedProxiesDocker 設置指南 提供了這種情境的具體網絡配置。

問:為什麼每次重新載入頁面時,Gateway 都會要求輸入我的 token? 您的瀏覽器的 localStorage 無法持久保存。通常是無痕模式、一個激進的隱私擴充套件,或啟用了「退出時清除儲存」功能所致。使用來自 openclaw dashboard 的帶 token 網址作為替代方案。如果認證問題超出這個範圍,OpenClaw 安全檢查清單 提供更詳細的 token 管理說明。

問:埠 18789 已經在使用中。是什麼佔用了它? 十之八九是舊的 Moltbot 或 Clawdbot gateway 還在運行。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 的第一批朋友