websocket握手成功必须配置proxy_set_header upgrade $http_upgrade和proxy_set_header connection "upgrade",并设置proxy_http_version 1.1;前者动态透传客户端upgrade值确保兼容性,后者显式声明升级意图,二者缺一不可。

要让 WebSocket 握手成功,proxy_set_header Upgrade $http_upgrade 不是可选项,而是必须项——它负责把客户端发来的 Upgrade: websocket 请求头原样透传给后端服务。如果漏掉或写错,后端根本收不到升级意图,只能按普通 HTTP 处理,直接返回 400 或 502,连接在建立前就失败。
为什么必须用 $http_upgrade 而不是固定值?
客户端发起 WebSocket 握手时,请求头中会带 Upgrade: websocket(也可能为其他合法值,比如某些 IoT 设备用 Upgrade: mqtt)。Nginx 默认不透传 Upgrade 头,且若硬写成 proxy_set_header Upgrade "websocket",就会覆盖原始值,导致非标准客户端握手失败。
- 用
$http_upgrade可动态捕获客户端实际发送的 Upgrade 值,兼容性更好 - 该变量由 Nginx 自动提取请求头中的
Upgrade字段,为空时也为空,不会误传 - 配合
proxy_http_version 1.1才构成完整升级前提,HTTP/1.0 不支持 Upgrade 机制
必须同步设置 Connection 头
Upgrade 头只是“申请升级”,真正触发协议切换的是 Connection: upgrade。Nginx 默认转发时可能把 Connection 改成 close 或 keep-alive,这就断掉了升级链路。
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
- 必须显式写
proxy_set_header Connection "upgrade"(注意双引号包裹,不是变量) - 不能写成
$connection,该变量通常为空,会导致 Nginx 发送Connection: close - 更稳妥的做法是配合 map 指令做条件判断:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
然后在 location 中用proxy_set_header Connection $connection_upgrade,这样普通 HTTP 请求仍走 keep-alive,WebSocket 请求才升级
位置和上下文不能错
这行配置必须放在 location 块内,且紧邻 proxy_pass 之后,否则可能被外层配置覆盖或不生效。
- 不要放在 http 或 server 全局块里,它只对当前代理路径有效
- 推荐单独为 WebSocket 流量设路径,如
location /ws/ { ... },避免影响其他接口 - 若使用宝塔等可视化面板,在反向代理配置页底部「配置文件」区域手动追加,确认缩进与 proxy_pass 同级
验证是否生效
配置完 reload Nginx,用 curl 模拟握手最直接:
curl -i -H "Connection: upgrade" -H "Upgrade: websocket" http://your-domain/ws- 看到响应头含
HTTP/1.1 101 Switching Protocols和Upgrade: websocket,说明握手通了 - 浏览器控制台不再报 "Error during WebSocket handshake" 或 "closed before established"










