OpenClaw 前期一直走 Tailscale / MagicDNS 入口,这套方式对个人使用其实很顺手:设备接进 tailnet,浏览器直接打开 MagicDNS 地址,私网里就能用。但随着使用方式变化,需求很快变成了另一种:我希望直接通过自己的主站域名访问 Control UI。
于是这次做了一轮接入方式切换:从私网 Tailscale 路线,改成主站 https://dufresnenana.com/claw/ 反向代理。这篇就把过程和踩坑一次性记下来。
一、为什么要从 Tailscale 切到主站反代
Tailscale 的优点很明确:
- 安全边界天然更小
- 适合多设备私有访问
- 不需要处理太多公网入口细节
但它也有现实问题:
- 每台设备都得先接入 tailnet
- 在某些浏览器、某些网络环境里不如普通域名直观
- 后期如果希望和主站整合,入口会显得割裂
所以这次不是说 Tailscale 不好,而是站点已经进入“统一入口”阶段了。既然主站已经承载博客、平台入口和多个子服务,那么 OpenClaw 也值得被纳入同一套域名体系。
二、目标结构是什么
这次最终目标不是把 OpenClaw 直接裸露到公网端口,而是:
浏览器 -> Nginx -> /claw/ -> 127.0.0.1:18789 -> OpenClaw Gateway
也就是说:
- 公网入口由主站 Nginx 承接
- Gateway 仍然跑在本机
- 前面再叠一层 Basic Auth
这种结构的好处是,不需要额外记一个新域名,也不用直接把内网端口当正式入口。
三、Nginx 这一层实际改了什么
这次核心改动主要有四块:
- 在主站配置中新增
location = /claw和location /claw/ - 把
/claw/反代到http://127.0.0.1:18789 - 补上 WebSocket 所需的 Upgrade / Connection 头
- 为
/claw/单独加 Basic Auth
这里最关键的不是“能不能代理”,而是 OpenClaw 的 Control UI 并不是普通静态页面,它背后还有 WebSocket、来源校验和设备/会话逻辑。
四、为什么只配 Nginx 还不够
第一次接这类入口时,很容易犯一个误判:看到页面能打开,就以为接入已经完成了。实际上 OpenClaw 这类控制台应用有两个额外要求:
- allowedOrigins:浏览器来源校验
- trustedProxies:反代后的本地/代理识别
这也是这次最典型的两个报错来源。
五、第一个坑:origin not allowed
刚把主站反代接起来时,浏览器端表现是“页面开了,但控制台连不上”。真正的根因在 gateway 日志里:
origin not allowed
问题很直接:主站域名虽然已经经过 Nginx 打到了 gateway,但 OpenClaw 自己并不信任这个来源。
所以必须把下面这些域名补进配置:
gateway.controlUi.allowedOrigins = [
"https://dufresnenana.com",
"https://www.dufresnenana.com"
]
如果你漏掉这一步,前端表现会很迷惑:页面像是“半连通”,但核心连接就是起不来。
六、第二个坑:Proxy headers detected from untrusted address
把 allowedOrigins 配好之后,并不代表反代链路就完全收工了。日志里很快又冒出另一条典型提示:
Proxy headers detected from untrusted address
这说明 gateway 知道自己正处在反向代理之后,但它并不把这个代理视为“可信的本地代理”。
这时就要继续补:
gateway.trustedProxies = [
"127.0.0.1/32",
"::1/128"
]
这一步的意义是:告诉 gateway,来自本机回环地址的代理请求是可信代理转发,不要把它当陌生来源处理。
七、Basic Auth 这层值不值得加
我这次的答案是:值得加。
原因不复杂。就算 gateway 自己已经有配对、会话和来源控制,Nginx 前面再加一层 Basic Auth 仍然有意义:
- 先把无意义的直接探测挡掉
- 减少控制台入口裸暴露的感觉
- 对外部访问形成第一层门禁
后面我还专门把多余账号清掉,只保留了一个真正要用的账户,避免自己以后都搞不清到底哪个账号还在生效。
八、这次还顺手踩到了一个提权配置坑
在接这条链路的同时,我还重新打通了 webchat 下的 elevated 提权。这个坑很典型,值得单独记一下。
问题不在 JSON 语法,而在配置结构:
tools.elevated.allowFrom.webchat
这里要的是数组,不是布尔值。
正确写法是:
"allowFrom": {
"webchat": ["openclaw-control-ui"]
}
如果误写成:
"webchat": true
那你会得到一个非常误导人的现象:Nginx 前面报 502,看起来像是反代坏了,但真正根因其实是 gateway 自己因为配置校验失败而没起来。
九、这一轮改造给我的实际结论
这次从 Tailscale 改到主站反代,最大的收获不是“终于能在域名后面挂一个 /claw 了”,而是把这套控制台入口真正纳入了主站体系。
它的代价也很明确:
- 你得处理 Nginx
- 你得处理 allowedOrigins
- 你得处理 trustedProxies
- 你得决定要不要叠 Basic Auth
换句话说,Tailscale 更像“简单私网方案”,主站反代更像“正式入口方案”。前者更省心,后者更整合。
十、如果你也要这么做,我建议按这个顺序
- 先确认 gateway 本机可用
- 再加 Nginx
/claw/反代 - 补
allowedOrigins - 补
trustedProxies - 最后再加 Basic Auth 和权限收口
不要一上来就把所有层一起改,不然排错时你根本分不清是哪一层出了问题。
对我来说,这次改造的意义也很明确:OpenClaw 不再只是一个通过私网才能摸到的“内部工具”,而是开始真正融进这台 VPS 的主站结构、运维笔记和日常工作流里。