IIS Rewrite Rule for Single Page Apps

Most of the time when I’m building a single page app, I want to use real URLS, rather than that hash-based nonsense that is generally the default. Most of the frameworks, (Angular.js, react-router, vue-router) refer to this url mode as “history” or “html5” mode. They use the relatively recent history API to push and pop URLs onto the browser’s navigation stack without incuring a full round trip to the server.

So you get nicer URLs like http://example.com/users/edit instead of https://example.com/#/users/edit. I don’t like that hash mark.

The downside is that if the user saves a bookmark or sends a link to a friend, the browser will actually request a resource at /users/edit (browsers don’t send anything after # in a URL).

You need to configure the web server to respond to that URL by sending back the html shell that that SPA loads into, otherwise you probably get a 404, and no one likes that.

In IIS, you can use the rewrite module to teach the webserver to respond to all urls by returning your main index.html file.

However, you probably also have some API resources living under /api. These URLS should actually be handled by the webserver.

And you’ve got some static files sitting around in various places, so you’d like to not rewrite those urls either.

Here’s the magic incantation.

web.config
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<rewrite>
<rules>
<rule name="SPA Routes" stopProcessing="true">
<!-- match everything by default -->
<match url=".*" />

<conditions logicalGrouping="MatchAll">
<!-- unless its a file -->
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />

<!-- or a directory -->
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />

<!-- or is under the /api directory -->
<add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />

<!-- list other routes or route prefixes here if you need to handle them server side -->

</conditions>

<!-- rewrite it to /index.html -->
<action type="Rewrite" url="/index.html" />

</rule>
</rules>
</rewrite>

Rewrite means “respond directly to the request with this content.”

Redirect means respond with a 301 or 302 status code instructing the browser to make a susequent request for this resource. You dont want this.