WebSocket 返回 426 Upgrade Required

FreeGuideOnline 最新 2026-07-04

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 升级,它会把 UpgradeConnection 头过滤掉,或者直接返回 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: websocketConnection: 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 将升级请求头原样转发。

打开对应的 serverlocation 块,添加以下关键配置:

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.csProgram.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_affinitytrue;灵活环境通常直接支持。
  • 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,
      }
    }
  }
}