Apache中WebSocket的内容安全策略(CSP)配置问题排查
问题场景
部署WebSocket应用后通过Apache反向代理对外提供服务,Apache配置如下:
<VirtualHost *:80> ServerName 192.168.2.121 # Proxy for WebSocket ProxyPass /ssh/websocket ws://192.168.2.121:8086/ssh/websocket ProxyPassReverse /ssh/websocket ws://192.168.2.121:8086/ssh/websocket # Content Security Policy Header always set Content-Security-Policy "default-src 'self'; connect-src 'self' ws://192.168.2.121:8086; script-src 'self';" # ErrorLog and CustomLog ErrorLog /var/opt/oe/base/log/gui/websocketapp_error.log CustomLog /var/opt/oe/base/log/gui/websocketapp_access.log combined <Directory /opt/oe/toposrv/sbin/ > Require all granted </Directory> <Directory /var/opt/oe/base/log/gui > Require all granted </Directory> </VirtualHost>
发起WebSocket连接时出现以下错误:
Refused to connect to 'ws://192.168.2.121:8086/ssh/websocket?username=&password=&node_name=edge1-site1' because it violates the following Content Security Policy directive: "default-src 'self'". Note that 'connect-src' was not explicitly set, so 'default-src' is used as a fallback.
问题分析与解决方案
Apache配置存在两处关键问题:
1. CSP配置的引号解析错误
配置中使用了HTML转义的"作为CSP规则的包裹引号,这会导致Apache无法正确解析完整的CSP规则,浏览器实际接收到的内容缺失了connect-src配置,因此只能使用default-src作为 fallback,拦截了非self的连接请求。
修正方法:将"替换为实际的双引号,确保Apache能正确解析CSP规则:
Header always set Content-Security-Policy "default-src 'self'; connect-src 'self' ws://192.168.2.121:8086; script-src 'self';"
2. 前端WebSocket连接地址不符合反向代理逻辑
当前前端直接请求后端的8086端口,绕开了Apache反向代理,既违背了代理的设计初衷,也额外增加了CSP规则的复杂度。
修正方法:将前端的WebSocket连接地址改为代理端口(默认80)的路径:
// 原错误地址 // const ws = new WebSocket('ws://192.168.2.121:8086/ssh/websocket?xxx'); // 修正后的地址 const ws = new WebSocket('ws://192.168.2.121/ssh/websocket?xxx');
此时CSP规则可以进一步简化,只保留'self'即可满足需求:
Header always set Content-Security-Policy "default-src 'self'; script-src 'self';"
总结
先修正Apache的CSP配置引号问题,再调整前端连接地址走代理端口,即可解决该WebSocket连接被CSP拦截的问题。
内容的提问来源于stack exchange,提问作者AR7

