Jekyll and IIS Web Configuration in Windows Azure

Written by Khalid Abuhakmeh
7
This post is days old.

In a previous post, “Deploying Jekyll on Windows Azure”, I detailed how to get a Jekyll based site on to Windows Azure with relative ease. During that time, the team has done several deployments to our cloud provider. With those deployments, we realized we needed to commit more work into the site’s web.config to get the solution we really wanted. We are happy with our current outcome, but understand there may be edge cases we still haven’t hit, and hope to come back and update this post when necessary.

HTTP Errors

It is rare to have server-side errors on a staticly generated site, and the most common one will be 404. Right now, we are just leaning on the httpErrors element under system.webServer to redirect to a default error page.

<httpErrors defaultPath="/woops.html" defaultResponseMode="Redirect"></httpErrors>

HTML5 Static Content

HTML5 is a part of our site building strategy, but the initial IIS configuration is not supportive of our strategy. With some help from Mads Kristensen, we modified our web.config with the staticContent element.

<staticContent>
    <mimeMap fileExtension=".mp4" mimeType="video/mp4" />
    <mimeMap fileExtension=".m4v" mimeType="video/m4v" />
    <mimeMap fileExtension=".ogg" mimeType="video/ogg" />
    <mimeMap fileExtension=".ogv" mimeType="video/ogg" />
    <mimeMap fileExtension=".webm" mimeType="video/webm" />
    <mimeMap fileExtension=".oga" mimeType="audio/ogg" />
    <mimeMap fileExtension=".spx" mimeType="audio/ogg" />
    <mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
    <mimeMap fileExtension=".svgz" mimeType="image/svg+xml" />
    <remove fileExtension=".eot" />
    <mimeMap fileExtension=".eot" mimeType="application/vnd.ms-fontobject" />
    <mimeMap fileExtension=".otf" mimeType="font/otf" />
    <mimeMap fileExtension=".woff" mimeType="font/x-woff" />
</staticContent>

Rewrite Rules

Rewrite rules are the most critical part of our Jekyll/IIS configuration and account for much of the necessary behavior we are looking for. Not all the rewrite rules you will see below are Jekyll specific. Rewrite rules are normally located under system.webServer/rewrite.

Redirecting to HTTPS

All of our sites will run on HTTPS by default. To enforce the secure behavior we used an inbound rule.

<rule name="Redirect to HTTPS" stopProcessing="true">
    <match url="(.*)" />
    <conditions>
        <add input="{REMOTE_PORT}" pattern=".*" />
        <add input="{HTTPS}" pattern="^OFF$" />
    </conditions>
    <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
</rule>

Support Non-Trailing Slash Urls

Trailing slashes are no longer a default in Jekyll 3. If you find yourself on a previous version those slashes can be annoying. Additionally, you may have someone accidentally add a trailing slash, because old habits die hard. Generally, this isn’t a behavior we liked, so we needed to create a rule that supported access to pages without the trailing slash.

<rule name="RewriteHtml">
    <match url="^(.*)$" />
    <conditions logicalGrouping="MatchAll">
        <add input="{REMOTE_PORT}" pattern=".*" />
        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
    </conditions>
    <action type="Rewrite" url="{R:1}.html" />
</rule>

All Jekyll pages are statically generated and suffixed with html. Using the rewrite module, we are able to write the contents of an HTML file directly into the response.

Remove WWW

We wanted a canonical url for our site. This meant specifically removing the www from our urls and reducing the possible permutations.

<rule name="Remove WWW" patternSyntax="Wildcard" stopProcessing="true">
    <match url="*" />
    <conditions>
        <add input="{REMOTE_PORT}" pattern=".*" />
        <add input="{CACHE_URL}" pattern="*://www.*" />
    </conditions>
    <action type="Redirect" url="{C:1}://{C:2}" redirectType="Permanent" />
</rule>

Relative Urls to Absolute Urls

For SEO reasons, we wanted to make all our links and images use absolute urls. This is easy to do with the outboundRules element.

<outboundRules rewriteBeforeCache="true">
    <rule name="Rewrite relative to absolute" preCondition="IsHtml">
        <match filterByTags="A, Img" pattern="^/(.*)" />
        <action type="Rewrite" value="https://{HTTP_HOST}/{R:1}" />
    </rule>
    <preConditions>
        <preCondition name="IsHTML">
            <add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
        </preCondition>
    </preConditions>
</outboundRules>

I want to note a really BIG gotcha with the outbound rule above.

<outboundRules rewriteBeforeCache="true">
...
</outboundRules>

By default, Windows Azure wants to gzip your static content. You cannot apply an outbound rule on content that has been compressed. If your Jekyll site begins to fail after adding this rule, you probably forgot the rewriteBeforeCache attribute.

The Complete Configuration

Putting all of these configuration settings together, we get the following web.config:

<?xml version="1.0"?>
<configuration>
    <system.webServer>
        <httpErrors defaultPath="/woops.html" defaultResponseMode="Redirect"></httpErrors>
        <modules runAllManagedModulesForAllRequests="true" />
        <rewrite>
            <rules>
                <rule name="Redirect to HTTPS" stopProcessing="true">
                    <match url="(.*)" />
                    <conditions>
                        <add input="{REMOTE_PORT}" pattern=".*" />
                        <add input="{HTTPS}" pattern="^OFF$" />
                    </conditions>
                    <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
                </rule>
                <rule name="RewriteHtml">
                    <match url="^(.*)$" />
                    <conditions logicalGrouping="MatchAll">
                        <add input="{REMOTE_PORT}" pattern=".*" />
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="{R:1}.html" />
                </rule>
                <rule name="Remove WWW" patternSyntax="Wildcard" stopProcessing="true">
                    <match url="*" />
                    <conditions>
                        <add input="{REMOTE_PORT}" pattern=".*" />
                        <add input="{CACHE_URL}" pattern="*://www.*" />
                    </conditions>
                    <action type="Redirect" url="{C:1}://{C:2}" redirectType="Permanent" />
                </rule>
            </rules>
            <outboundRules rewriteBeforeCache="true">
                <rule name="Rewrite relative to absolute" preCondition="IsHtml">
                    <match filterByTags="A, Img" pattern="^/(.*)" />
                    <action type="Rewrite" value="https://{HTTP_HOST}/{R:1}" />
                </rule>
                <preConditions>
                    <preCondition name="IsHTML">
                        <add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
                    </preCondition>
                </preConditions>
            </outboundRules>
        </rewrite>
        <staticContent>
            <mimeMap fileExtension=".mp4" mimeType="video/mp4" />
            <mimeMap fileExtension=".m4v" mimeType="video/m4v" />
            <mimeMap fileExtension=".ogg" mimeType="video/ogg" />
            <mimeMap fileExtension=".ogv" mimeType="video/ogg" />
            <mimeMap fileExtension=".webm" mimeType="video/webm" />
            <mimeMap fileExtension=".oga" mimeType="audio/ogg" />
            <mimeMap fileExtension=".spx" mimeType="audio/ogg" />
            <mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
            <mimeMap fileExtension=".svgz" mimeType="image/svg+xml" />
            <remove fileExtension=".eot" />
            <mimeMap fileExtension=".eot" mimeType="application/vnd.ms-fontobject" />
            <mimeMap fileExtension=".otf" mimeType="font/otf" />
            <mimeMap fileExtension=".woff" mimeType="font/x-woff" />
        </staticContent>
    </system.webServer>
</configuration>
Suggested reading

Comments