必须启用mod_proxy_wstunnel模块,否则apache忽略upgrade和connection头导致websocket握手后静默断连;proxypass须用ws://或wss://协议前缀,timeout和proxytimeout均需调大至3600秒以维持长连接。

mod_proxy_wstunnel 必须启用,否则根本不会处理 WebSocket Upgrade
Apache 默认完全忽略 Upgrade: websocket 和 Connection: upgrade 头,直接按普通 HTTP 流程走,结果就是握手成功但后续帧收发失败、连接静默断开。这不是后端问题,是 Apache 根本没进隧道逻辑。
确认方式:httpd -M | grep proxy 输出中必须包含 proxy_wstunnel_module。若缺失,需在配置中显式加载:
LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
Debian/Ubuntu 用户可直接运行:a2enmod proxy proxy_http proxy_wstunnel,但执行前务必 apachectl configtest —— 模块未加载时启动会报 “Invalid argument”,日志里找不到具体哪行错。
ProxyPass 必须用 ws:// 或 wss://,不能写 http://
写成 ProxyPass /ws/ http://127.0.0.1:8080/ws/ 看似能通,实则走的是 mod_proxy_http,它会重写或丢弃 Upgrade 相关头,导致浏览器收到 101 响应后无法维持长连接。
正确写法只有两种:
-
ProxyPass /ws/ ws://127.0.0.1:8080/ws/(明文 WebSocket) -
ProxyPass /ws/ wss://127.0.0.1:8080/ws/(加密 WebSocket,要求 Apache 自己配 SSL)
路径末尾的 / 必须前后一致,否则 Sec-WebSocket-Location 头被重写错误,现代浏览器直接拒绝连接。
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
Timeout 和 ProxyTimeout 都得调大,且必须同时设
WebSocket 是长连接,但 Apache 默认 Timeout 60 和 ProxyTimeout 60 会让空闲连接在 60 秒后被主动 kill,后端无感知,客户端只看到 “connection closed”。
生产环境建议:
- 在 WebSocket 专用
<virtualhost></virtualhost>内覆盖设置,避免影响其他 HTTP 服务 -
Timeout 3600(1 小时)—— 影响所有请求等待时间 -
ProxyTimeout 3600——mod_proxy_wstunnel实际依赖此值控制底层 socket 超时
别加 retry=0 或 keepalive=on 这类参数:mod_proxy_wstunnel 不支持大多数 ProxyPass 的 HTTP 参数,加了会导致配置校验失败。
监控只能靠日志 + 抓包,Apache 不提供 WebSocket 连接数实时指标
Apache 没有类似 mod_status 对 WebSocket 连接的原生统计。你无法用 server-status 查当前活跃 WS 连接数。
可行监控手段只有两个:
- 开启
LogLevel debug并过滤日志:关注含"101"、"Upgrade: websocket"、"Connection: upgrade"的行,确认握手是否进入隧道逻辑 - 用
tcpdump抓包验证:在 Apache 侧执行tcpdump -i lo port 8080 and tcp[tcpflags] & (tcp-syn|tcp-fin) != 0,看 FIN 包是否由 Apache 主动发出(说明保活失效)
真正容易被忽略的点是:WebSocket 连接状态对 Apache 来说是“黑盒”。它只管转发字节流,不解析帧、不维护会话上下文。所以连接异常时,90% 的排查要回到后端服务日志和网络链路本身,而不是 Apache 配置。










