WebSocket 返回 426 Upgrade Required
http GET /chat HTTP/1.1 Host: example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13
如果一切正常,服务器会返回 `101 Switching Protocols`,然后双方开始 WebSocket 通信。但是,当服务器返回 **426** 时,响应可能会包含:
```http
HTTP/1.1 426 Upgrade Required
Upgrade: WebSocket
Connection: Upgrade
注意这里的关键区别:服务器虽然返回了 Upgrade: WebSocket,但仍然给出了 426 错误。这通常表示服务器认可升级机制,但当前的网关、代理、负载均衡或后端服务没有正确配置 WebSocket 支持。
为什么 WebSocket 会返回 426?常见原因分析
1. 反向代理或负载均衡器未启用 WebSocket 支持
许多现代部署使用 Nginx、Apache、HAProxy 等作为前端代理。如果代理层没有显式配置 WebSocket 升级,它会把 Upgrade 和 Connection 头过滤掉,或者直接返回 426。
典型场景:
- Nginx 没有设置
proxy_set_header Upgrade $http_upgrade;和proxy_set_header Connection "upgrade";。 - AWS Application Load Balancer 或 API Gateway 的 WebSocket 路由未正确配置。
- Cloudflare 等 CDN 默认不理解 WebSocket 协议(需开启 WebSocket 支持)。
2. 服务器端代码未正确处理升级
即使代理层配置正确,如果 WebSocket 服务端框架没有正确响应升级请求,也可能触发 426。
例如:
- 在 Node.js 中,如果直接使用
http.createServer而没有通过ws库接管upgrade事件,所有 WebSocket 请求会落入普通的 HTTP 处理逻辑,手动返回 426 或 404。 - 在 ASP.NET Core 或 Django Channels 中,WebSocket 中间件未被添加到管道中,请求无法匹配到 WebSocket 处理器。
3. 环境或开发工具限制
- 本地开发时使用 Vite、webpack‑dev‑server 的开发服务器,如果不支持 WebSocket 代理转换,可能直接返回 426。
- 某些免费线上环境(如 Glitch、部分 Serverless 平台)本身不支持 WebSocket,会返回 426 或 400 等错误。
- 防火墙或安全组拦截了 WebSocket 所需的非标准端口或协议。
4. 客户端请求格式错误
虽然少见,但客户端发送的升级请求头格式不符合规范时,服务器也可能返回 426。例如缺少 Upgrade: websocket 或 Connection: Upgrade 头。
从 0 到 1 修复 426 Upgrade Required
下面我们按照从简单到复杂的顺序,逐一给出排查步骤和配置代码。
步骤一:确认 WebSocket 地址正确
确保客户端连接的地址前缀是 ws://(非加密)或 wss://(加密),而不是 http:// 或 https://。如果使用的是浏览器的 new WebSocket('http://...') 就会立即失败,可能被浏览器或服务器转为 HTTP 请求并接收 426。
正确示例:
// 正确 – 使用 wss 协议
const socket = new WebSocket('wss://example.com/socket');
步骤二:检查基础连接能力
用 curl 模拟 WebSocket 握手,观察服务器返回的响应头。这可以快速定位问题是否出在代理层或后端。
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Key: test" -H "Sec-WebSocket-Version: 13" \
https://your-domain.com/ws-path
期望返回:HTTP/1.1 101 Switching Protocols
如果返回 426:继续往下排查。
步骤三:修复反向代理配置(以 Nginx 为例)
大多数生产环境中,WebSocket 会通过 Nginx 代理到上游服务。必须确保 Nginx 将升级请求头原样转发。
打开对应的 server 或 location 块,添加以下关键配置:
location /ws {
proxy_pass http://backend;
proxy_http_version 1.1;
# 这两行是 WebSocket 支持的核心
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
重要解释:
proxy_http_version 1.1:WebSocket 需要 HTTP/1.1 的持久连接。proxy_set_header Connection "upgrade":必须逐字设置为"upgrade",不能使用变量。- 如果你使用
proxy_set_header Connection $connection_upgrade;的话,需要在http块中提前定义 map:map $http_upgrade $connection_upgrade { default upgrade; '' close; }
对于 Apache httpd,需要启用 mod_proxy_wstunnel:
ProxyPass /ws/ ws://backend/ws/
ProxyPassReverse /ws/ ws://backend/ws/
步骤四:确保后端框架正确处理 WebSocket
检查你的服务端代码是否正确注册了 WebSocket 路由。不同的语言和框架做法不同,下面列举几种常见的修复。
Node.js + ws 库
错误示例(直接使用 http server,没有 ws 处理):
const http = require('http');
http.createServer((req, res) => {
res.writeHead(200);
res.end('hello');
}).listen(3000);
// 缺少对 upgrade 事件的处理 → 外部请求会收到 426
正确做法:引入 ws 并让它接管 upgrade 事件。
const server = require('http').createServer();
const WebSocket = require('ws');
const wss = new WebSocket.Server({ server });
server.on('request', (req, res) => {
// 处理普通 HTTP 请求
res.end('ok');
});
server.listen(3000);
如果 ws 库绑定在另一个端口,确保 Nginx 或直连地址能指向该端口。
Python + FastAPI / Flask
FastAPI 天然支持 WebSocket,但必须确保路由以 @app.websocket 定义,并且应用运行在支持 WebSocket 的服务器上(如 Uvicorn)。
from fastapi import FastAPI, WebSocket
app = FastAPI()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Message text was: {data}")
如果使用 Flask,通常需要 Flask‑SocketIO 或 gevent‑websocket 来支持,并且启动方式不能是默认的 Flask 开发服务器。
Go + gorilla/websocket
确保 Upgrade 函数被正确调用,并且没有因为中间件提前返回。
var upgrader = websocket.Upgrader{}
func wsHandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("upgrade failed: ", err)
return
}
defer conn.Close()
// 处理消息...
}
ASP.NET Core
需要在 Startup.cs 或 Program.cs 中添加 WebSocket 中间件并映射路由。
app.UseWebSockets();
app.Map("/ws", async context => {
if (context.WebSockets.IsWebSocketRequest)
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
// 处理消息
}
else
{
context.Response.StatusCode = 400;
}
});
步骤五:检查云平台和 PaaS 的额外设置
- Heroku:需要在
Procfile中使用web进程类型,并绑定$PORT,Heroku 路由器默认支持 WebSocket。 - AWS Elastic Beanstalk / ALB:确保负载均衡器的目标组协议支持 WebSocket。ALB 默认支持,但要设置
stickiness保持会话。 - Google App Engine:标准环境需要设置
network: session_affinity为true;灵活环境通常直接支持。 - Vercel / Netlify:这些平台是 Serverless 函数,不支持长时间 WebSocket 连接,必须使用 Serverless‑to‑WebSocket 网关或改用其他平台。
步骤六:开发服务器场景的特殊处理
在本地开发时,如果使用 React (CRA) 或 Vue (Vite) 的开发服务器,直接给它们添加 WebSocket 代理需要额外配置。
例如 Vite 中,可以在 vite.config.js 中配置代理,并确保开启 WebSocket 支持:
export default {
server: {
proxy: {
'/ws': {
target: 'http://localhost:3001',
ws: true, // 关键:开启 WebSocket 代理
changeOrigin: true,
}
}
}
}