LI YAO'S BLOG

有趣的人生, 一半家长里短, 一半山川胡海

Windows 本地用 Nginx 反代 OpenClaw(18789 一键入口 + WebSocket 不掉线)并实现开机自启

需求与目标(从用户体验出发)

我在 EC2 上部署 OpenClaw 时,发现要访问聊天页面往往需要输入一串带参数的 URL(/chat?session=...&token=...),不够“人类友好”。

现在希望在 Windows 本地环境也实现同样的体验:

  • 直接访问:http://localhost:18789
  • 自动进入聊天页面(自动拼好 session 和 token)
  • WebSocket 长连接不掉(不能出现 1006/1008 等断开)
  • 并且 nginx 能像 openclaw gateway 一样开机自动启动

结论先行:

  • 18790:OpenClaw Gateway(上游,仅本机)
  • 18789:Nginx 入口(对外访问)
  • 通过 nginx 对根路径 / 进行“HTTP/WS 分流”,并配置 gateway 允许从 18789 origin 访问(否则会 pairing required)。

总体架构

为什么要拆成两个端口?

最直观的做法是让 OpenClaw Gateway 直接监听 18789,然后在 Gateway 内部做跳转。但这样会遇到两个现实问题:

  1. 端口职责混乱:既要提供页面,又要处理 WebSocket,又要做跳转拼参数。
  2. 根路径 / 的陷阱:OpenClaw 的前端会非常早地对 / 发起 WebSocket 连接(Upgrade)。如果你对 / 一刀切做 301/302,WebSocket 握手也会被重定向,导致断连。

因此采用线上常见的更稳的形态:

  • 127.0.0.1:18790:OpenClaw Gateway(真实服务)
  • 127.0.0.1:18789:Nginx(入口反代 + 跳转)

第 1 步:把 OpenClaw Gateway 挪到 18790

1.1 修改 OpenClaw 配置

OpenClaw 配置文件:

  • C:\Users\LIYAO\.openclaw\openclaw.json

把 gateway.port 改为:

1
2
3
4
5
{
"gateway": {
"port": 18790
}
}

1.2 踩坑:改了 openclaw.json 仍然监听 18789?

我实际遇到过:配置里已经是 18790,但端口还是 18789。

原因是:Windows 上我的 Gateway 是通过 gateway.cmd 启动,并且命令行带了 --port 18789,命令行参数优先级更高。

相关链路(示例):

  • start-gateway-silent.vbs → 运行 gateway.cmd
  • gateway.cmd 里存在:
1
E:\nvm\v25.6.0\node.exe ... openclaw ... gateway --port 18789

解决方式:同步修改 gateway.cmd:

  • set OPENCLAW_GATEWAY_PORT=18790
  • 把 --port 18789 改为 --port 18790

然后重启 gateway 进程。

1.3 验证上游(18790)

注意:不要用 http://127.0.0.1:18790/ 来判断服务是否正常。

因为 / 会跳到 /chat?session=main,但该 URL 没带 token,WebSocket 会报:

  • disconnected (1008): unauthorized: gateway token missing

正确的验证方式是访问 tokenized dashboard URL:

1
http://127.0.0.1:18790/chat?session=agent%3Amain%3Amain&token=<YOUR_TOKEN>

第 2 步:Nginx(18789)实现“根路径自动跳转 + WebSocket 不重定向”

2.1 核心原则:根路径 / 必须区分 HTTP 和 WebSocket

WebSocket 握手请求会带:

  • Upgrade: websocket

Nginx 可以用 map + $http_upgrade 识别:

1
2
3
4
5
6
7
8
9
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

map $http_upgrade $is_ws {
default 0;
websocket 1;
}

2.2 Nginx 配置(Windows,入口 18789 → 上游 18790)

Nginx 主配置:

  • E:\openclaw-local\nginx-1.28.2\conf\nginx.conf

关键逻辑:

  • location = /:
    • 如果不是 WS:302 跳到 /chat?...&token=...
    • 如果是 WS:proxy_pass 到 18790
  • location /:其余路径全部反代到 18790

