Try our conversational search powered by Generative AI!

Johan Antila
Sep 4, 2019
(2 votes)

Scripted testing of IIS URL Rewrite rules

On most websites today you are likely to need to rewrite urls to match a specific requirement such as the typical SEO rules where you need urls to all end with a trailing slash and be all lowercase to prevent duplicate content in the search engines or add a few hard coded redirects for old discontinued sites. While these rules by themselves are easy enough to test, as soon as you start creating a few more rules and they contain host names, you will get a bit of a challenge to test these locally.
While it is possible to test the rules locally in the IIS Administration tool, this is only an atomic test of the single rule and not a test of the entire redirect pipeline so if you introduce one bad rule, you can't tell if it breaks the other redirects unless you request it with a browser through IIS and with a lot of urls, this becomes increasingly impractical and in the scenario where you yourself aren't handling the go-live and you haven't been able to test the rules locally, it creates the need to write these same rules as a human readable script for whoever is doing the deploy/rollback to test after applying the change. If only there was a way to script this. Well there is.
Using nodejs with puppeteer, you can easily create a test script in which you define all your urls you want to test and see if they are redirecting as expected.

First you need to install nodejs if you haven't already done that, then in a console window, install puppeteer , a module to programmatically control googles Headless Chrome.

npm install puppeteer

With puppeteer you can do all sorts of things with automatic testing and we are going to use it in a pretty simple way, let it navigate to a url and see what url it ends up on and test if we were expecting this url. The nice thing here is that we're testing what a browser would actually do after it has completed all the redirects, even ones you've written in code for redirecting users based on geo-ip for instance and if you have a redirect loop here, you will get an error.

Now you need to make sure you have all the redirect rules in place for production in your local dev environment, ie, in web.config and if you're not comfortable with having these rules permanently in your development environment, you could set up a config transformation and a custom msbuild target that will create the web.config with local settings and the redirects included. If you're not familiar with how to do this, this is how I generate config files when I need to see how they would look when deployed to the Episerver DXC that uses config transformations:

<Target Name="DXCConfigs">
  <TransformXml Source="web.config" Transform="Web.integration.config" Destination="web.integration.out.dxc.config" />
  <TransformXml Source="web.integration.out.dxc.config" Transform="web.preproduction.config" Destination="web.preproduction.out.dxc.config" />
  <TransformXml Source="web.preproduction.out.dxc.config" Transform="web.production.config" Destination="web.production.out.dxc.config" />

and you run it from the command-line like this:

msbuild /T:DXCConfigs

After this is done, you need to make sure you local IIS listens to all domains you are going to test. The easiest way to accomplish this is to just add a wildcard mapping but if this is not possible for you, add them one by one under Edit Bindings in IIS. Note that if the redirect rules include upgrading the connection to https, you need to add a self signed certificate and add mappings in IIS for this too.

Now edit the hosts file and add your hosts

One thing to note is that if you are testing redirects with HTTPS, you need Chromium to ignore SSL certificate warnings for your local certs for the script to work, this is done by launching puppeteer with the ignoreHTTPSErrors flag set to true as can be seen in the script below.

My script looks like this:

Test redirects

const puppeteer = require('puppeteer');

(async () => {

  async function testurl( fromurl, tourl) {
  try {
     await page.goto(fromurl, {
     waitUntil: 'networkidle2'
   if (await page.url() === tourl) {
     await console.log("Success: ", page.url());
     return true;
   } else {
     console.log("*** Fail. " + fromurl + " Got: ", page.url() + " expected: " + tourl);
     return false;
  } catch (error) {
   console.log("*** Error " + fromurl + " " + error);

  // set up puppeteer to ignore HTTPS errors so it won't bomb on your self-signed certificates
  const browser = await puppeteer.launch({
    ignoreHTTPSErrors: true

  const page = await browser.newPage();

  await testurl('', '');
  await testurl('', '');
  await testurl('https://www.EPiServer.COM/', '');
  await testurl('', '');
  await testurl('', '');

  await browser.close();

I only used my script to manually validate that the rules I introduced didn't break any of the other redirects (it turned out they did), especially the ones I had in the Geta 404 handler, but if you need to do this in a more unittest-like fashion, see this post for inspiration on how to use an assertion framework with Headless Chrome.

Finally, when you are sure you didn't break anything with your new rules and they were deployed into production, just comment out the entries in the hosts file and run the script again to test the rules in production if you'd like to.

Sep 04, 2019


Sep 5, 2019 01:16 PM

We created a tool for generating IIS rewrite rules from a speadsheet. I recently updated it so when it creates them it creates a full suite of postman tests in a collection which validate the to and from. Used it for validating 50k redirects on a recently project and worked really well

Johan Antila
Johan Antila Sep 6, 2019 09:18 AM

Thanks Scott, that's clever! If anyone is interested in that approach, you can read about it here:

Daniel Ovaska
Daniel Ovaska Sep 10, 2019 08:34 AM

Ah! Cool. I have to test that! Great work guys

Please login to comment.
Latest blogs
Change the IP HTTP Header used for geo-lookup in Application Insights


Johan Kronberg | Jun 10, 2024 | Syndicated blog

Copying property values

In this article I’d like to show simple Edit Mode extension for copying property values to other language versions. In one of my previous blogposts...

Grzegorz Wiecheć | Jun 8, 2024 | Syndicated blog

Auto-translate with OpenAI GPT-4o in Optimizely CMS

Improvements for Episerver.Labs.LanguageManager! It's now possible to auto-translate both a page and its children at the same time! Additionally, m...

Tomas Hensrud Gulla | Jun 7, 2024 | Syndicated blog

Upgrade To Optimizely CMS 12 Issue: List item fields have become Required

There are many funny details to be aware of when upgrading from Episerver CMS 11 to Optimizely CMS 12. One of them that might feel a bit confusing ...

Allan Thraen | Jun 7, 2024 | Syndicated blog