用于代理的 Apache RewriteRule

用于代理的 Apache RewriteRule

我安装了一个基于所用子域运行的 Web 应用程序 (ClockingIT)。由于我们想使用 SSL,但没有通配符证书,因此这对我们来说不太方便 :-) 所以我考虑使用 Apache 的 mod_proxy 和 mod_rewrite 功能。

更准确地说,我希望 URL Xttps://example.com/cit/(外部)显示 Xttp://test.example.com:3000/(内部)的内容。

这是我的设置:

   <VirtualHost *:443>
     ServerName example.com

     (SSL setup, etc)

     SSLProxyEngine On
     UseCanonicalName Off
     ProxyRequests Off   
     ProxyPreserveHost Off # or On, makes no difference

     RewriteEngine On
     RewriteRule      ^/cit$         Xttp://test.example.com:3000/ [P,NC]
     RewriteRule      ^/cit/(.*)$    Xttp://test.example.com:3000/$1 [P,NC]
     ProxyPassReverse /cit/          Xttp://test.example.com:3000/

test.example.com 未在 DNS 服务器上定义,但它在 /etc/hosts 中设置为映射到 127.0.0.1。如果我在服务器上执行“w3m Xttp://test.example.com:3000/”,我会得到正确的网页。但是,如果我访问https://example.com/cit/在我的桌面浏览器上,我没有得到正确的网页。Web 应用程序接收到请求,但它似乎认为请求是针对 example.com 域的,并提供默认页面而不是预期的子域“测试”内容。代理似乎不知何故没有传递 test.example.com 域,尽管根据文档应该传递。除了 RewriteRule,我还尝试了 ProxyPass 指令,但结果相同。

我是否遗漏了什么?

(如果相关的话,ClockingIT 是一个通过 Mongrel 提供的 Ruby on Rails 应用程序)

PS:s/Xttp/http/g - ServerFault 不喜欢我在问题中多次使用 http 冒号斜线斜线 ;-)

编辑:

使用 tcpflow 查看流量数据后,问题似乎是 Apache 向端口 3000 发送以下内容:

GET / HTTP/1.1
Host: test.example.com:3000
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-15,utf-8;q=0.7,*;q=0.7
Cookie: _session_id=99f5f70d684c2186e64c5ebb8f69d574
Via: 1.1 example.com
X-Forwarded-For: 1.2.3.4
X-Forwarded-Host: example.com
X-Forwarded-Server: example.com

使用“telnet localhost 3000”并粘贴上述内容,我得到了一个重定向。如果我重复此操作并省略 X-Forwarded-Host: 行,我就会得到预期的页面。所以我的设置实际上是有效的,但 ClockingIT 似乎根据 X-Forwarded-Host 值做出决定。有什么方法可以防止它被包含进来?

答案1

Apache 2.4 及更高版本有一个指令来删除 X-Forwarded-* 标头。

ProxyAddHeaders off

https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#proxyaddheaders

答案2

我被这个咬了。这真是一个令人讨厌的怪事。

Apache 的 mod_proxy 会将 x-forwarded-host 标头附加到所有出站请求。无法使用 HeaderRequest unset x-forwarded-host、ProxyVia 或 ProxyPreseveHost 禁用它。我找到的其他任何方法也无法禁用它。

当 Rails 看到该标头时,它会使用构造任何 HTTP 响应的 Location: 标头。作为参考,在带有 Webistrano 1.4 的 Rails vendor 版本中(该应用程序使用 mod_proxy 绊倒了我),相关代码似乎位于 vendor/rails/actionpack/lib/action_controller/cgi_process.rb 的第 88 行,位于函数 host_with_port_without_standard_port_handling 内。

现在看一下描述的 ProxyPass 和 ProxyPassReverse 的典型示例到处在网上 - 包括(基本上)您的问题和这里给出的替代答案:

<VirtualHost *:80>
ServerName proxy.domain.tld
ProxyPass /app1/ http://app1host.internal/
ProxyPassReverse /app1/ http://app1host.internal/
</VirtualHost>

看到问题了吗?是 PPR 管线。

因为 Rails/ActionPack/dasFramework 正在明智地尝试帮助您通过“更正” Location: 标题,PPR 行的后半部分不正确:而不是匹配

Location: http://app1host.internal/redirected/path

mod_proxy 将实际上

Location: http://proxy.domain.tld/redirected/path

幸运的是,修复方法非常简单 - 将上述 vhost 配置更改为:

<VirtualHost *:80>
ServerName proxy.domain.tld
ProxyPass /app1/ http://app1host.internal/
ProxyPassReverse /app1/ http://proxy.domain.tld/
</VirtualHost>

如果您在 vhost 中代理了多个应用程序,请注意,您至少需要将 PPR 放在位置部分内以区分它们。

答案3

您可能会发现使用 ProxyPass 而不是 mod_rewrite 来做您想做的事情更容易。它可能无法解决您的问题,但它肯定会让您的配置文件更简洁一些:

ProxyPass /cit/ http://test.example.com:3000/
ProxyPassReverse /cit/ http://test.example.com:3000/

您可能想尝试使用 tcpflow 或 wireshark 之类的工具来准确查看 apache 正在使用哪些标头来代理请求。

答案4

你确定这个ProxyPreserveHost指令没有影响吗?如果启用了它,那么初始请求的主机标头将保留在对后端服务器的请求中,这就是你所描述的。

看:

http://httpd.apache.org/docs/2.2/mod/mod_proxy.html#proxypreservehost

相关内容