Apache默认不原生支持WebSocket,需启用mod_proxy_wstunnel模块并配置ProxyPass ws://透传Upgrade请求,禁用干扰模块、切换event MPM、调优KeepAlive参数,且后端必须主动处理ping/pong心跳。

Apache 默认配置撑不住 WebSocket 高并发,必须调模块行为、改 MPM 模式、透传关键头 —— 否则 500 连接就 OOM 或 502。
mod_proxy_wstunnel 在 2.4.47+ 已被标记为弃用,但仍是生产主力
官方建议用 mod_proxy_http + Upgrade 处理协议升级,但实测中 mod_proxy_wstunnel 对 WebSocket 的语义支持更稳,尤其在心跳保活和错误连接清理上更可控。2.4.47 之后它没被移除,只是“不鼓励新项目用”,现有服务继续维护没问题。
必须确认已启用依赖模块:
-
mod_proxy(必需) -
mod_proxy_http(非可选,mod_proxy_wstunnel内部依赖它做底层连接) -
mod_proxy_wstunnel(主模块)
检查命令:httpd -M | grep -E "(proxy|wstunnel)",缺一不可。
ProxyPass 必须带 keepalive=On 且路由要前置
不加 keepalive=On,每个 WebSocket 请求都会新建 TCP 连接,后端压力翻倍;不前置 WebSocket 路由,HTTP 的 ProxyPass / 会拦截所有请求,/ws/ 根本到不了后端。
正确写法示例:
ProxyPass "/ws/" "ws://backend:8080/ws/" keepalive=On ProxyPassReverse "/ws/" "ws://backend:8080/ws/" ProxyPass "/" "http://backend:8080/"
注意顺序:WebSocket 规则一定要写在通用 HTTP 规则之前。如果用 RewriteRule 动态识别 Upgrade,则必须开启 RewriteEngine On 并确保 %{HTTP:Upgrade} 和 %{HTTP:Connection} 未被清洗 —— CDN 或某些安全模块(如 ModSecurity)默认会 strip 这两个头。
安全更新和维护 CLI Proxy API(CPA)部署与配置。用于 CPA 镜像升级、配置变更、认证目录兼容修复、上线验证与回滚。适用于用户提到“CPA 更新/升级/配置改了/容器重建/回滚”等场景。
MPM 必须切 event,Timeout 和 KeepAliveTimeout 要协同设
prefork 模式下每个连接占一个进程,1000 连接 ≈ 1000 个进程,内存爆炸;worker 模式线程复用仍不够;只有 event MPM 支持真正的异步 I/O,能单线程管理数千空闲连接。
确认当前 MPM:httpd -V | grep MPM;切换方式(Ubuntu/Debian):a2dismod mpm_prefork && a2enmod mpm_event。
关键参数必须同步调整:
-
Timeout 600:防止后端响应慢导致连接卡死 -
KeepAlive On:允许复用连接 -
KeepAliveTimeout 300:要比后端心跳间隔(如 30 秒 ping)大得多,否则 Apache 自己先关连接 -
MaxKeepAliveRequests 0:长连接场景下不限制请求数,避免频繁重连
这些值不匹配,就会出现“浏览器显示 101 Switching Protocols 成功,但发几条消息就断”——其实不是协议问题,是 Apache 主动 kill 了“空闲太久”的连接。
后端不回 pong 就算配全了也会断连
mod_proxy_wstunnel 本身不发 ping,但它会透传后端的 ping/pong 帧;而 Apache 会在连接空闲时(默认每 60 秒)向后端发 ping,若后端没在超时时间内回 pong,它就关闭连接并记录类似 client denied by server configuration 的日志(实际是 upgrade 协议握手后的心跳失败)。
所以后端必须显式处理 ping:
- Node.js
ws库:默认启用,无需干预 - Python
websockets:需在连接生命周期内调用await ws.ping()并确保ws.recv()或ws.wait_closed()持续运行 - Go
gorilla/websocket:必须调conn.SetPingHandler(...),且不能阻塞conn.ReadMessage()
最容易忽略的一点:前端 WebSocket 客户端发的 ping 帧,Apache 不会自动转成 pong 回去,必须后端自己处理 —— 这不是代理层该干的事,mod_proxy_wstunnel 只透传。

















