Redirects on Microsoft IIS
IIS gives you two ways to redirect: the built-in HTTP Redirect feature and the URL Rewrite module. Here is when to use which, with web.config examples you can copy.
HTTP Redirect or URL Rewrite?
Internet Information Services has two independent redirect mechanisms. Both are configured in IIS Manager or directly in the site's web.config:
| HTTP Redirect | URL Rewrite module | |
|---|---|---|
| Installation | Windows feature "HTTP Redirection" | Separate download (URL Rewrite 2.1) |
| Config element | <httpRedirect> | <rewrite> |
| Conditions (host, HTTPS, query string) | No | Yes |
| Regular expressions | No | Yes |
| Good for | Moving a whole site or folder, single pages | HTTPS, www, patterns, large redirect lists |
Rule of thumb: for "everything from here goes there", HTTP Redirect is enough. As soon as a condition is involved, and HTTP to HTTPS always is, you need URL Rewrite. Don't use both for the same URLs, or you will end up with chains.
The HTTP Redirect feature
Installation
On Windows Server, open Server Manager and add Web Server (IIS) → Web Server → Common HTTP Features → HTTP Redirection. With PowerShell:
Install-WindowsFeature Web-Http-Redirect
On Windows 10/11, enable it under "Turn Windows features on or off" → Internet Information Services → World Wide Web Services → Common HTTP Features.
Redirect a whole site
In IIS Manager, select the site, open HTTP Redirect, tick "Redirect requests to this destination", enter the target and choose the status code. In web.config this looks like:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpRedirect enabled="true"
destination="https://example.com$V$Q"
exactDestination="true"
httpResponseStatus="Permanent" />
</system.webServer>
</configuration>
With exactDestination="true", IIS uses the destination literally and replaces the variables: $V is the requested path, $Q the query string including the question mark. So /shop/item?id=5 ends up at https://example.com/shop/item?id=5. This is the setup for a domain migration when the old domain has its own IIS site.
The values of httpResponseStatus:
Permanent: 301Found: 302 (the default, so set it explicitly)Temporary: 307PermRedirect: 308, available in newer IIS versions
Redirect a single page
Wrap the setting in a <location> element. This goes directly under <configuration>:
<location path="old-page.aspx">
<system.webServer>
<httpRedirect enabled="true"
destination="https://example.com/new-page/"
exactDestination="true"
httpResponseStatus="Permanent" />
</system.webServer>
</location>
The inheritance trap
HTTP Redirect settings are inherited by every subfolder. If you redirect the root of a site to /new/ on the same site, /new/ inherits the redirect and you get an endless loop. Either tick "Only redirect requests to content in this directory" (childOnly="true"), add <httpRedirect enabled="false" /> in the target folder, or use URL Rewrite with a precise pattern.
The URL Rewrite module
URL Rewrite is an official, free extension from Microsoft that you have to download and install separately on every server. Without it, any <rewrite> section in web.config causes an HTTP 500.19 error, because IIS does not recognize the configuration section. On Azure App Service for Windows it is already installed. The full syntax is in the URL Rewrite configuration reference.
Rules live under <system.webServer><rewrite><rules>. Three things to know:
<match url="...">is checked against the path without leading slash and without query string, case-insensitively by default.redirectTypecan bePermanent(301),Found(302, the default),SeeOther(303) orTemporary(307).stopProcessing="true"ends rule processing after a match. Always set it on redirect rules.
Single page
<rule name="Old page" stopProcessing="true">
<match url="^old-page\.aspx$" />
<action type="Redirect" url="https://example.com/new-page/" redirectType="Permanent" />
</rule>
Pattern with back-reference
<rule name="Blog to articles" stopProcessing="true">
<match url="^blog/\d{4}/(.+)$" />
<action type="Redirect" url="https://example.com/articles/{R:1}" redirectType="Permanent" />
</rule>
{R:1} refers to the first capture group of the match, {C:1} to a capture group from a condition. The query string is appended automatically; set appendQueryString="false" on the action to drop it.
HTTP to HTTPS
The complete web.config for a site that should only be reached via HTTPS:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="HTTP to HTTPS" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTPS}" pattern="^OFF$" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
The site needs an HTTPS binding with a valid certificate, and the HTTP binding on port 80 must stay in place, otherwise the request never reaches the rule. Behind a load balancer or Application Request Routing that terminates TLS, {HTTPS} is always OFF. Check the forwarded header instead: <add input="{HTTP_X_FORWARDED_PROTO}" pattern="^https$" negate="true" />. More on the topic: HTTP to HTTPS redirect.
HTTPS and non-www in one hop
Two separate rules would send http://www.example.com/ through two redirects. Combine them with logicalGrouping="MatchAny":
<rule name="Canonical host and HTTPS" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAny">
<add input="{HTTPS}" pattern="^OFF$" />
<add input="{HTTP_HOST}" pattern="^www\." />
</conditions>
<action type="Redirect" url="https://example.com/{R:1}" redirectType="Permanent" />
</rule>
Put this rule first, before any page-specific rules, or give the page rules absolute https://example.com targets so each URL needs only one hop. See the www redirect guide for choosing between www and non-www.
Many redirects with a rewrite map
For long lists of old and new URLs, a rewrite map is easier to maintain than one rule per URL:
<rewrite>
<rewriteMaps>
<rewriteMap name="Redirects">
<add key="/old-page.aspx" value="/new-page/" />
<add key="/products/list.aspx?cat=5" value="/shop/shoes/" />
</rewriteMap>
</rewriteMaps>
<rules>
<rule name="Redirect map" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{Redirects:{REQUEST_URI}}" pattern="(.+)" />
</conditions>
<action type="Redirect" url="{C:1}" appendQueryString="false" redirectType="Permanent" />
</rule>
</rules>
</rewrite>
{REQUEST_URI} includes the query string, so keys can match specific parameters. Very large maps can be moved into a separate file with <rewriteMaps configSource="rewritemaps.config" />.
Testing
URL Rewrite also offers ready-made templates in IIS Manager ("Canonical domain name", "Append or remove the trailing slash symbol", "Enforce lowercase URLs") and a "Test pattern" dialog for your regex. That checks the pattern, not the response. For the real result, enter the old URL in the redirect checker: it shows status code, target and number of hops without any browser cache. Test HTTP and HTTPS, with and without www, and URLs with query strings. If a redirect doesn't fire at all, IIS's Failed Request Tracing shows which rules were evaluated. Long chains after a migration are covered in redirect chains.
Frequently asked questions
- Why do I get error 500.19 after adding redirect rules?
Your
web.configcontains a<rewrite>section, but the URL Rewrite module is not installed on the server, so IIS doesn't know the section. Install URL Rewrite or remove the section. A typo in the XML leads to the same error.- Does IIS HTTP Redirect keep the path and query string?
Only if you tell it to. The reliable way is
exactDestination="true"with$V$Qat the end of the destination, e.g.https://example.com$V$Q. Check the result with the redirect checker.- What is the default status code of an IIS redirect?
Both HTTP Redirect and URL Rewrite default to 302 (Found). For permanent moves, set
httpResponseStatus="Permanent"orredirectType="Permanent"explicitly to get a 301. Why that matters is explained in 301 vs. 302.- Can I use .htaccess on IIS?
No, IIS uses
web.config. URL Rewrite can import mod_rewrite rules in IIS Manager ("Import Rules"), but review the result, because not every Apache flag has an equivalent.
Related guides
-
.htaccess redirects on Apache
Everything you need to redirect pages and whole domains with .htaccess on Apache: mod_alias vs. mod_rewrite, copy-ready rules for the common cases, and how to avoid loops and chains.
-
nginx redirects
Copy-ready nginx configuration for 301 redirects: single pages, patterns, whole domains, HTTPS and www in a single hop, and hundreds of URLs with map.
-
Redirects on Cloudflare
Cloudflare can answer redirects at the edge before a request ever reaches your server. Here is how Single Redirects, Bulk Redirects and Always Use HTTPS work, and how to keep them from fighting with your origin.
-
Redirects in WordPress
WordPress gives you several ways to redirect a URL: a plugin, a rule in .htaccess or a few lines of PHP. This guide shows when to use which, and how to avoid the typical pitfalls with caching and HTTPS.
-
Redirects in Next.js
Next.js has four places where you can redirect: the config file, middleware, server code in the App Router and the trailingSlash option. Each one sends different status codes by default, so it pays to know which is which.
-
Redirects on Vercel and Netlify
On Vercel and Netlify you don't touch a web server config. Redirects live in a file in your repository or in the dashboard, and the platform's edge network sends them. Here is how both work and where they differ.