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

22
我正在实现一个反向代理,用于路由请求到后端服务器。
功能上一切正常,但我担心所有来自后端服务器的响应都没有经过压缩就传输给了客户端(Web浏览器)。
设置如下:
- 后端服务器在内部域上,不对外开放。在https://internal.app上托管Web应用程序。 - 前端Web服务器使用IIS 7.5,托管主要的公共网站,并充当后端服务器的代理。主站点位于https://site.com
我想以对客户端透明的方式将所有请求路由到https://site.com/app/WHATEVERhttps://internal.app/WHATEVER
我的当前设置基于URL Rewrite 2.0和Application Request Routing IIS扩展。一般的方法基于以下文章的指南:

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头,则相同的错误再次出现。

据我所知,如果重写涉及到另一个Web应用程序,则可以执行的操作会更受限制。例如,URL重写器必须从后端服务器接收未压缩的响应才能使用出站规则进行重写。我猜在这种情况下清除HTTP_ACCEPT_ENCODING是必须的。

然而,我期望最终重写的响应应该被压缩,无论其来源于何处,因为压缩模块位于模块列表的顶部。看起来IIS做了一些捷径,直接将响应返回给客户端,绕过压缩模块。或者HTTP_ACCEPT_ENCODING头被很快删除,以完全禁用压缩(不仅在服务器与服务器之间的通信中)。

因此,我的问题是:有没有一种方式可以压缩这些响应?

4个回答

31

我已经自己想出了解决方案。

需要完成以下步骤才能使其正常工作:

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

目前为止表现得非常好。


很高兴你觉得这个有用,并感谢你在这里留下链接! - Jakub Januszkiewicz
1
嗯,我感觉你的公式有一部分缺失。我已经把所有东西都准备好了,但是我仍然没有看到任何代理内容的压缩。你的IIS模块的顺序是什么?HttpCacheModuleRewriteModuleDynamicCompressionModuleStaticCompressionModule的关系是否重要? - Paul d'Aoust
@Pauld'Aoust 很久没参与我用过这个项目了,所以不能确定,但是我链接的线程表明模块的顺序确实很重要。 - Jakub Januszkiewicz
@JakubJanuszkiewicz 感谢您抽出时间回复!最终我选择了另一种方法(查看我们应用程序的代码后发现我不需要重写响应正文,因此我只是将 HTTP_ACCEPT_ENCODING 标头保留为原样)。 - Paul d'Aoust
5
尽管此解决方案解决了OP描述的500.53错误,但实际上它将从响应中移除gzip压缩。这可能是可以接受的,但应该注意,如果希望得到gzip压缩结果,则此解决方案无法提供。 - Scot
显示剩余5条评论

3

为了解决原贴作者的问题,同时又保留gzip压缩响应,只需要按照以下步骤进行操作:

  1. 更新公共Web服务器上的注册表,操作如下:

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

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

  2. 重置IIS

  3. 禁用静态压缩

    a. 为了达到这个目的,在我的Web.config文件中插入以下内容:<urlCompression doStaticCompression="false" doDynamicCompression="true" dynamicCompressionBeforeCache="false" />

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

请注意

我看到网络上有很多关于此问题的解决方案,包括此页面上的答案,其中HTTP_CONTENT_TYPE请求头部被清除为URL重写规则的一部分。应该注意的是,虽然这确实解决了500.52错误的原始问题,但响应中的gzip压缩被删除。这可能是期望的结果,但如果需要gzip压缩,则可以使用上述解决方案。


你能解释一下为什么这个代码可以运行,以及在未来的URL重写版本中是否会出现问题吗?你是不是利用了“LogRewrittenUrlEnabled”注册值的副作用来实现重写压缩响应? - Jakub Januszkiewicz

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>

1

注意:以下解决方案仅适用于您控制应用程序服务器的情况。

基本上是让Web服务器进行压缩,让应用程序服务器执行应用程序应该执行的重活(不进行压缩)。

如果在应用程序服务器上禁用压缩,则从应用程序服务器收到的响应未经压缩。在Web服务器上,您应启用压缩,以便Web服务器在响应客户端(浏览器)时遵守HTTP标头“Accept-Encoding:gzip,deflate”。

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


这对我有用,谢谢。以下是更改IIS上这些设置的文档页面:https://www.iis.net/configreference/system.webserver/httpcompression - Hamman Samuel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接