IIS Reverse Proxy. It Even Works. Can’t Modify HTTP Response Headers

Description of a Problem

You have a custom WEB application that runs inside your enterprise on some internal server and on some custom port.  How to make this application accessible from outside through a published HTTP link on port 80?

Proposed Solution

On your production IIS server you create a directory that is accessible from outside on port 80.  Then you configure a Reverse Proxy solution described here.  This way neither you, nor your clients have to open custom ports on their firewalls.

How Does It Work

IIS server receives HTTP request on port 80 through corporate firewalls. It then translates request according to routing rules to a destination on your corporate network on some port (Ex: 8080).  Next, IIS server receives response from your corporate server and forwards it back to a requester.  Client doesn’t have to open port 8080 for external communications, nor you need to reconfigure your custom web application.

Test Platform

This particular example was tested on a simple HTML site running on Windows 7 Ultimate x64 and IIS 7.5.  Real-life test was performed on Windows 2008 Server and IIS 7.  Custom ASP.NET site inside enterprise was running on port 9090 on a Windows 7 machine.

Prerequisites

A basic knowledge and familiarity with IIS features and interface is required to understand this article.
Windows 7 with IIS 7.5 or Windows 2008 Server with IIS 7.
Ability to access your IIS-hosted  sites from outside (Usually, you RDP to a remote machine and then check your sites from there).

Step-by-Step Instructions

(01) First, create empty virtual directory on your IIS server, and make sure you can access it on port 80from outside.

In this example we created a virtual directory called zEmptyDir and tested, if it is accessible from outside using address similar to this one:
http://yourDomain.com/zEmptyDir/

Please note that alias( zEmptyDir) should be unique enough to be found and replaced  in a string.

(02) Make sure you can access your internal custom site from your server running IIS.  In this example we are trying to access an internal site http://192.168.0.188:81/Heptagon

