用于代理的 Apache RewriteRule

5 rewrite proxy apache-2.2

我安装了一个基于使用的子域的 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/
Run Code Online (Sandbox Code Playgroud)

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 域,尽管根据文档它应该。我还尝试了 ProxyPass 指令,而不是 RewriteRule,但结果相同。

有什么我想念的吗?

(如果相关,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
Run Code Online (Sandbox Code Playgroud)

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

小智 6

Apache 2.4 及更高版本具有删除 X-Forwarded-* 标头的指令。

ProxyAddHeaders off

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


小智 5

我被这个咬了。这是一个非常烦人的奇怪的。

Apache 的 mod_proxy 将标题 x-forwarded-host 附加到所有出站请求。它不能被 HeaderRequest unset x-forwarded-host、ProxyVia 和 ProxyPreseveHost 禁用。也没有我能找到的任何其他东西。

当 Rails 看到该标头时,它会使用来构造任何 HTTP 响应的 Location: 标头。作为参考,在 Rails vendor'd with Webistrano 1.4(用 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>
Run Code Online (Sandbox Code Playgroud)

看到问题了吗?这是PPR线..

因为 Rails/ActionPack/dasFramework 在它的智慧中试图通过“更正” Location: 标题来帮助您,所以 PPR 行的后半部分不正确:而不是匹配

Location: http://app1host.internal/redirected/path
Run Code Online (Sandbox Code Playgroud)

mod_proxy实际上会看到

Location: http://proxy.domain.tld/redirected/path
Run Code Online (Sandbox Code Playgroud)

幸运的是,修复很容易 - 将上面的 vhost 配置更改为:

<VirtualHost *:80>
ServerName proxy.domain.tld
ProxyPass /app1/ http://app1host.internal/
ProxyPassReverse /app1/ http://proxy.domain.tld/
</VirtualHost>
Run Code Online (Sandbox Code Playgroud)

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


小智 0

您确定该ProxyPreserveHost指令没有影响吗?如果打开,初始请求中的主机标头将保留在向后端服务器发出的请求中,这就是您所描述的。

看:

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