简体   繁体   English

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

[英]IIS as a reverse proxy - compression of rewritten response from backend server

I am implementing a reverse proxy for routing requests to a backend server. 我正在实现一个反向代理,用于将请求路由到后端服务器。

Functionally everything works correctly, however I am concerned that all responses from the backend server are transferred to the client (web browser) without compression. 功能上一切正常,但是我担心来自后端服务器的所有响应都会在没有压缩的情况下传输到客户端(Web浏览器)。

The setup is as follows: 设置如下:

  • Backend server, not accessible for public, on an internal domain. 内部域上无法公开访问的后端服务器。 Hosts a web application on https://internal.app https://internal.apphttps://internal.app Web应用程序
  • Front web server with IIS 7.5, hosting the main public website and acting as a proxy for the backend server. 带有IIS 7.5的前端Web服务器,托管主公共网站并充当后端服务器的代理。 The main site is at https://site.com . 主站点位于https://site.com

I want to route all requests to https://site.com/app/WHATEVER to https://internal.app/WHATEVER in a way that is transparent to clients. 我想以对客户端透明的方式将所有请求路由到https://site.com/app/WHATEVERhttps://internal.app/WHATEVER

My current setup is based on URL Rewrite 2.0 and Application Request Routing IIS extensions. 我当前的设置基于URL Rewrite 2.0和Application Request Routing IIS扩展。 The general approach is based on guidelines from the following articles: 一般方法基于以下文章的指导原则:

The relevant section of web.config of the site.com app: 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>

The problem is that as soon as I stop clearing the HTTP_ACCEPT_ENCODING server variable, each request which matches the above rule ends with the following error: HTTP Error 500.52 - URL Rewrite Module Error. Outbound rewrite rules cannot be applied when the content of the HTTP response is encoded ("gzip"). 问题是,一旦我停止清除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"). HTTP Error 500.52 - URL Rewrite Module Error. Outbound rewrite rules cannot be applied when the content of the HTTP response is encoded ("gzip").

I am aware of this thread and I have followed those instructions. 我知道这个帖子 ,我遵循了这些指示。 I have set dynamicCompressionBeforeCache="false" as can be seen above, I have added the necessary registry entry and I have assured that the modules are in correct order in IIS. 我已经设置了dynamicCompressionBeforeCache="false" ,如上所示,我添加了必要的注册表项,我确信模块在IIS中的顺序正确。

However, this only seems to work only if the rewriting happens within one web app. 但是,这似乎仅在重写发生在一个Web应用程序中时才起作用。 If I remove the above rules and add a simple one (and respective outbound rules) to rewrite eg /x/WHATEVER to just /WHATEVER , all works perfectly without a need to clear HTTP_ACCEPT_ENCODING - the rule works and compression is enabled for the rewritten requests. 如果我删除上述规则并添加一个简单的规则(和各自的出站规则)来重写例如/x/WHATEVER/WHATEVER ,所有工作都完美无需清除HTTP_ACCEPT_ENCODING - 规则有效并且对重写的请求启用了压缩。

But as soon as I re-add my rule which rewrites the response to a different web app, and I don't clear the HTTP_ACCEPT_ENCODING header, the same error appears again. 但是,一旦我重新添加我的规则,该规则将响应重写为其他Web应用程序,并且我不清除HTTP_ACCEPT_ENCODING标头,则会再次出现相同的错误。

From what I understand, if the rewriting involves another web app, there are more constraint on what can be done. 根据我的理解,如果重写涉及另一个Web应用程序,那么可以做什么的约束更多。 Eg URL rewriter must receive an uncompressed response from the backend server in order to be able to rewrite it using the outbound rules. 例如,URL重写器必须从后端服务器接收未压缩的响应,以便能够使用出站规则重写它。 I guess clearing HTTP_ACCEPT_ENCODING in this scenario is a must because of this. 我想在这种情况下清除HTTP_ACCEPT_ENCODING是必须的。

However, I would expect that since the compression module is listed on top of the modules list, the final rewritten response should be compressed no matter where it originated from. 但是,我希望由于压缩模块列在模块列表的顶部,因此最终重写的响应应该被压缩,无论它来自何处。 It seems IIS makes some shortcuts and returns the response to the client bypassing the compression module. 似乎IIS制作了一些快捷方式,并绕过压缩模块将响应返回给客户端。 Or the HTTP_ACCEPT_ENCODING header is removed soon enough to completely disable compression (not only in server-to-server communication). 或者很快就会删除HTTP_ACCEPT_ENCODING标头以完全禁用压缩(不仅仅是在服务器到服务器的通信中)。

So finally, my question is: is there a way to compress those responses? 最后,我的问题是:有没有办法压缩这些回复?

I have figured it out myself. 我自己弄清楚了。

