|   Contact  

Enabling CORS for specific domains in IIS using URL Rewrite


November 2015

If you are writing modern applications one thing that is becoming more and more common is the use of Cross-Origin Resource Sharing otherwise known as CORS. If you want to learn all the details about it you can go to: http://www.w3.org/TR/cors/.

A super simplification of the flow for the purpose of this article is that the client (like a browser) sends the request and includes a header “Origin” including the origin of the request. The server then can make decisions depending on the origin and in response add a Access-Control-Allow-Origin header that specifies a list of origins, or a “*” to indicate that it is allowed.

Now the problem is when you already have an application and cannot modify the code (or do not want to do it), is there a way to enable CORS and do the more advanced handling such as responding the Access-Control-Allow-Origin with the Origin header in the incoming request such as in the case when Access-Control-Allow-Credentials is required?

Turns out using URL Rewrite you can very easily achieve that without writing a single line of code, and best of all works with all version of IIS 7 and above.

At a high level the steps required are:

  1. Add a URL Rewrite Inbound Rule to capture the Origin header and set it in a Server Variable (this will require adding a Server Variable to the list of allowed sever variables for security reasons)
  2. Add a URL Rewrite Outbound Rule to add a Access-Control-Allow-Origin header with the server variable set above.
  3. For completeness here we will do that based on a URL Rewrite Map that will have a list of allowed origins

 

1. Add a URL Rewrite Inbound Rule to capture the Origin header

  1. Run IIS Manager
  2. Navigate to your site and click  URL Rewrite
  3. Click Add Rule..
  4. Enter the following values:
    1. Name: Capture Origin Header
    2. Matches the pattern: .*
    3. Add a condition to capture the header:
      1. Condition Input: {HTTP_ORIGIN}
      2. Pattern:  .+
    4. Set it as a server variable
      1. server variable name: CAPTURED_ORIGIN
      2. value: {C:0}
    5. Action Type: None

 

This rule in your web.config will look like as below:

    <rule name="Capture Origin Header">
       
<match url=".*" />
        <
conditions>
           
<add input="{HTTP_ORIGIN}" pattern=".+" />
        </
conditions>
       
<serverVariables>
           
<set name="CAPTURED_ORIGIN" value="{C:0}" />
        </
serverVariables>
       
<action type="None" />
    </
rule>

It is basically matching every URL (regardless if static content, dynamic, etc), and will look for the Origin header (notice that URL Rewrite uses the convention of prefixing by HTTP_ for headers), if it has an value then it will set a server variable called CAPTURED_ORIGIN so we can later refer to it in the response to set a header.

Allowing the CAPTURED_ORIGIN Server Variable

For security reasons URL Rewrite will not let you overwrite headers and server variables at will since that could mean potentially a security risk, where someone could overwrite authentication headers or the like. For that reason by default it will not let you change any. You have to add every header or server-variable to a list of allowed headers to be overridden.

  1. Back in URL Rewrite at the site level click the “View Server Variables” link in the Manage Server Variables section.
  2. Add one for CAPTURED_ORIGIN and since we are here add one for RESPONSE_Access-Control-Allow-Origin

Note that this change is made in ApplicationHost.config since that section is locked by default to allow administrators/IT owners to have control over which headers are allowed. You could unlock the section if you wanted to add that along in your web.config.
    <location path="Default Web Site">
       
<system.webServer>
           
<rewrite>
               
<allowedServerVariables>
                   
<add name="CAPTURED_ORIGIN" />
                    <
add name="RESPONSE_Access-Control-Allow-Origin" />
                </
allowedServerVariables>
           
</rewrite>
       
</system.webServer>
   
</location>

Note that the convention to set response headers is to prefix them with RESPONSE_.

 

2. Add a URL Rewrite Outbound Rule to add a Access-Control-Allow-Origin

Now that we have captured the Origin header and have specified that it is ok to override the Access-Control-Allow-Origin, lets write a Map where we’ll keep the list of origins that we want to allow and rewrite for those.

First lets add the RewriteMap

  1. Back in URL Rewrite at the site level click the View Rewrite Maps.
  2. Add a new map called AllowedOrigins and add all the origins you want to allow, for example: add a name: foo.com and value foo.com. The value will be the value that will be set in the header.

Now add an Outbound Rule

  1. Set it to match a server variable, RESPONSE_Access-Control-Allow-Origin
  2. Set it to: Does not match the pattern: .+
  3. Add a condition:
    1. Input: {AllowedOrigins:{CAPTURED_ORIGIN}}
    2. Pattern: .+
  4. Set the action to Rewrite, Value: {C:0}

 

The complete web.config file looks like:

<configuration>
   
<system.webServer>
       
<rewrite>
           
<rules>
               
<rule name="Capture Origin Header">
                   
<match url=".*" />
                    <
conditions>
                       
<add input="{HTTP_ORIGIN}" pattern=".+" />
                    </
conditions>
                   
<serverVariables>
                       
<set name="CAPTURED_ORIGIN" value="{C:0}" />
                    </
serverVariables>
                   
<action type="None" />
                </
rule>
           
</rules>
           
<outboundRules>
               
<rule name="Set-Access-Control-Allow-Origin for known origins">
                   
<match serverVariable="RESPONSE_Access-Control-Allow-Origin" pattern=".+" negate="true" />
                    <
conditions>
                       
<add input="{AllowedOrigins:{CAPTURED_ORIGIN}}" pattern=".+" />
                    </
conditions>
                   
<action type="Rewrite" value="{C:0}" />
                </
rule>
           
</outboundRules>
           
<rewriteMaps>
               
<rewriteMap name="AllowedOrigins">
                   
<add key="foo.com" value="foo.com" />
                    <
add key="bar.com" value="bar.com" />
                </
rewriteMap>
           
</rewriteMaps>
       
</rewrite>
   
</system.webServer>
</configuration>

 

Summary:

The rules above will basically do the following:

  1. Inspect all the requests, it checks to see if it has an Origin header
    1. if it does have a header then it will set it to the server variable CAPTURED_ORIGIN
    2. if it does not then nothing is done
  2. In the outbound when the response is about to be sent, then it will check to see if the Access-Control-Allow-Origin header is empty, then
    1. it will lookup the value of the CAPTURED_ORIGIN server variable in the map AllowedOrigins, and if there is a key that matches, then it will capture the value of that.
    2. Then it will set the Access-Control-Origin header to that value

 

You can hopefully realize how powerfull the combination of Inbound rules and Outbound rules are in URL Rewrite and how you could extend the sample above to handle scenarios to request credentials or not.


Carlos Aguilar Mares © 2015