Create your own CDN using IIS Outbound Rules

Offloading content is important for the performance of your website, because then content is downloaded in parallel and thus faster. A faster website attracts more visitors, is more user friendly, improves CTR and Google SEO. Here is how to create a Content Delivery Network (CDN) using IIS outbound rewrite rules.
Published on Sunday, 29 September 2013

Use these IIS Outbound Rules to offload javascript and CSS stylesheets.

Offload static content and create a Content Delivery Network (CDN) with IIS Outbound Rules

Last february I wrote an article on how to create your own Origin Pull CDN with PHP. Content offloading (Dutch) is important for the performance of your website, because content is downloaded in parallel and thus faster, and from cookieless hostnames.

Using IIS Outbound Rules can make your website a whole lot faster, or break it when used improperly. Also note: Evaluation of regular expression patterns on the entire response content is a CPU intensive operation and may affect the performance of the web application (URL Rewrite Module 2.0 Configuration Reference)

I decided to give it a go and here is the write-up.

Step 1: define what to rewrite

Because Outbound Rules are applied in the output stream, it is important to define what you want to rewrite. I chose to rewrite all .css stylesheet and .js JavaScript requests. For convenience, requests are rewritten to different hostheaders (subdomains, IIS bindings). If you like, you can also choose to rewrite requests for images and such.

The Outbound Rule

<rule name="CDN-01-css" preCondition="CheckHTML" stopProcessing="true">
  <match filterByTags="Link" pattern="http(s)?://www.(saotn.org)/(.*\.css)" />
  <action type="Rewrite" value="http{R:1}://cdn.{R:2}/{R:3}" />
</rule>

This rule looks at HTML tag for stylesheet URLs. If one is found, the output is rewritten to a new value: cdn.saotn.org/path/to/style.css. We do the same for JavaScript:

<rule name="CDN-01-js" preCondition="CheckHTML" stopProcessing="true">
  <match filterByTags="Script" pattern="http(s)?://www.(saotn.org)/(.*\.js)" />
  <action type="Rewrite" value="http{R:1}://static.{R:2}/{R:3}" />
</rule>

Notice the preCondition.

Step 2: define Preconditions

A precondition is used to evaluate whether the outbound rules evaluation should be performed on a response. For example if a rule that modifies HTML content, only HTTP responses with content-type header set to "text/html" should be evaluated against this rule. Outbound rules evaluation and content rewriting is a CPU intensive operation that may negatively affect the performance of a web application. Therefore, use preconditions to narrow down the cases when outbound rules are applied. (source: iis.net)

So, we need to create a precondition.

The preCondition rule

<preCondition name="CheckHTML">
  <add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>

All this rule does is verifying the CONTENT_TYPE of the response. If it is "text/html" the IIS Outbound Rule is applied.

Install IIS URL Rewrite Module in Windows Server

Complete Outbound Rules web.config

The complete Outbound Rules to use in your web.config file is:

<rewrite>
  <outboundRules rewriteBeforeCache="true">
    <rule name="CDN-01-css" preCondition="CheckHTML" stopProcessing="true">
      <match filterByTags="Link" pattern="http(s)?://www.(saotn.org)/(.*\.css)" />
      <action type="Rewrite" value="http{R:1}://cdn.{R:2}/{R:3}" />
    </rule>
    <rule name="CDN-01-js" preCondition="CheckHTML" stopProcessing="true">
      <match filterByTags="Script" pattern="http(s)?://www.(saotn.org)/(.*\.js)" />
      <action type="Rewrite" value="http{R:1}://static.{R:2}/{R:3}" />
    </rule>
    <preConditions>
      <preCondition name="CheckHTML">
        <add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
      </preCondition>
    </preConditions>
  </outboundRules>
</rewrite>

The rewriteBeforeCache = "true" is important in situations where IIS user mode caching is used.

Even though you could copy/paste these rules into your web.config file, it still is a challenge to get it to work properly. There are a lot of IIS settings you need to take into account, like HTTP.SYS (kernel) Output Cache, Dynamic Compression, Static Compression (dynamicCompressionBeforeCache = "true" and dynamicCompressionBeforeCache = "false"), other caches, response headers, and so on.

Don't give up after a failed attempt.

Outbound rewrite rules with gzip encoded HTTP response

If or when you encounter the HTTP 500 error message Outbound rewrite rules cannot be applied when the content of the HTTP response is encoded ("gzip")., see my post IIS Outbound Rules with gzip compression on how to fix that error.

Global geolocation DNS load balancing CDN

You may even create a global DNS load balancing and Varnish Cache (CDN) service on relative cheap DigitalOcean droplets.