问题背景
最近在服务器上部署了 OpenClaw,初始访问需要使用完整的 URL:
1
| https://openclaw.iambeyouare.com/chat?session=main&token=7173f...
|
希望能简化访问方式,直接输入 openclaw.iambeyouare.com 就能自动跳转到聊天页面,让 nginx 帮忙拼接后面的参数。
初始配置
原始的 nginx 配置 (/etc/nginx/conf.d/openclaw.conf):
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
| server { listen 443 ssl; http2 on; server_name openclaw.iambeyouare.com;
ssl_certificate /etc/letsencrypt/live/iambeyouare.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/iambeyouare.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
add_header X-Content-Type-Options nosniff always;
location / { proxy_pass http://127.0.0.1:18789; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; 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 https; proxy_buffering off; proxy_read_timeout 3600s; proxy_send_timeout 3600s; } }
|
我的”解决方案”
为了让根路径自动跳转,我添加了 location = / 块:
1 2 3 4 5 6 7 8 9 10
| location = / { return 301 https://$host/chat?session=main&token=7173f...; }
location / { proxy_pass http://127.0.0.1:18789; }
|
看起来完美:精确匹配根路径做重定向,其他路径正常代理。
测试配置通过,重载 nginx:
1
| sudo nginx -t && sudo systemctl reload nginx
|
问题爆发
配置生效后,访问 https://openclaw.iambeyouare.com/ 出现错误:
1
| Disconnected from gateway. disconnected (1006): no reason
|
页面无法正常连接,WebSocket 断开。
问题分析
错误原因
OpenClaw 是一个实时聊天应用,依赖 WebSocket 长连接。工作流程如下:
- 浏览器访问根路径
/,加载 HTML 页面
- 页面中的 JavaScript 代码立即建立 WebSocket 连接到
/
- 我的配置对所有访问
/ 的请求都做了 301 重定向
- WebSocket 握手请求也被重定向到了
/chat?...
- WebSocket 连接失败 → 错误码 1006(异常断开)
核心问题
没有区分普通 HTTP 请求和 WebSocket 请求,一刀切的重定向导致 WebSocket 无法建立连接。
正确的修复方案
第一步:在 nginx.conf 中添加 WebSocket 检测
在 http 块中添加以下 map 指令:
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; }
|
$http_upgrade:HTTP 请求头中的 Upgrade 字段
- WebSocket 握手时,该字段值为
websocket
- 普通请求时,该字段为空或其他值
第二步:修改 openclaw.conf
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| server { listen 443 ssl; http2 on; server_name openclaw.iambeyouare.com;
ssl_certificate /etc/letsencrypt/live/iambeyouare.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/iambeyouare.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
add_header X-Content-Type-Options nosniff always;
location = / { if ($is_ws = 0) { return 302 /chat?session=main&token=7173f...; }
proxy_pass http://127.0.0.1:18789; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade;
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;
proxy_buffering off; proxy_read_timeout 3600s; proxy_send_timeout 3600s; }
location / { proxy_pass http://127.0.0.1:18789;
proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade;
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;
proxy_buffering off; proxy_read_timeout 3600s; proxy_send_timeout 3600s; } }
|
关键点
- 使用
if ($is_ws = 0) 判断是否为普通 HTTP 请求
- 只有非 WebSocket 请求才执行重定向
- WebSocket 请求正常代理到后端网关
- 使用 302 而非 301,避免浏览器缓存重定向规则
我的反思
错误原因总结
| 问题 |
说明 |
| 没考虑 WebSocket |
OpenClaw 是实时聊天应用,依赖 WebSocket 长连接 |
| 一刀切重定向 |
对所有 / 请求无差别重定向,包括 API 和 WebSocket |
| 缺乏场景分析 |
应该区分”人类访问页面”和”程序建立连接” |
教训
配置涉及 WebSocket 的服务时,必须:
- 识别 WebSocket 请求(通过
$http_upgrade 头)
- 只对普通 HTTP 请求做重定向
- WebSocket 连接必须原样代理到后端
正确的思考方式
在修改 nginx 配置前,应该问自己:
- 这个服务使用什么协议?(HTTP/HTTPS/WebSocket)
- 有哪些类型的请求会访问这个路径?
- 重定向会影响哪些请求?
- 是否有特殊请求需要例外处理?
最终效果
修复后:
- ✅ 访问
https://openclaw.iambeyouare.com/ → 浏览器自动跳转到聊天页面
- ✅ WebSocket 连接正常建立 → 实时消息功能正常
- ✅ 其他路径正常代理 → API、静态资源等均不受影响
参考资源
总结:这次踩坑让我深刻理解了 WebSocket 的工作原理和 nginx 配置的细节。在涉及实时通信的应用部署时,一定要仔细分析请求类型,避免”想当然”的配置。