DEV Community

Cover image for Web.config redirects with rewrite rules - https, www, and more
Thomas Ardal
Thomas Ardal

Posted on • Originally published at blog.elmah.io

Web.config redirects with rewrite rules - https, www, and more

Rewrite rules is a powerful feature in IIS. Common tasks like redirecting www to non-www (or the other way around), implementing canonical URLs, redirecting to HTTPS, and similar tasks are documented right there in your Web.config file. In this post, you will learn about the syntax of rewrite rules and how to implement the mentioned common tasks, and much more.

When needing to redirect one URL to another, a lot of different options exist. Some tasks can be implemented in the DNS. Others are done through reverse proxies like Nginx and Squid. Sometimes you just want to redirect a single URL and you can choose to modify the HTTP response from within an MVC controller.

A much more generic solution is IIS rewrite rules. With rules, you can create a common set of rules and make IIS enforce these over multiple URLs and even across applications.

Rewrite rules can be either global (in the applicationHost.config file) or local (in the web.config file). There are several ways to add both global and local rules. IIS Manager has built-in support for creating rules, but it is my impression most people don't have IIS installed locally. Rules have been the source of lots of frustration on StackOverflow, which is why using IIS Manager for creating your first rule can be a good choice.

Creating a new rule

To create a new rule, launch IIS Manager, and click a website:

See the URL Rewrite icon below the IIS section? If not, make sure to download and install the URL Rewrite extension: https://www.iis.net/downloads/microsoft/url-rewrite.

Double-click the URL Rewrite icon and click the Add Rule(s)... link from the Actions menu:

There is a wide range of different templates to choose from. As a first-time rule developer, getting a bit of assistance is a nice little feature of the IIS Manager. I'm not saying you shouldn't learn the underlying syntax (you need to), but looking at pre-generated examples is a good starting point.

Select the Append or remove the trailing slash symbol template and click the OK button. In the following step, keep the Appended if it does not exist selection and click OK. A new inbound rule is created:

Requesting the website without a trailing slash (/) will automatically append the slash using the newly created rule.

Since the rule was created directly on the website, the new rule is added to the web.config file of the website. You hopefully understand why this approach shouldn't be used in your production environment. Every piece of configuration should be in either source control or deployment software like Octopus Deploy or Azure DevOps. For playing around with rules, the UI approach is excellent, though.

Let's take a look at the generated code inside the web.config file:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="AddTrailingSlashRule1" stopProcessing="true">
                    <match url="(.*[^/])$" />
                    <conditions>
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    </conditions>
                    <action type="Redirect" url="{R:1}/" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

Enter fullscreen mode Exit fullscreen mode

At first glance, rewrite rules can be a challenge to read. If you feel this way, I understand you. Configuring software in deeply nested XML never been my favorite either. Let's go through the markup one element at the time.

Inbound and outbound rules

All rules must be added in the element inside <system.webServer>. There are two types of rules available: inbound rules (defined in the <rules> element) and outbound rules (defined in the <outboundRules> element). In the example above, the rewrite rule is added as an inbound rule. Inbound rules are executed against the incoming request. Examples of these rules are: add a trailing slash (yes, the example from previous), rewrite the request to a different URL, and blocking incoming requests based on headers. Outbound rules are executed against the response of an HTTP request. Examples of outbound rules are: add a response header and modify the response body.

Both inbound and outbound rules are defined in a <rule> element. In the example above, a new rule named AddTrailingSlashRule1 is added. The name of each rule is left for you to decide, as long as you give each rule a unique name. Also, take notice of the stopProcessing attribute specified on the rule. If set to true, IIS will stop executing additional rules if the condition specified inside the rule matches the current request. You mostly want to specify false in this attribute, unless you are redirecting the entire request to another URL.

Match, action, and conditions

All rules have a pattern, an action, and an optional set of conditions. The example above has all three.

The pattern is implemented in the <match> element and tells the rewrite extension which URLs to execute the rule for. Patterns are written using regular expressions in the url attribute. Regular expressions is an extremely complicated language, which deserves a very long blog post on its own. I write regular expressions by using Google to find examples of how people already wrote examples of regular expressions looking like what I would like to achieve in each case.

The <action> element tells IIS what to do about requests matching the pattern. All actions need a type attribute to tell IIS how to move forward. type can have one of the following values:

  • None: do nothing.
  • Rewrite: rewrite the request to another URL.
  • Redirect: redirect the request to another URL.
  • CustomResponse: return a custom response to the client.
  • AbortRequest: drop the HTTP connection for the request.