示例(精简版,关键点保留):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
server {
listen 18789;

location = / {
if ($is_ws = 0) {
return 302 /chat?session=agent%3Amain%3Amain&token=<YOUR_TOKEN>;
}

proxy_pass http://127.0.0.1:18790/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}

location / {
proxy_pass http://127.0.0.1:18790;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}

2.3 验证

  • 访问 http://127.0.0.1:18789/:应 302 到 /chat?...token=...
  • DevTools → Network → WS:/ 的连接应看到 101 Switching Protocols

第 3 步:踩坑 pairing required —— 通过 Nginx 反代后需要允许 Origin

我在打通 18789 → 18790 之后,页面能打开,但 WebSocket 仍然报:

  • disconnected (1008): pairing required

这是一个很典型的“反代引入新 origin”的问题:

  • 直接访问 18790:origin 是 http://127.0.0.1:18790
  • 通过 nginx 访问 18789:origin 变为 http://127.0.0.1:18789

OpenClaw Control UI 有设备配对/设备鉴权机制,会校验来源 origin。结果就是:18789 这个新 origin 未被允许时,客户端无法完成鉴权初始化,于是表现为 pairing required。

3.1 解决:配置 gateway.controlUi.allowedOrigins

在 openclaw.json 里为 gateway 增加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"gateway": {
"controlUi": {
"allowedOrigins": [
"http://127.0.0.1:18789",
"http://localhost:18789"
],
"allowInsecureAuth": true
},
"trustedProxies": [
"127.0.0.1",
"::1"
]
}
}

然后重启/热重载 gateway。

说明:

  • 本地是 http,所以需要 allowInsecureAuth=true
  • trustedProxies 用于信任本机反代(避免代理头被错误处理)

3.2 再次验证

  • 重新打开浏览器(建议无痕,避免旧 localStorage 状态干扰)
  • 访问:http://127.0.0.1:18789/

应能正常连接 gateway,Health OK,聊天输入框可用。


第 4 步:让 Nginx 开机自启(像 OpenClaw 一样静默)

目标:用户登录 Windows 后自动启动 nginx,且不弹黑窗口。

4.1 用 VBS 隐藏启动窗口

创建:

  • C:\Users\LIYAO\.openclaw\nginx-start.cmd
1
2
3
4
@echo off
set NGINX_DIR=E:\openclaw-local\nginx-1.28.2
cd /d %NGINX_DIR%
%NGINX_DIR%\nginx.exe
  • C:\Users\LIYAO\.openclaw\start-nginx-silent.vbs
1
2
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run """C:\Users\LIYAO\.openclaw\nginx-start.cmd""", 0, False

4.2 放到 Startup 文件夹实现“登录自启”

把下面这个 vbs 放到 Startup:

  • C:\Users\LIYAO\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\openclaw-local-nginx.vbs

内容:

1
2
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run """C:\Users\LIYAO\.openclaw\nginx-start.cmd""", 0, False

这样每次登录 Windows,nginx 就会静默启动。

4.3 踩坑:schtasks 需要管理员权限

原本想用 schtasks /Create /SC ONLOGON ... 建计划任务,但遇到:

  • Access is denied

说明当前权限/策略不允许创建该计划任务(尤其是 /RL HIGHEST)。因此采用用户级 Startup 更省心。


最终效果与访问方式

  • ✅ OpenClaw Gateway:127.0.0.1:18790(上游)

    • 直接访问根路径 / 仍会提示 token missing:这是正常的安全策略
    • 需要用 tokenized URL 才能访问
  • ✅ Nginx 入口:127.0.0.1:18789

    • 访问 http://localhost:18789 即可进入聊天页
    • WebSocket / 不被重定向,连接稳定
  • ✅ Nginx 登录自启:无需手动启动


经验总结(值得记下来)

  1. WebSocket 代理最怕“对根路径一刀切重定向”:要识别 Upgrade 请求,否则 WS 握手会被 302/301 误伤。
  2. 配置改了但端口不变,优先看启动命令行是否覆盖了配置(Windows 很常见:cmd/vbs/service wrapper)。
  3. 反代引入新 origin 时,很多带鉴权的 Control UI 会拒绝:需要显式允许 allowedOrigins。
  4. 本地自启:
    • 不想折腾管理员权限 → 用 Startup 文件夹 + vbs 隐藏窗口
    • 真正“未登录也启动” → 再上计划任务(SYSTEM)或 Windows Service(NSSM)

附:常用命令

Nginx reload

1
2
3
cd E:\openclaw-local\nginx-1.28.2
.\nginx.exe -t
.\nginx.exe -s reload

Nginx stop

1
C:\Users\LIYAO\.openclaw\nginx-stop.cmd
赞赏一杯咖啡