Upgrade 表达什么
请求里的 Upgrade 头表达客户端希望切换到的协议(websocket、h2c 等);服务器同意就回 101,忽略它就按原协议继续响应。它必须与 Connection: Upgrade 一起出现,否则中间设备不会按升级语义处理(RFC 9110 §7.8)。
逐跳属性带来的问题
Upgrade 是逐跳字段:代理必须自己处理,不能简单原样转发给下一跳。所以 WebSocket 握手失败时,问题常常出在反向代理配置上——它需要显式转发升级相关的头,并且不能按普通短请求那样很快回收这条连接。
基本信息
| 名称 | Upgrade |
|---|---|
| 作用 | 协议升级协商 |
| 规范 | RFC 9110 §7.8 |
| 配合字段 | Connection: Upgrade |
| 结果 | 服务器接受则回 101,忽略则按原协议响应 |
典型使用场景
- WebSocket 握手:从普通 HTTP 请求升级为长连接
- 内网服务的明文 HTTP/2 升级,多见于服务间通信
- 排查 WebSocket 经反向代理后握手失败
- 排查长连接被固定时长掐断的问题
WebSocket 升级请求
WebSocket 升级请求
GET /socket HTTP/1.1
Host: ws.example.com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==
# 服务器同意时回 101 Switching Protocols,此后按 WebSocket 语义解析字节流常见现象对照
| 现象 | 原因 | 处理 |
|---|---|---|
| 返回 200 而不是 101 | 上级不支持升级或未转发升级头 | 检查代理配置是否转发升级相关头 |
| 连接建立后固定时长断开 | 代理的读超时太短 | 提高长连接的读超时并保持心跳 |
| 内网明文升级失败 | 链路设备不支持明文 HTTP/2 升级 | 改用在 TLS 上通过 ALPN 协商 |
| 部分节点正常部分失败 | 多级链路中只有一级做了配置 | 逐跳核对每一级的配置 |
明文 HTTP/2 升级在公网极少见:主流做法是在 TLS 上通过 ALPN 直接协商 HTTP/2,不需要 Upgrade 这一套。
收到 101 之后这条连接就不再是 HTTP 语义:双方按新协议解析字节流,抓包工具也要按新协议解码才能看懂。
代理侧配置要点
- 转发升级相关的头,并确保上游确实支持该协议
- 为这类路径单独放宽读超时,避免长连接被固定时长掐断
- 关闭可能破坏双向流的响应缓冲,别把帧攒起来再发
- 按路径把连接转发到正确的上游,避免升级请求落到不支持的服务上
长连接的保活主要靠应用层心跳:只调大超时对抗不了中间设备的空闲回收,心跳还能顺带发现对端已经消失的连接。