问题现象
- 后端服务运行在
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 _; # 匹配任意 host
location / {
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”到“连接成功”,看似是配置问题,实则是网络拓扑理解偏差。希望本文能帮你少走弯路!
到此这篇关于nginx代理websocket失败的完整排查过程的文章就介绍到这了,更多相关nginx代理websocket失败内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论