我正在实现一个反向代理,用于将请求路由到后端服务器。
功能上一切正常,但我担心来自后端服务器的所有响应都会在没有压缩的情况下传输到客户端(web浏览器(。
设置如下:
- 内部域上的后端服务器,公共无法访问。在
https://internal.app
上托管web应用程序 - 带有IIS 7.5的前端web服务器,托管主公共网站并充当后端服务器的代理。主站点位于
https://site.com
我希望以对客户端透明的方式将所有请求路由到https://site.com/app/WHATEVER
到https://internal.app/WHATEVER
。
我当前的设置基于URL重写2.0和应用程序请求路由IIS扩展。一般方法基于以下文章中的指导方针:
- 使用IIS、URL重写和ARR设置反向代理
- 具有URL重写v2和应用程序请求路由的反向代理
site.com
应用程序的web.config
的相关部分:
<system.webServer>
<rewrite>
<rules>
<rule name="Route the requests for backend app" stopProcessing="true">
<match url="^app/(.*)" />
<conditions>
<add input="{CACHE_URL}" pattern="^(https?)://" />
</conditions>
<action type="Rewrite" url="{C:1}://internal.app/{R:1}" />
<serverVariables>
<set name="HTTP_ACCEPT_ENCODING" value="" />
</serverVariables>
</rule>
</rules>
<outboundRules>
<rule name="RewriteBackendAbsoluteUrlsInResponse" preCondition="ResponseIsHtml1">
<match filterByTags="A, Area, Base, Form, Frame, Head, IFrame, Img, Input, Link, Script" pattern="^http(s)?://internal.app(:80)?/(.*)" />
<action type="Rewrite" value="/app/{R:3}" />
</rule>
<rule name="RewriteBackendAbsoluteUrlsInRedirects" preCondition="ResponseIsHtml1">
<match serverVariable="RESPONSE_LOCATION" pattern="^http(s)?://internal.app(:80)?/(.*)" />
<action type="Rewrite" value="/app/{R:3}" />
</rule>
<rule name="RewriteBackendRelativeUrlsInResponse" preCondition="ResponseIsHtml1">
<match filterByTags="A, Area, Base, Form, Frame, Head, IFrame, Img, Input, Link, Script" pattern="^/(.*)" negate="false" />
<conditions>
<add input="{URL}" pattern="^/app/.*" />
</conditions>
<action type="Rewrite" value="/app/{R:1}" />
</rule>
<rule name="RewriteBackendRelativeUrlsInRedirects" preCondition="ResponseIsHtml1">
<match serverVariable="RESPONSE_LOCATION" pattern="^/(.*)" negate="false" />
<conditions>
<add input="{URL}" pattern="^/app/.*" />
</conditions>
<action type="Rewrite" value="/app/{R:1}" />
</rule>
<preConditions>
<preCondition name="ResponseIsHtml1">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
</preConditions>
</outboundRules>
</rewrite>
<urlCompression dynamicCompressionBeforeCache="false" />
</system.webServer>
问题是,一旦我停止清除HTTP_ACCEPT_ENCODING
服务器变量,每个符合上述规则的请求都会以以下错误结束:HTTP Error 500.52 - URL Rewrite Module Error.
Outbound rewrite rules cannot be applied when the content of the HTTP response is encoded ("gzip").
我知道这个线索,我已经按照这些指示去做了。如上所示,我已经设置了dynamicCompressionBeforeCache="false"
,添加了必要的注册表项,并确保模块在IIS中的顺序正确。
然而,这似乎只有在重写发生在一个web应用程序中时才有效。如果我删除上面的规则,并添加一个简单的规则(以及相应的出站规则(来重写,例如/x/WHATEVER
到仅/WHATEVER
,则所有规则都可以完美地工作,而无需清除HTTP_ACCEPT_ENCODING
-该规则可以工作,并为重写的请求启用压缩。
但是,一旦我重新添加将响应重写到不同web应用程序的规则,并且我没有清除HTTP_ACCEPT_ENCODING
标头,同样的错误就会再次出现。
据我所知,如果重写涉及另一个网络应用程序,那么可以做的事情会受到更多限制。例如,URL重写器必须从后端服务器接收未压缩的响应,才能使用出站规则对其进行重写。我想在这种情况下清除HTTP_ACCEPT_ENCODING
是必须的,因为这一点。
然而,我希望,由于压缩模块列在模块列表的顶部,因此无论最终重写的响应来自哪里,都应该进行压缩。IIS似乎做了一些快捷方式,并绕过压缩模块将响应返回给客户端。或者HTTP_ACCEPT_ENCODING
标头很快被删除,从而完全禁用压缩(不仅在服务器到服务器的通信中(。
最后,我的问题是:有没有办法压缩这些回应?
我自己已经想好了。
需要做些什么才能让它发挥作用:
- 在将请求路由到后端服务器之前,必须删除
Accept-Encoding
标头,以便可以使用出站规则重写响应 - 必须通过附加的出站规则来恢复标头,以便在将响应发送到客户端之前,当压缩模块启动时,标头就会出现
我决定这样做:
-
在重写规则中添加一个新的服务器变量,以保存客户端发送的原始标头:
<set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCODING}" />
(我把它放在清除
HTTP_ACCEPT_ENCODING
变量的行之前( -
添加一个新的出站规则:
<rule name="RestoreAcceptEncoding" preCondition="NeedsRestoringAcceptEncoding"> <match serverVariable="HTTP_ACCEPT_ENCODING" pattern="^(.*)" /> <action type="Rewrite" value="{HTTP_X_ORIGINAL_ACCEPT_ENCODING}" /> </rule>
以及一个附带的先决条件:
<preCondition name="NeedsRestoringAcceptEncoding"> <add input="{HTTP_X_ORIGINAL_ACCEPT_ENCODING}" pattern=".+" /> </preCondition>
到目前为止效果不错。
要解决原始海报的问题,同时仍保留gzip压缩响应,只需执行以下操作:
-
更新面向公众的网络服务器上的注册表,如下所示:
a。对于64位网站,请在具有管理员权限的命令控制台中运行以下操作:
reg add HKEY_LOCAL_MACHINESOFTWAREMicrosoftInetStpRewrite /v LogRewrittenUrlEnabled /t REG_DWORD /d 0
b。对于32位网站,请在具有管理员权限的命令控制台中运行以下操作:
reg add HKEY_LOCAL_MACHINESOFTWAREWow6432nodeMicrosoftInetstpRewrite /v LogRewrittenUrlEnabled /t REG_DWORD /d 0
-
重置IIS
-
禁用静态压缩
a。为了实现这一点,我插入了以下web配置:
<urlCompression doStaticCompression="false" doDynamicCompression="true" dynamicCompressionBeforeCache="false" />
-
在IIS管理器的服务器节点中,双击
Modules
图标,然后在右侧,单击"查看有序列表",并验证静态/动态压缩模块是否位于顶部,URL重写模块是否位于底部
请注意
我看到很多关于这个问题的解决方案在网上流传,HTTP_CONTENT_TYPE请求头作为URL重写规则的一部分被清除(包括本页上的答案(。需要注意的是,虽然这确实解决了500.52错误的原始问题,但响应上的gzip压缩被删除了。这可能是想要的结果,但如果需要gzip压缩,上面的解决方案将完成
PS:下面的解决方案只有在您可以控制应用服务器的情况下才有效。
它基本上是让web服务器进行压缩,让应用程序服务器完成应用程序应该做的繁重工作(无压缩(。
如果在应用服务器上禁用压缩,则从应用服务器获得的响应将被解压缩。在web服务器上,您应该启用压缩,这样web服务器在响应客户端(浏览器(时将遵守HTTP标头"Accept-Encoding:gzip,deflate"。
此配置将卸载应用程序服务器上的CPU,但会增加web服务器和应用程序服务器之间的网络流量。如果你在内部网络上,它不会对性能产生太大影响。
只需在重写规则中添加一个"serverVariables"标记就可以了:
<rewrite>
<rules>
<rule name="ReverseProxyInboundRule1" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="http://otherurl{R:1}" />
<serverVariables>
<set name="HTTP_ACCEPT_ENCODING" value="" />
</serverVariables>
</rule>
</rules>
</rewrite>