本文记录一次在 Docker + Nginx 反向代理 场景下,部署 Open WebUI + Ollama 时,遇到 WebSocket 400 与前端解析异常的问题,以及完整的定位与修复过程。本文采用工程记录风格,省略与具体环境强绑定的细节(路径、域名、IP 已脱敏),可作为同类问题的参考模板。
一、问题现象
部署完成后,表现为:
- Web 页面可正常打开(HTTP 200)
- 但 WebUI 日志中持续出现:
GET /ws/socket.io/?EIO=4&transport=websocket 400
- 浏览器控制台或前端报错:
Unexpected token 'd', "data: {..." is not valid JSON
直观感受是:
- UI 能加载
- 但实时通信 / 流式输出异常
- 日志中大量 400,看起来像是服务未正确工作
二、初步排查:确认不是服务本身的问题
1. 确认容器状态
- Ollama 容器正常运行
- Open WebUI 容器正常运行
- 容器内部网络通信正常(WebUI 可访问 Ollama)
2. 直接访问容器端口
通过本机或内网直接访问 WebUI 暴露端口:
- 返回 HTML
- 日志正常
说明:
问题不在 Open WebUI 或 Ollama 本身
三、确认问题边界:反代之后才出问题
当通过 Nginx 反代访问时:
- 页面依然 200
- 但
/ws/socket.io请求在 WebUI 日志中返回 400 - 前端不断重试该 websocket 请求
结论很明确:
问题发生在 Nginx → Open WebUI 的反向代理层
四、问题本质分析
1. Open WebUI 使用了什么通信机制?
- WebUI 使用 Socket.IO / Engine.IO
- 表现为访问路径:
/ws/socket.io - 本质依赖 WebSocket(以及其握手流程)
2. 为什么会 400?
常见原因:
- Nginx 没有正确转发
Upgrade/Connection头 - WebSocket 请求被当作普通 HTTP GET 转发
- 后端(uvicorn / FastAPI)无法完成 websocket 握手,返回 400
此外,如果开启了:
proxy_buffering on- gzip
还可能破坏:
- SSE
- 流式输出
从而导致前端出现:
Unexpected token 'd'
(前端以 JSON 解析流式 data)
五、修复思路
核心原则只有三条:
- WebSocket 请求必须正确 Upgrade
- 普通 HTTP 请求不要被强制 upgrade
- 关闭 Nginx buffering / cache / gzip
工程上最稳妥的做法是:
为 WebSocket 单独拆一个 location
六、Nginx 配置修复方案(核心)
1. 在 http{} 中定义 upgrade map
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
2. server 块中拆分 WebSocket 与普通请求
server {
listen 443 ssl http2;
# WebSocket / Socket.IO
location /ws/ {
proxy_pass http://<webui_backend>;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600;
proxy_send_timeout 3600;
proxy_buffering off;
proxy_cache off;
gzip off;
}
# 普通 HTTP / SSE / 静态资源
location / {
proxy_pass http://<webui_backend>;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_buffering off;
proxy_cache off;
gzip off;
proxy_read_timeout 3600;
proxy_send_timeout 3600;
}
}
关键点:
/ws/强制 upgrade/根据请求头动态决定是否 upgrade
七、验证方式(避免被旧日志误导)
1. 只看最近日志
docker logs --since 2m open-webui
重点观察:
- 是否还在持续刷
/ws/socket.io ... 400
2. 检查 Nginx access log
grep socket.io access.log | tail
3. 功能验证(最重要)
- WebUI 页面正常
- 对话可创建
- 模型回复为流式输出
如果以上三点成立:
即可认定配置已经稳定
八、关于“残留 400 日志”的说明
在排查过程中,容易出现一种错觉:
以为问题还存在,其实只是翻到了旧日志
原因:
- 浏览器在早期配置错误时频繁重试 websocket
- 修复后日志并不会自动消失
正确做法永远是:
只看最近 1~2 分钟的日志
九、总结
本次问题的核心结论:
- Open WebUI 本身没有问题
- Ollama 本身没有问题
- 问题完全出在 Nginx 反向代理对 WebSocket / 流式请求的处理方式
经验要点:
- WebSocket 必须单独对待
- 不要在
location /里无脑Connection: upgrade - SSE / 流式输出一定要关 buffering
- 排错时务必区分“旧日志”和“当前状态”
该配置在长期运行、低配置主机、无 GPU 环境下均表现稳定,可作为 Open WebUI + Ollama 的反代参考模板。