Apache Rewrite规则异常跳转问题:非目标路径被错误转发
需求
将Apache服务器中https://example.edu/rdemos/(.*)请求转发至运行在3838端口的Shiny服务器,其余页面走常规代理。
当前配置
RewriteEngine on RewriteRule ^/PP/(.*) https://example.com/PhysicsPlayground/$1 <Location /rdemos> RedirectMatch permanent ^/rdemos$ /rdemos/ RewriteEngine on RewriteCond %{HTTP:Upgrade} =websocket RewriteRule /rdemos/(.*) ws://localhost:3838/$1 [P,L] RewriteCond %{HTTP:Upgrade} !=websocket RewriteRule /rdemos/(.*) http://localhost:3838/$1 [P,L] ProxyPass http://localhost:3838/ ProxyPassReverse http://localhost:3838/ # Header edit Location ^/ /rdemos/ </Location>
出现的问题
/rdemos路径下的请求可正常转发,但非目标路径(如https://example.com/PP/demo.html)被错误转发至https://example.com/rdemos/PhysicsPlayground/demo.html(该路径不存在)。
环境
Apache/2.4.37 (Red Hat Enterprise Linux)、OpenSSL/1.1.1k等组件。
错误原因及修正方案
1. ProxyPass配置格式错误
<Location /rdemos>块内的ProxyPass http://localhost:3838/写法不符合Apache规范,会导致ProxyPass规则被全局应用(而非仅作用于/rdemos路径)。所有未匹配到其他规则的请求(包括/PP重写后的请求)都会被转发到Shiny服务器,路径被错误拼接为/rdemos/PhysicsPlayground/...。
修正:将ProxyPass改为ProxyPass "" http://localhost:3838/,同时修正ProxyPassReverse:
ProxyPass "" http://localhost:3838/ ProxyPassReverse "" http://localhost:3838/
该写法明确将<Location /rdemos>匹配的路径映射到Shiny服务器的根路径,避免规则溢出。
2. 全局RewriteRule缺少必要标志
全局的RewriteRule ^/PP/(.*) https://example.com/PhysicsPlayground/$1未添加[L]和[R=301]标志:
- 无
[L]会导致重写后的请求继续被后续规则处理,加重ProxyPass的错误影响; - 无
[R=301]会使Apache尝试内部代理跨域请求(而非返回重定向给客户端),引发内部路径解析混乱。
修正:添加[R=301,L]标志:
RewriteRule ^/PP/(.*) https://example.com/PhysicsPlayground/$1 [R=301,L]
3. 内RewriteRule匹配路径冗余
<Location /rdemos>已限定请求路径前缀为/rdemos,内部的RewriteRule无需再匹配完整路径/rdemos/(.*),冗余匹配可能导致规则解析异常。
修正:改为匹配/rdemos之后的相对路径:
RewriteCond %{HTTP:Upgrade} =websocket RewriteRule ^/(.*) ws://localhost:3838/$1 [P,L] RewriteCond %{HTTP:Upgrade} !=websocket RewriteRule ^/(.*) http://localhost:3838/$1 [P,L]
修正后的完整配置
RewriteEngine on RewriteRule ^/PP/(.*) https://example.com/PhysicsPlayground/$1 [R=301,L] <Location /rdemos> RedirectMatch permanent ^/rdemos$ /rdemos/ RewriteEngine on RewriteCond %{HTTP:Upgrade} =websocket RewriteRule ^/(.*) ws://localhost:3838/$1 [P,L] RewriteCond %{HTTP:Upgrade} !=websocket RewriteRule ^/(.*) http://localhost:3838/$1 [P,L] ProxyPass "" http://localhost:3838/ ProxyPassReverse "" http://localhost:3838/ # 若Shiny服务器返回的Location头不带/rdemos前缀,取消注释该行 # Header edit Location ^/ /rdemos/ </Location>
内容的提问来源于stack exchange,提问作者ralmond

