根本原因是Apache未启用mod_proxy_wstunnel模块且ProxyPass未用ws://协议,导致Upgrade头缺失、后端误判为普通HTTP请求而返回403;须启用proxy_wstunnel、配置ws://代理路径、透传协议升级信号。
websocket 连接被 apache 代理拒绝(返回 403 或握手失败),根本原因不是“跨域”本身,而是 apache 在反向代理过程中未正确透传 websocket 协议升级信号,导致后端服务收不到 upgrade: websocket 请求,误判为普通 http 请求并拒绝——浏览器看到的就是 403 或 error during websocket handshake: unexpected response code: 403。
Apache 和 Nginx 不同,它默认完全不支持 WebSocket 代理,必须显式启用模块、配置协议升级头,并关闭可能干扰的中间处理(如重写、缓存、安全拦截)。
✅ Apache 必须启用的模块
确保以下模块已加载(在 httpd.conf 或 mods-enabled/ 中确认):
mod_proxymod_proxy_http-
mod_proxy_wstunnel← 关键!专用于 WebSocket 隧道代理,不可用mod_proxy_http替代 -
mod_rewrite(可选,用于路径重写) -
mod_headers(可选,用于清理 Origin)
检查方式(Linux):
httpd -M | grep -E "(proxy|wstunnel)" # 应看到:proxy_module, proxy_http_module, proxy_wstunnel_module
未启用需执行(以 Debian/Ubuntu 为例):
Apache Superset 是一个广泛采用的开源 BI 平台,用于 SQL 探索、图表构建和仪表板交付。当代理需要查询仓库数据、组装仪表板或使用成熟的分析界面解释指标而不是临时笔记本代码时,此技能非常有用。
a2enmod proxy proxy_http proxy_wstunnel headers systemctl restart apache2
✅ Apache 代理配置要点(location 级)
假设前端访问 wss://app.example.com/ws,后端 WebSocket 服务运行在 http://127.0.0.1:8080/ws,配置示例如下:
<VirtualHost *:443>
ServerName app.example.com
SSLEngine on
# ... SSL 证书配置省略
# 关键:匹配 WebSocket 路径,使用 proxy_wstunnel
<Location "/ws">
ProxyPass "ws://127.0.0.1:8080/ws"
ProxyPassReverse "ws://127.0.0.1:8080/ws"
# 强制启用 WebSocket 升级(等效于 Nginx 的 Upgrade/Connection 头)
ProxyPreserveHost On
ProxyRequests Off
# 清除可能引发校验失败的 Origin 头(可选,推荐后端控制)
RequestHeader unset Origin early
</Location>
# 若需代理 /ws/info(SockJS 探测端点),走普通 HTTP 代理
<Location "/ws/info">
ProxyPass "http://127.0.0.1:8080/ws/info"
ProxyPassReverse "http://127.0.0.1:8080/ws/info"
</Location>
</VirtualHost>⚠️ 注意事项:
-
ProxyPass必须用ws://(非http://)协议前缀,否则mod_proxy_wstunnel不生效; - 不要加
/尾缀(如ws://.../ws/),否则路径截断,后端收到的是/; -
RequestHeader unset Origin early可避免后端因 Origin 值不匹配(如http://localhost:3000→https://app.example.com)而 403;更规范的做法是后端用setAllowedOriginPatterns("https://app.example.com")显式放行; - 若后端 Spring Boot ≥ 2.4,务必在 Java 配置中调用
setAllowedOriginPatterns("https://app.example.com"),setAllowedOrigins("*")已废弃且报错。
✅ 常见故障对应修复
| 现象 | 原因 | 解法 |
|---|---|---|
403 Forbidden,Network 显示 GET 请求返回 403 |
Apache 未启用 mod_proxy_wstunnel,或 ProxyPass 写成 http://
|
检查模块 + 改为 ws:// 协议 |
Error during WebSocket handshake: Unexpected response code: 200 |
后端返回了 HTML/JSON 而非 101 Switching Protocols,说明请求没进 WebSocket handler |
确认 <Location "/ws"> 路径与后端注册的 endpoint 完全一致(大小写、斜杠) |
| 握手成功但立即断开(1006) | Apache 默认超时太短(如 Timeout 60),或连接被 mod_security 拦截 |
在 <Location> 中加 ProxySet keepalive=On timeout=3600;禁用或调整 mod_security 规则 |
SockJS 的 /ws/info 返回 404 或 403 |
该路径是 HTTP 请求,不能走 ws:// 代理 |
单独用 <Location "/ws/info"> + http:// 代理 |
✅ 补充:SSL 终止与 wss 支持
若 Apache 终止 HTTPS(即前端用 wss://,Apache 解密后以 ws:// 转发给后端):
- 不需要后端开启 TLS(即不用
wss://127.0.0.1:8080); - 但必须确保 Apache 的
SSLEngine on正常工作,且证书有效(自签名证书会导致浏览器直接拒连,不报 403,而是net::ERR_CERT_AUTHORITY_INVALID); - 浏览器只校验前端域名证书,不校验后端地址。
不复杂但容易忽略。

















