Caddy 与 Nginx 的 WebSocket 代理配置有什么区别:升级头与超时
对比 Caddy 与 Nginx 的 WebSocket 反向代理配置,说明 HTTP/1.1 Upgrade、空闲超时和版本差异,并给出断连排查方法。
两者都能代理 WebSocket。本文示例采用传统 HTTP/1.1 Upgrade:Nginx 需要正确转发升级头;Caddy 的 reverse_proxy 会处理升级。连接建立后是否断开,还要看读空闲超时、连接总时长和中间层策略。下面给出配置示例与版本边界。
排查时先看握手是否成功,再看连接建立后的断开时间和各层日志。客户端出网代理的协议选择见HTTP、HTTPS、SOCKS5 代理怎么选。
Nginx 如何配置 WebSocket 升级头和读超时
```nginx
location /ws/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
proxy_buffering off;
}
```
Upgrade 与 Connection 用于传统 HTTP/1.1 握手;proxy_read_timeout 控制两次上游读取之间允许的空闲时间,长空闲连接应按应用心跳和实际链路设置。Nginx 官方文档对 HTTP/1.1 默认值有版本说明:1.29.7 之前需显式设置;示例保留显式配置以兼容较旧版本。升级后的连接按隧道工作,不能把普通 HTTP 响应缓冲直接当作握手条件。长连接保活见WebSocket 代理如何保持长连接。
Caddy 如何用 reverse_proxy 转发 WebSocket
```caddyfile
example.com {
reverse_proxy 127.0.0.1:8080
}
```
这就够了。Caddy 会在转发时处理升级,不需要手写 Upgrade 和 Connection。只有业务需要限制整条流连接的最长存活时间时才设置 stream_timeout;它不是空闲超时,达到上限时活动连接也会关闭。外层 example.com { } 与上面相同:
```caddyfile
reverse_proxy 127.0.0.1:8080 {
stream_timeout 24h
}
```
stream_timeout 限制流连接总时长,默认不设此上限;不要用它代替心跳或空闲检测。配置重载时的 stream_close_delay 是另一项行为。transport http 的读写超时也应按对应文档和应用行为核实。配置项见 Caddy reverse_proxy 文档。
代理本身的主机、端口和鉴权写法,见帮助中心的代理设置。
升级头、空闲超时和响应缓冲会怎样影响连接
| 对比项 | Nginx | Caddy | 实际影响 |
|---|---|---|---|
| HTTP/1.1 Upgrade | 通常需显式转发升级头;HTTP/1.1 默认值受版本影响 | reverse_proxy 处理升级 | 配置与版本不匹配可能导致握手失败 |
| 空闲读超时 | proxy_read_timeout 控制上游读取空闲间隔 | 依据具体传输配置 | 按心跳和链路实际空闲策略判断 |
| 流连接总时长 | 由具体配置决定 | stream_timeout 可设总时长上限 | 到时会关闭活动连接 |
| 响应缓冲 | Upgrade 后是双向隧道;按实际数据路径排查 | 流式转发 | 不作为握手成功的必备条件 |
| 证书 | 需另配 certbot | 自动申请续期 | 部署步骤数量不同 |
| HTTP/2 | 取决于客户端与服务器实现 | 取决于实现 | RFC 8441 定义扩展 CONNECT,支持情况需逐端核实 |
若使用传统 Upgrade,按 HTTP/1.1 握手流程排查;HTTP/2 扩展 CONNECT 是否可用取决于客户端、代理和上游实现。若只有特定目标失败,比较该目标的响应和代理日志,不要直接归因于出口。
如何设置 WebSocket 保活间隔并定位断连
按链路最短空闲超时设置 ping 间隔
链路上任一层的空闲策略都可能关闭连接,实际时限需查对应 CDN、负载均衡和应用配置,并用边缘及后端日志测量。不要把未经核实的固定秒数当作通用默认值。应用层心跳间隔应依据实测到的最短空闲时限设置,并留出网络抖动余量。
握手失败或连接中断时按四步排查
排障按这个顺序走,不要跳步:
1. curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==" http://你的域名/ws/,看是否返回 101 Switching Protocols
2. 没有 101 就查 Nginx 的头和 proxy_pass 路径
3. 有 101 但很快断,记录断开间隔并对照各层实际超时配置与日志
4. 断得没有规律,结合代理、CDN 和后端日志检查连接限制或异常关闭原因
根据 400、502、504 和固定时长断连定位故障层
出现下面这些现象时,先查对应的一层,不要先改代理:
| 现象 | 先查哪一层 | 判断动作 |
|---|---|---|
| 握手返回 400 或 426 | Nginx 升级头 | 核对 Upgrade、Connection 和 HTTP/1.1 |
| 返回 502 | 后端 | 后端进程、端口、路径是否与 proxy_pass 一致 |
| 返回 504 | Nginx 读超时 | 看后端处理耗时,再调 proxy_read_timeout |
| 101 之后 60 秒整断 | Nginx | proxy_read_timeout 仍是默认值 |
| 101 后固定时长断开 | CDN、负载均衡或应用 | 对照该层配置及日志确认空闲策略 |
| 消息延迟或成批到达 | 应用写入、代理或中间层 | 检查数据流和缓冲行为,不据此判断握手失败 |
超时、连接被拒和 TLS 证书错误是三组不同原因:超时多半卡在链路或上游,连接被拒说明端口那头没在听,证书错误说明中间有人在拦截或替换证书。先按 curl 报出的类型分开,再决定动哪一端。
FAQ
Caddy 还要不要显式写 Upgrade 头
不用。v2 的 reverse_proxy 会自动处理升级,手写反而可能覆盖掉它自己的逻辑。只有当你需要改写头内容时才用 header_up。
Nginx 报 502 和 504 分别查什么
502 是后端拒绝或崩溃,查后端进程和端口;504 是后端在超时内没响应,查 proxy_read_timeout 和后端处理耗时。两者都不指向代理 IP 本身。
HTTP/2 下 WebSocket 还能用吗
RFC 8441 定义了 HTTP/2 的扩展 CONNECT;是否可用取决于客户端、代理及服务端实现。本文配置示例采用传统 HTTP/1.1 Upgrade,部署时应核实整条链路支持的方式。
如果长连接主要面向需要稳定出口的业务后台,用静态住宅代理 IP 比频繁更换出口更省事,出口固定之后,超时和缓冲问题也更容易复现。
