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.
The setup is as follows:
- Backend server, not accessible for public, on an internal domain. Hosts a web application on
https://internal.app
- Front web server with IIS 7.5, hosting the main public website and acting as a proxy for the backend server. The main site is at
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.
My current setup is based on URL Rewrite 2.0 and Application Request Routing IIS extensions. The general approach is based on guidelines from the following articles:
- Setting up a Reverse Proxy using IIS, URL Rewrite and ARR
- Reverse Proxy with URL Rewrite v2 and Application Request Routing
The relevant section of web.config
of the site.com
app:
<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").
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.
However, this only seems to work only if the rewriting happens within one web app. If I remove the above rules and add a simple one (and respective outbound rules) to rewrite e.g. /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.
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.
From what I understand, if the rewriting involves another web app, there are more constraint on what can be done. E.g. URL rewriter must receive an uncompressed response from the backend server in order to be able to rewrite it using the outbound rules. I guess clearing HTTP_ACCEPT_ENCODING
in this scenario is a must because of this.
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. Or the HTTP_ACCEPT_ENCODING
header is removed soon enough to completely disable compression (not only in server-to-server communication).
So finally, my question is: is there a way to compress those responses?