What needs to be done to get it working: 要使其工作,需要做些什么:

  • Accept-Encoding header must be removed before routing the request to the backend server, so that the response can be rewritten using outbound rules 在将请求路由到后端服务器之前,必须先删除Accept-Encoding标头,以便可以使用出站规则重写响应
  • the header must be restored by an additional accompanying outbound rule, so that it is present when the compression module kicks in before the response is sent to the client 必须通过附加的附带出站规则来恢复标头,以便在将响应发送到客户端之前压缩模块启动时存在

I have decided to do it like this: 我决定这样做:

  • add a new server variable to the rewrite rule to hold he original header sent by the client: 将新的服务器变量添加到重写规则以保存客户端发送的原始标头:

     <set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCODING}" /> 

    (I put it before the line which clears the HTTP_ACCEPT_ENCODING variable) (我把它放在清除HTTP_ACCEPT_ENCODING变量的行之前)

  • add a new outbound rule: 添加新的出站规则:

     <rule name="RestoreAcceptEncoding" preCondition="NeedsRestoringAcceptEncoding"> <match serverVariable="HTTP_ACCEPT_ENCODING" pattern="^(.*)" /> <action type="Rewrite" value="{HTTP_X_ORIGINAL_ACCEPT_ENCODING}" /> </rule> 

    and an accompanying precondition: 以及一个附带的前提条件:

     <preCondition name="NeedsRestoringAcceptEncoding"> <add input="{HTTP_X_ORIGINAL_ACCEPT_ENCODING}" pattern=".+" /> </preCondition> 

Works like a charm so far. 到目前为止,它就像一个魅力。

To address the original poster's issue while still preserving gzip compressed responses , one simply needs to do the following: 为了解决原始海报的问题,同时仍然保留gzip压缩响应 ,只需要执行以下操作:

  1. update the registry on your public facing web server as such: 更新面向公众的Web服务器上的注册表:

    a. 一个。 For 64bit web sites, run the following in a command console with admin rights: reg add HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\InetStp\\Rewrite /v LogRewrittenUrlEnabled /t REG_DWORD /d 0 对于64位网站,请在具有管理员权限的命令控制台中运行以下命令: reg add HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\InetStp\\Rewrite /v LogRewrittenUrlEnabled /t REG_DWORD /d 0

    b. For 32bit web sites, run the following in a command console with admin rights: reg add HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432node\\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. reset IIS 重置IIS

  3. disable static compression 禁用静态压缩

    a. 一个。 to accomplish this, I inserted the following my web config: <urlCompression doStaticCompression="false" doDynamicCompression="true" dynamicCompressionBeforeCache="false" /> 为了实现这一点,我插入了以下我的web配置: <urlCompression doStaticCompression="false" doDynamicCompression="true" dynamicCompressionBeforeCache="false" />

  4. in the server node in IIS manager, double click on the Modules icon, then on the right, click "view ordered List" and verify that your static/dynamic compression modules are towards the top and the URL rewrite module is toward the bottom 在IIS管理器的服务器节点中,双击“ Modules图标,然后在右侧单击“查看有序列表”并验证您的静态/动态压缩模块是否位于顶部,URL重写模块位于底部

Please Note 请注意

I see a lot of resolutions to this issue floating around the net where the HTTP_CONTENT_TYPE request header is being cleared out as part of the URL rewrite rule (including answers on this page). 我看到很多关于此问题的解决方案浮动在网络上,其中HTTP_CONTENT_TYPE请求标头被清除作为URL重写规则的一部分(包括此页面上的答案)。 One should be aware that while this does resolve the original issue of the 500.52 error, gzip compression on the response is REMOVED . 每个人都应该知道,虽然这确实解决了500.52错误的原始问题,对响应gzip压缩将被删除 This may be the desired outcome, but if gzip compression required, the above solution will do the trick 这可能是期望的结果,但如果需要gzip压缩,上述解决方案就可以解决问题

PS: The below solution only works if you have control over your app server. PS:以下解决方案仅在您控制应用服务器时才有效。

It's basically letting the web server do the compression, and let the app server do the heavy duty of what the app is supposed to do (without compression). 它基本上是让Web服务器进行压缩,让app服务器完成应用程序应该做的重任(无需压缩)。

If you disable the compression on the app server, the response you get from app server is uncompressed. 如果您在应用服务器上禁用压缩,则从应用服务器获得的响应将被解压缩。 On the web server, you should enable the compression, so web server will honor the HTTP header "Accept-Encoding: gzip,deflate" while responding to client (browser). 在Web服务器上,您应该启用压缩,因此Web服务器将在响应客户端(浏览器)时遵循HTTP标头“Accept-Encoding:gzip,deflate”。

This configuration will offload the CPU on the app server but will increase the network traffic between your web server and app server. 此配置将卸载应用服务器上的CPU,但会增加Web服务器和应用服务器之间的网络流量。 If you are on the internal network, it doesn't have much performance impact. 如果您在内部网络上,则不会对性能产生太大影响。

Just adding a "serverVariables" tag in the rewrite rule did the trick for me: 只需在重写规则中添加“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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM