5

I am trying to compare two server variables as part of a URL rewrite condition. But first, some context...

In IIS, if you request http://www.example.com/foo and foo is a directory, IIS sends a 302 "Object moved" redirect to http://www.example.com/foo/ as a "courtesy redirect" (Source).

If you are using IIS+ARR as reverse proxy with SSL offloading, the request that the back-end IIS node receives is over http, not https.

Combine these two behaviors, and IIS's courtesy redirects drop SSL. The courtesy redirect uses the same scheme as the request that IIS received (Source), http in this case instead of https.

I'd like to create an outbound rewrite rule that will compare the incoming URL with the outgoing Location header and rewrite it from http to https in this case:

  • The response is a redirect: {RESPONSE_STATUS} is 302
  • The incoming request was over SSL: {HTTPS} is "on"
  • The incoming URL does not end in a slash.
  • The outgoing Location header ends in a slash.

All of the above is handled in the precondition. The tricky part is the last bit:

  • The path of the outgoing Location header is the same as the incoming URL with a slash appended.

Below is the code I have so far. The precondition and header rewrite both work fine. However, the condition causes a 500 error (URL Rewrite module error), presumably because I'm using {REQUEST_URI} in the pattern. I've tried separating the condition into two and using capture groups, but that didn't work either. Any ideas?

<rule name="Fix: Courtesy Redirect + SSL Offloading = SSL dropped" preCondition="Courtesy Redirect Drops SSL" enabled="true">
    <match serverVariable="RESPONSE_LOCATION" pattern="^http://(.+/)$" />
    <conditions>
        <add input="{RESPONSE_LOCATION}" pattern="{REQUEST_URI}/" />
    </conditions>
    <action type="Rewrite" value="https://{R:1}" />
</rule>
<preConditions>
    <preCondition name="Courtesy Redirect Drops SSL">
        <add input="{RESPONSE_STATUS}" pattern="^302$" />
        <add input="{HTTPS}" pattern="^on$" />
        <add input="{REQUEST_URI}" pattern=".*[^/]$" />
        <add input="{RESPONSE_LOCATION}" pattern="^http://.+/$" />
    </preCondition>
</preConditions>
Michael
  • 173
  • 9
MALfunction84
  • 161
  • 1
  • 10

1 Answers1

3

You can use a custom rewrite provider. A provider is C# code that turns one string into another string. You can then use it in a similar way to how you'd use rewrite map:

You can pick a separator which is not valid in a url at all. (Maybe use space or something like that. I'll use | so it's visible in this post, but you should pick some other string.)

You'll write a rule to set the value of server variable IsItMatching. The value of the server variable will be set using your custom url rewrite provider:

{provider_name:{server_variable_1}|{server_variable_2}}

The C# code implementing the provider will then do this (pseudo-code, no error checking):

string Rewrite(string input)
{
    string[] inputVariables = input.split(separator);
    if (inputVariables[0] == inputVariables[1] + "/")
        return "yes";
    else
        return "no";
}

Then you'll write one more rule to check whether the value of the IsItMatching server variable is "yes" or "no".

Michael
  • 173
  • 9
Palo
  • 131
  • 2