Apache本身不原生支持WebSocket长连接,核心是通过mod_proxy_wstunnel透传Upgrade请求并维持TCP隧道,真正双向通信由后端服务实现;必须启用mod_proxy、mod_proxy_http和mod_proxy_wstunnel三模块,ProxyPass须用ws://或wss://前缀,配置keepalive=On、Timeout与KeepAliveTimeout调优,并确保后端正确响应ping/pong帧。

Apache 本身不原生支持 WebSocket 长连接,mod_proxy_wstunnel 不是双向通信的实现者,而是透传升级请求的桥梁。真正实现双向(full-duplex)通信的是后端 WebSocket 服务——Apache 只负责把客户端的 Upgrade: websocket 请求准确、稳定地转给它,并维持住这条 TCP 连接不被误杀。
必须启用并验证的模块组合
缺一不可,否则代理直接失败或降级为 HTTP:
-
mod_proxy(基础代理框架) -
mod_proxy_http(内部依赖,不是可选;wstunnel 底层复用其连接管理) -
mod_proxy_wstunnel(核心模块,处理 Upgrade 协议识别与隧道建立)
检查命令:apachectl -M | grep -E "(proxy|wstunnel)",应同时看到三者。Ubuntu/Debian 用户可用 a2enmod proxy proxy_http proxy_wstunnel 启用。
关键配置顺序与写法
WebSocket 路由必须前置,且协议前缀必须是 ws:// 或 wss://:
- 错误写法:
ProxyPass "/ws/" "http://127.0.0.1:8080/ws/"→ 走 HTTP 代理,握手失败 - 正确写法:
ProxyPass "/ws/" "ws://127.0.0.1:8080/ws/" keepalive=On - 必须加
keepalive=On:避免每个帧都新建 TCP 连接,大幅降低后端压力 - 必须写在通用规则之前,例如:
ProxyPass "/ws/" ...要在ProxyPass "/" "http://..."之前
高并发必需的底层调优
默认 prefork MPM 和超时参数完全无法支撑千级长连接:
-
切换 MPM 到 event 模式:
a2dismod mpm_prefork && a2enmod mpm_event(prefork 下 1000 连接 ≈ 1000 进程,内存爆炸) -
Timeout 600:防止后端响应延迟导致连接卡死 -
KeepAlive On且KeepAliveTimeout 300:要明显大于后端心跳间隔(如 30 秒 ping) -
MaxKeepAliveRequests 0:长连接场景下不限制请求数,避免频繁重连
后端服务必须主动响应 ping 帧
Apache 会定期发送 WebSocket ping 帧(默认约每 60 秒),若后端不回 pong,mod_proxy_wstunnel 就判定连接失效并关闭它——这是“能发不能收”最常见原因:
- Node.js ws 库:默认已处理 ping/pong,无需额外代码
- Spring Boot:需注册
PongMessageHandler或启用WebSocketHandlerRegistry的心跳支持 - Go gorilla/websocket:必须调用
conn.SetPingHandler(),且确保conn.ReadMessage()或conn.NextReader()持续运行(否则 ping 帧堆积不触发回调)
防头清洗与调试确认
CDN、WAF 或 ModSecurity 常会 strip Upgrade 和 Connection 头,导致升级失败:
- 检查请求头是否到达 Apache:
curl -H "Upgrade: websocket" -H "Connection: Upgrade" http://your-apache/ws/test - 若后端日志没收到 Upgrade 请求,优先排查中间设备是否清洗了这两个头
- 浏览器 Network 中看到 101 状态码 ≠ 连接真走 wstunnel,要用
tcpdump或后端日志确认 ws:// 流量是否抵达











