Nginx 代理 WebSocket 失败排查全过程:从 426 到连接成功的完整复盘
Nginx 代理 WebSocket 失败排查全过程:从 426 到连接成功的完整复盘
- 🧩 问题现象
- 🔍 初步怀疑:Nginx 配置错误?
- 🕵️♂️ 关键转折:前端页面来源被忽略
- ❌ 问题本质
- ✅ 解决方案
- 方法 1:显式指定虚拟机 IP(临时可用)
- 方法 2:使用相对路径(推荐 ✅)
- 🛠 附:正确的 Nginx 配置模板
- 💡 经验总结
- 📌 最后提醒
- ✅ 结语
关键词:Nginx、WebSocket、Docker、426 Upgrade Required、前端连接失败、代理配置
在开发基于 WebSocket 的实时应用时,使用 Nginx 作为反向代理是常见做法。可是我遇见了后端直连 WebSocket 成功,通过 Nginx 代理却失败。本文将完整复盘一次真实排查过程,带你避开所有“坑”。
🧩 问题现象
- 后端服务运行在
192.168.1.10:8080
,提供 WebSocket 接口/ws/xxx
- 使用
wscat
或浏览器直连ws://192.168.1.10:8080/ws/xxx
✅ 成功 - 配置 Nginx 反向代理后,前端连接
ws://localhost/ws/xxx
❌ 失败 - 浏览器 Network 面板显示 WebSocket 请求失败(无 101 响应)
- 关键线索:Nginx 的
access_log
中 没有任何 WebSocket 请求日志
🔍 初步怀疑:Nginx 配置错误?
首先检查 Nginx 配置:
map $http_upgrade $connection_upgrade {default upgrade;'' close;
}upstream webservers {server 192.168.1.10:8080;
}server {listen 80;server_name localhost;location /ws/ {proxy_pass http://webservers/ws/;proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "$connection_upgrade"; # ⚠️ 这里有坑!proxy_read_timeout 3600s;}
}
常见错误包括:
Connection
头加了双引号 → 变成字面量map
没放在http
块顶层- 缺少
proxy_buffering off
但即使修正这些,日志依然为空,说明:请求根本没到 Nginx!
🕵️♂️ 关键转折:前端页面来源被忽略
通过 docker ps
确认端口映射正常:
nginx 0.0.0.0:80->80/tcp
但发现一个致命细节:
前端页面是通过虚拟机 IP 打开的:
http://192.168.228.128/
而 WebSocket 地址写的是:ws://localhost/ws/xxx
❌ 问题本质
- 浏览器中的
localhost
永远指向客户端本机(Windows 电脑) - 而我的 Nginx 运行在 虚拟机中
- 所以
ws://localhost/...
实际请求的是 我 Windows 的 80 端口,而非虚拟机!
→ 请求压根没发给 Nginx,自然没有日志!
✅ 解决方案
方法 1:显式指定虚拟机 IP(临时可用)
// 前端 JS
const ws = new WebSocket('ws://192.168.228.128/ws/xxx');
✅ 立即生效!
方法 2:使用相对路径(推荐 ✅)
// 自动继承当前页面的 host
const ws = new WebSocket('/ws/xxx');
优势:
- 页面在
http://localhost
→ 连ws://localhost
- 页面在
http://192.168.228.128
→ 连ws://192.168.228.128
- 部署到域名也无需修改代码
🛠 附:正确的 Nginx 配置模板
http {# 必须在 http 块顶层!map $http_upgrade $connection_upgrade {default upgrade;'' close;}upstream backend {server host.docker.internal:8080; # Docker Desktop 推荐# 或 server 192.168.1.10:8080;}server {listen 80;server_name _; # 匹配任意 Hostlocation / {root /usr/share/nginx/html;index index.html;}location /ws/ {access_log /var/log/nginx/ws_access.log;proxy_pass http://backend/ws/;proxy_http_version 1.1;proxy_buffering off; # 必须关闭proxy_cache off;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection $connection_upgrade; # 无引号!proxy_read_timeout 3600s;}}
}
💡 经验总结
问题类型 | 排查要点 |
---|---|
Nginx 无日志 | 先确认请求是否真的发到了 Nginx(检查前端地址、端口映射) |
WebSocket 426 | 检查 Upgrade 和 Connection 头是否正确传递 |
前端连接失败 | 确保 WebSocket 地址与页面同源(避免硬编码 localhost ) |
Docker 环境 | 用 host.docker.internal 代替 IP 更可靠 |
📌 最后提醒
localhost
在前端代码中 ≠ 你的服务器!
它永远指向用户浏览器所在的机器。
开发时务必注意页面来源与 WebSocket 目标的匹配关系,这是 WebSocket 代理失败的最高频原因。
✅ 结语
从“426 Upgrade Required”到“连接成功”,看似是配置问题,实则是网络拓扑理解偏差。希望本文能帮你少走弯路!
欢迎点赞、收藏、评论交流!
如果你也在 Docker + Nginx + WebSocket 中踩过坑,欢迎留言分享你的故事!