(03) Install Application Request Routing from Microsoft ( http://www.iis.net/download/ApplicationRequestRouting) x86 and x64 platforms differ.  Link will open the latest version. Size is under 2 MB.  Also install URL Rewrite Module 2.0 (http://www.iis.net/download/URLRewrite).   Size is about 6 MB.  These two can be installed together using Microsoft Web Installer Platform.

(04)  Enable Proxy for Application Request Routing

Open IIS Manager.   On the left side of the screen you need to select the server node.   On the right side open Application Request Routing.  On the Actions screen click Server Proxy Settings.  Enable Proxy (check box).  Leave all other options as is.

(05) Create Inbound URL Rewrite Rules

Think about your rewrite inbound rules.  In our case, we would need to rewrite URL

from http://yourDomain.com/zEmptyDir/anyDir/anyPage…
to http://192.168.0.188:81/Heptagon/anyDir/anyPage…

Navigate to the site, where your virtual directory zEmptyDir is hosted and open URL Rewrite feature.

Create your inbound rule here.  The result of your work should be saved in a file web.config in the root of the site, where you created your virtual directory:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="zEmptyDir" stopProcessing="true">
                    <match url="^zEmptyDir/(.*)" />
                    <action type="Rewrite" url="http://192.168.0.188:81/Heptagon/{R:1}" logRewrittenUrl="true" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

Note 1
Neither your parent site, nor your custom site do not have to be ASP.NET sites.  In this example the site is actually a PHP WordPress site.  If you did not have web.config file in the site root directory, URL Rewrite will create it there.

Note 2
You do have to match using Regular Expressions.  That is the only way to add the remainder of the URL string into the rewritten URL – {R:1}.

(06) Test to access your site from outside

From a remote machine located outside of your corporate network try to access your custom application by using the link you already tested at step (01): http://yourDomain.com/zEmptyDir/

It should work.  Not all the features, but you should be able to see your site.  Yes!  Your internal application is now accessible from outside.

If nothing changes, it means that you places web.config not in the root of the SITE.  It has to be in the root of the local site, not in the root of your virtual directory.

If you receive an error message, think about the syntax in your rewrite regular expression.

But we are not done.  None of the links are working yet.  Try to login, logout or navigate.  A lot of links are broken.  A lot of functionality is not there.  Now we need to create outbound rules to re-write links inside tags.

(07)  Rewrite Response (Outbound) URLs
If you manage to get thus far, I am sure you are intrigued.  You are thinking: “It might work!”  So gather all the remaining patience of yours and proceed.

Think about your rewrite outbound rules. In our case, we would need to find and rewrite links

from /Heptagon/anyDir/anyPage
to /eEmptyDir/anyDir/anyPage
^ Use Test pattern option (image above) to understand, how to refer to a remainder of the URL that you need to append to a new URL.
^ Note that {R:1} here in action box refers to a {R:1} that you saw, while testing the match pattern.  It is a remainder of the string that you now attaching

You can spend a couple of fun-filled hours trying to figure out all the appropriate options, conditions and pre-conditions on outbound rewire rules interface, or you can just copy this portion of web.config and insert it between tag </rules> and tag </rewrite>:

<outboundRules>
   <rule name="zEmptyDirOutbound" preCondition="IsHTML" enabled="true">
      <match filterByTags="A" pattern="^/Heptagon/(.*)" />
      <conditions>
      </conditions>
      <action type="Rewrite" value="/zEmptyDir/{R:1}" />
   </rule>
</outboundRules>

Interface portion of this task is explained in great detail at source *s03 * (see a link at the bottom of this article).    Search for “Configuring rules for response rewriting” in the middle of the post.

When you done with this step, and it is working, you should see that all your links are now working properly.

But your login redirection or any other code-forced redirection still is not working.  Let’s proceed to a next step to fix that.

(08) Modifying HTTP Response Headers

[This step doesn't work for me neither on IIS nor on IIS 7.5]

I can’t intercept a response redirect to a Login.aspx page.   And I can’t find any articles on how to do it.
Instead, I came up with an additional Inbound rule that fixed the problem.

(09)   Second Inbound Rule to Handle Login Redirection.

This is not the most efficient way to do it, but I can’t find any other way to do it.  First, let’s examine what is happening when your request a protected page – pp.aspx.  Server redirects you to a /OrigAppName/Login.aspx.  That response returns to your Internet client, and remote web browser tries to open a page http://DomainName.com/OrigAppName/Login.aspx

That can’t be resolved, and the user sees an error message.   To fix this you need to add one more inbound URL rewrite rule.  Your front end IIS server should rewrite any requests contaning /OrigAppName/Something… into /OrigAppName:81/Something…

In our case we would redirect according to these rules:

from http://yourDomain.com/Heptagon/anyDir/anyPage…
to http://192.168.0.188:81/Heptagon/anyDir/anyPage…

 Summary and Thirst for More

(1) You do NOT need a new virtual directory zEmtyDir on IIS server.  Here we created zEmptyDir virtual directory just for initial connectivity testing.  If you delete it, your Reverse Proxy configuration will continue to work.

(2) There is a very promising Reverse Proxy option under new URL Rewrite rules, but we did not explore, if it works:

(3) We do not know what this option – Reverse rewrite host in response headers – means in IIS ARR:

There is no additional explanation in Microsoft documentation.

 Sources and Links

Seq Name Link
 *L01*  Install Application Request Routing  http://www.iis.net/download/ApplicationRequestRouting
 *L02*  Install URL Rewrite Module 2.0  http://www.iis.net/download/URLRewrite
 *S03*  Reverse Proxy with URL Rewrite and Application Request Routing  http://learn.iis.net/page.aspx/659/reverse-proxy-with-url-rewrite-v2-and-application-request-routing/
 *S04*  Modifying HTTP Response Headers  http://learn.iis.net/page.aspx/711/modifying-http-response-headers/
(Visited 5,197 times, 1 visits today)

7 Comments

  1. Reverse re-writes not only check HTML code but also HTML Location: headers. The problem with the latter is if I want to redirect to http://youtube.com/myvideo, and set a Location: header to do it, IIS will mangle the redirect to try and go to http://mysite.com/myvideo – FAIL.

    The option “Reverse rewrite host in response headers” allows admin to disable this behaviour.

    References: https://wordpress.org/support/topic/404-not-found-79 bug for WordPress Postman SMTP plugin.

  2. How do you guard against hackers using this to completely bypass your firewall and get access to your internal network.

  3. I do trust all the ideas you’ve offered on your post. They’re rllaey convincing and will certainly work. Nonetheless, the posts are very quick for beginners. May just you please extend them a bit from subsequent time? Thanks for the post.

  4. Apologies for the last comment. I meant, when I turn on the outbound rule, I get 500. Inbound works fine.

  5. The second inbound rule looks like this:

    <rule name="Fix Heptagon/Login.aspx" patternSyntax="ECMAScript">
    <match url="^Heptagon/(.*)" />
    <action type="Rewrite" url="http://192.168.0.188:81/Heptagon/{R:1}" />
    </rule>

    If after Login or any other redirection, client requests a page with local (internal) directory name, IIS should rewrite the address using proper local (internal) URL. If I remove this rule, Login.aspx will never be reached.

  6. Hi ,

    I think i have similar issue on item (08) Modifying HTTP Response Headers. Could you share with me your 2nd Inbound rule setting ?

    Thanks

Your question, correction or clarification Ваш вопрос, поправка или уточнение