IIS作为反向代理 - 压缩来自后端服务器的重写响应

时间:2013-04-10 12:44:23

标签: asp.net iis reverse-proxy http-compression arr

我正在实现一个反向代理,用于将请求路由到后端服务器。

功能上一切正常,但是我担心来自后端服务器的所有响应都会在没有压缩的情况下传输到客户端(Web浏览器)。

设置如下:

  • 内部域上无法公开访问的后端服务器。在https://internal.app
  • 上托管Web应用程序
  • 使用IIS 7.5的前端Web服务器,托管主公共网站并充当后端服务器的代理。主站点位于https://site.com

我希望以对客户端透明的方式将所有请求路由到https://site.com/app/WHATEVERhttps://internal.app/WHATEVER

我当前的设置基于URL Rewrite 2.0和Application Request Routing IIS扩展。一般方法基于以下文章的指导原则:

web.config应用的site.com相关部分:

<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").

我知道this thread并且我遵循了这些指示。我已经设置了dynamicCompressionBeforeCache="false",如上所示,我已经添加了必要的注册表项,并确保模块在IIS中的顺序正确。

然而,这似乎仅在重写发生在一个Web应用程序中时起作用。如果我删除上述规则并添加一个简单的规则(和相应的出站规则)来重写例如/x/WHATEVER/WHATEVER,所有工作完美无需清除HTTP_ACCEPT_ENCODING - 规则正常工作,并且已为重写的请求启用压缩。

但是,只要我重新添加我的规则,将规则重写为其他网络应用,并且我不清除HTTP_ACCEPT_ENCODING标题,就会再次显示相同的错误。

根据我的理解,如果重写涉及另一个网络应用程序,那么可以做什么的约束更多。例如。 URL重写器必须从后端服务器接收未压缩的响应,以便能够使用出站规则重写它。我想在这种情况下清除HTTP_ACCEPT_ENCODING是必须的,因为这个。

但是,我希望由于压缩模块列在模块列表的顶部,因此最终重写的响应应该被压缩,无论它来自何处。似乎IIS制作了一些快捷方式,并绕过压缩模块将响应返回给客户端。或者很快就会删除HTTP_ACCEPT_ENCODING标头以完全禁用压缩(不仅仅是在服务器到服务器的通信中)。

最后,我的问题是:有没有办法压缩这些回复?

4 个答案:

答案 0 :(得分:25)

我自己已经弄清楚了。

要使其发挥作用需要做些什么:

    在将请求路由到后端服务器之前必须删除
  • 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>
    

到目前为止,它就像一个魅力。

答案 1 :(得分:2)

要解决原始海报的问题,而仍保留gzip压缩回复,只需执行以下操作:

  1. 更新面向公众的Web服务器上的注册表:

    一个。对于64位网站,请在具有管理员权限的命令控制台中运行以下命令:reg add HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\InetStp\Rewrite /v LogRewrittenUrlEnabled /t REG_DWORD /d 0

    湾对于32位网站,请在具有管理员权限的命令控制台中运行以下命令:reg add HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432node\Microsoft\Inetstp\Rewrite /v LogRewrittenUrlEnabled /t REG_DWORD /d 0

  2. 重置IIS

  3. 禁用静态压缩

    一个。为了实现这一点,我插入了以下我的网络配置:<urlCompression doStaticCompression="false" doDynamicCompression="true" dynamicCompressionBeforeCache="false" />

  4. 在IIS管理器的服务器节点中,双击Modules图标,然后在右侧单击“查看有序列表”并验证您的静态/动态压缩模块是否位于顶部, URL重写模块位于底部

  5. 请注意

    我看到很多关于这个问题的解决方案浮动在网络上,其中HTTP_CONTENT_TYPE请求标头被清除作为URL重写规则的一部分(包括此页面上的答案)。应该知道虽然这确实解决了500.52错误的原始问题,但响应上的gzip压缩是 REMOVED 。这可能是期望的结果,但是如果需要gzip压缩,上面的解决方案就可以解决这个问题

答案 2 :(得分:1)

PS:以下解决方案仅在您控制您的应用服务器时才有效。

它基本上是让Web服务器进行压缩,让app服务器完成应用程序应该做的重任(不压缩)。

如果在应用服务器上禁用压缩,则从应用服务器获取的响应将被解压缩。在Web服务器上,您应该启用压缩,因此Web服务器将在响应客户端(浏览器)时遵循HTTP标头“Accept-Encoding:gzip,deflate”。

此配置将卸载应用服务器上的CPU,但会增加Web服务器和应用服务器之间的网络流量。如果您在内部网络上,则不会对性能产生太大影响。

答案 3 :(得分:1)

只需在重写规则中添加“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>