nginx代理导致websocket跨域连接失败的本质是未完整透传握手请求,必须精准配置proxy_http_version 1.1、proxy_set_header upgrade $http_upgrade、proxy_set_header connection "upgrade"三要素,并关闭缓冲、延长超时、禁用缓存,且跨域响应头须由后端返回而非nginx干预。

WebSocket 代理跨域连接失败,本质不是跨域问题本身没解决,而是 Nginx 没把原始握手请求完整、正确地透传给后端——导致后端根本收不到 Origin、Upgrade 等关键信息,自然无法完成校验和响应。浏览器看到的 400、426、连接 pending 或无日志等现象,基本都指向这个环节被阻断。
确保三行协议升级头精准透传
这是 WebSocket 握手不被“截胡”的前提,缺一不可:
- proxy_http_version 1.1:HTTP/1.0 不支持 Upgrade 机制,必须显式启用;写成 1.0 或遗漏会直接导致握手降级失败
- proxy_set_header Upgrade $http_upgrade:变量名是 $http_upgrade(带 http_ 前缀),大小写敏感;不能写成 $upgrade 或硬编码 websocket 字符串
- proxy_set_header Connection "upgrade":必须是双引号包裹的字面量 "upgrade",不是 $connection_upgrade 变量(除非你已用 map 显式定义);Nginx 默认发 Connection: close,会直接掐断升级意图
关闭缓冲与延长超时,防止长连接被误杀
WebSocket 是双向长连接,Nginx 默认行为会主动中断空闲链路:
- proxy_buffering off:避免 Upgrade 响应被缓存延迟,造成握手超时或响应错乱
- proxy_read_timeout 86400 和 proxy_send_timeout 86400:设为 24 小时级(如 86400 秒),防止空闲期被断连;不要沿用默认的 60 秒
- proxy_cache_bypass $http_upgrade:确保含 Upgrade 头的请求绕过缓存;同时禁用 proxy_cache 相关指令,WebSocket 帧不可缓存
跨域头必须由后端返回,Nginx 不要干预
浏览器只在 101 Switching Protocols 响应中检查 CORS 头,且要求精确匹配:
- 不要在 Nginx 的 location 块里加 add_header Access-Control-Allow-Origin "*":101 响应默认不触发 add_header;加了 always 也可能覆盖后端对 credentials 的精确校验
- 后端必须返回完整头:Access-Control-Allow-Origin(若前端带 cookie,就不能用 *,需回传原始 Origin)、Access-Control-Allow-Credentials: true、必要时还有 Access-Control-Allow-Headers
- Nginx 默认透传这些响应头,只要后端写了,就无需额外配置
排查前端与网络层常见干扰项
很多“400”或“无日志”问题其实卡在更外层:
- 前端 WebSocket 地址写 ws://localhost?浏览器里的 localhost 指的是用户本机,不是服务器;应使用与页面同源的域名或明确 IP
- HTTPS 页面访问 ws://(非 wss://)?现代浏览器直接阻止混合内容,连接卡在 pending 后静默失败
- SSL 证书不匹配或自签名?wss 连接会因证书错误被浏览器立即终止,控制台有明确提示
- access_log 为空?先确认请求是否真到达 Nginx:检查监听端口、server_name 匹配、防火墙及 Docker 端口映射是否生效











