envoy代理websocket需显式启用upgrade支持、正确配置超时与心跳、处理tls终止和路径匹配,并实现完整握手的健康检查,否则易断连。

Envoy 能代理 WebSocket,但默认配置会断连——关键在升级头和连接保活,不是加个 route 就完事。
WebSocket 握手必须透传 Upgrade 头
Envoy 默认把 HTTP/1.1 升级请求当普通请求处理,直接丢掉 Upgrade 和 Connection: upgrade 头,导致后端 Go 服务收不到握手信号,返回 400 或直接 EOF。
- 必须在 Envoy 的
http_connection_manager配置中显式启用升级支持:upgrade_configs下添加upgrade_type: websocket -
route配置里不能设auto_host_rewrite: true,否则可能篡改Host头,触发 Go 的websocket.Upgrader.CheckOrigin拒绝 - 若后端用
gorilla/websocket,CheckOrigin返回true仅够开发用;生产环境建议校验r.Header.Get("Origin"),而不是无条件放行
超时设置不匹配会导致连接静默中断
Envoy 默认的 idle timeout 是 5 分钟,而 Go 的 http.Server 默认没有设置 ReadTimeout 或 WriteTimeout,但 gorilla/websocket 的 SetPingHandler 和 SetPongHandler 依赖底层连接活跃性。一旦 Envoy 先断开,客户端收不到 close 帧,表现为“卡住”或重连失败。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 在 Envoy 的
route或virtual_host级别设idle_timeout: 0s(禁用)或调大到足够值,如3600s - Go 服务侧要显式配置
http.Server的IdleTimeout,且必须 ≥ Envoy 的 idle timeout,否则 Go 先关连接,Envoy 不知道,后续 write 会报broken pipe - 务必启用心跳:在 Go 的
websocket.Conn上调用SetPingHandler并定期WriteMessage(websocket.PingMessage, nil),否则某些 NAT 或中间设备会单向 kill 连接
路径路由和 TLS 终止容易忽略协议降级风险
如果 Envoy 终止 TLS(即用 wss:// 访问 Envoy,再以 ws:// 转发到后端),Go 服务无法感知原始协议是加密的,r.TLS 为 nil,可能导致鉴权逻辑误判或重定向跳转出错。
- Envoy 需在
http_connection_manager中设置set_current_client_cert_details: true,并把X-Forwarded-Proto: wss注入请求头 - Go 服务应检查
r.Header.Get("X-Forwarded-Proto") == "wss"来判断是否走安全通道,而不是依赖r.TLS != nil - 路径匹配别用前缀硬写死,比如
/ws;Envoy 的path_prefix路由若没配case_sensitive: false,大小写不一致的请求(如/WS)会被 404,而浏览器 WebSocket 构造函数对路径大小写敏感
最常被跳过的其实是健康检查:Envoy 对 WebSocket 后端做主动探测时,不能只发 HTTP GET,得模拟一次完整握手,否则上游节点明明已挂,Envoy 还持续转发流量过去。这需要自定义 health_check 的 http_health_check 并带 upgrade_type: websocket,否则等于没检。