In the example above, the action used a Redirect type, which means that the web server will return an HTTP redirect to the client. You can specify if you want to use a permanent or temporary redirect using the redirectType attribute.

I don't want to list every possible element and value inside this post since the official MSDN documentation is quite good. Visual Studio also provides decent IntelliSense for rewrite rules. Browse through the examples last in this post to get an overview of what is possible with rewrite rules.

Common rules

This is a collection of common rules that we are either using on elmah.io or I have used in the past.

Redirect to HTTPS

<rule name="RedirectToHTTPS" stopProcessing="true">
  <match url="(.*)" />
  <conditions>
    <add input="{HTTPS}" pattern="off" ignoreCase="true" />
  </conditions>
  <action type="Redirect" url="https://{SERVER_NAME}/{R:1}" redirectType="Permanent" />
</rule>
Enter fullscreen mode Exit fullscreen mode

Redirect www to non-www

<rule name="RedirectWwwToNonWww" stopProcessing="false">
  <match url="(.*)" />
  <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
    <add input="{HTTP_HOST}" pattern="^(www\.)(.*)$" />
  </conditions>
  <action type="Redirect" url="https://{C:2}{REQUEST_URI}" redirectType="Permanent" />
</rule>
Enter fullscreen mode Exit fullscreen mode

Redirect non-www to www

<rule name="RedirectNonWwwToWww" stopProcessing="true">
  <match url="(.*)" />
  <conditions>
    <add input="{HTTP_HOST}" pattern="^domain.com$" />
  </conditions>
  <action type="Redirect" url="http://www.domain.com/{R:0}" redirectType="Permanent" />
</rule>
Enter fullscreen mode Exit fullscreen mode

Reverse proxy

In this example, I'm using rewrite rules to serve a blog hosted on GitHub but through a custom domain name. By using the Rewrite action type, the URL stays the same, even though the content is served from elmahio.github.io.

<rule name="ReverseProxy" stopProcessing="true">
  <match url="(.*)" />
  <conditions logicalGrouping="MatchAll">
    <add input="{REQUEST_URI}" negate="true" pattern="^(.*)/.well-known/(.*)$"/>
  </conditions>
  <action type="Rewrite" url="http://elmahio.github.io/blog/{R:1}" />
</rule>
Enter fullscreen mode Exit fullscreen mode

Rewrite all but elmah.axd

<rule name="RewriteAllButElmahAxd" stopProcessing="true">
  <match url="(.*)" />
  <conditions>
    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
    <add input="{URL}" negate="true" pattern="\elmah.axd$" />
  </conditions>
  <action type="Rewrite" url="http://elmahio.github.io/blog/{R:1}" />
</rule> 
Enter fullscreen mode Exit fullscreen mode

Add trailing slash

<rule name="AddTrailingSlash" stopProcessing="true">
  <match url="(.*[^/])$" />
  <conditions>
    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
  </conditions>
  <action type="Redirect" url="{R:1}/" redirectType="Permanent" />
</rule>
Enter fullscreen mode Exit fullscreen mode

Lowercase all URLs

<rule name="LowercaseAllUrls" stopProcessing="true">
  <match url=".*[A-Z].*" ignoreCase="false" />
  <action type="Redirect" url="{ToLower:{R:0}}" />
</rule>
Enter fullscreen mode Exit fullscreen mode

Would your users appreciate fewer errors?

elmah.io is the easy error logging and uptime monitoring service for .NET. Take back control of your errors with support for all .NET web and logging frameworks.

➡️ Error Monitoring for .NET Web Applications ⬅️

This article first appeared on the elmah.io blog at https://blog.elmah.io/web-config-redirects-with-rewrite-rules-https-www-and-more/

Top comments (2)

Collapse
 
dsesteban profile image
Dennis Pérez • Edited

This works smoothly to me, thanks a lot! Do you know how could i now add some cache rules for static content in that same web.config file? something like this:

<configuration>
  <system.webServer>
    <staticContent>
      <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="00:00:15" />
    </staticContent>
  </system.webServer>
</configuration>
Enter fullscreen mode Exit fullscreen mode

every single time i've tried, my app fails.

Collapse
 
thomasardal profile image
Thomas Ardal

Don't know off the top of my head. Maybe look for something like this: stackoverflow.com/a/2196848/758765 ?