IIS URL Rewrite is a powerful module that enables URL manipulation, redirects, reverse proxying, and SEO-friendly URL structures on Windows Server. It is equivalent to Apache's mod_rewrite and is essential for hosting modern web applications on IIS. This guide covers common URL Rewrite patterns and configurations.
Install URL Rewrite Module
# Download and install URL Rewrite Module
# Option 1: Web Platform Installer
Invoke-WebRequest -Uri "https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_en-US.msi" `
-OutFile "C:\temp\rewrite.msi"
Start-Process msiexec.exe -ArgumentList "/i C:\temp\rewrite.msi /quiet" -Wait
# Verify installation
Get-WebGlobalModule | Where-Object Name -like "*Rewrite*"
Basic URL Rewrites
<!-- web.config — place in site root -->
<configuration>
<system.webServer>
<rewrite>
<rules>
<!-- Force HTTPS -->
<rule name="Force HTTPS" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTPS}" pattern="^OFF$" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
</rule>
<!-- Redirect www to non-www -->
<rule name="Remove WWW" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" pattern="^www\.(.+)$" />
</conditions>
<action type="Redirect" url="https://{C:1}/{R:1}" redirectType="Permanent" />
</rule>
<!-- Clean URLs for PHP/MVC apps -->
<rule name="Application Routing" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="index.php/{R:1}" />
</rule>
<!-- WordPress permalinks -->
<rule name="WordPress" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="index.php" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Reverse Proxy Configuration
<!-- Reverse proxy to a backend Node.js/Python application -->
<!-- Requires: URL Rewrite + Application Request Routing (ARR) -->
<!-- Install ARR -->
<!-- Download from: https://www.iis.net/downloads/microsoft/application-request-routing -->
<!-- Enable proxy in ARR -->
<!-- IIS Manager > Application Request Routing > Server Proxy Settings > Enable proxy -->
<configuration>
<system.webServer>
<rewrite>
<rules>
<!-- Proxy /api/* to Node.js backend -->
<rule name="API Proxy" stopProcessing="true">
<match url="^api/(.*)" />
<action type="Rewrite" url="http://localhost:3000/api/{R:1}" />
<serverVariables>
<set name="HTTP_X_FORWARDED_FOR" value="{REMOTE_ADDR}" />
<set name="HTTP_X_FORWARDED_PROTO" value="{HTTPS}" />
</serverVariables>
</rule>
<!-- Proxy entire subdomain -->
<rule name="App Proxy" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTP_HOST}" pattern="^app\.example\.com$" />
</conditions>
<action type="Rewrite" url="http://localhost:5000/{R:1}" />
</rule>
<!-- WebSocket proxy -->
<rule name="WebSocket Proxy" stopProcessing="true">
<match url="^ws/(.*)" />
<conditions>
<add input="{HTTP_UPGRADE}" pattern="websocket" ignoreCase="true" />
</conditions>
<action type="Rewrite" url="http://localhost:8080/ws/{R:1}" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
SEO and Redirect Patterns
<rewrite>
<rules>
<!-- Add trailing slash -->
<rule name="Add Trailing Slash" stopProcessing="true">
<match url="(.*[^/])$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
<add input="{REQUEST_URI}" pattern=".*\.\w+$" negate="true" />
</conditions>
<action type="Redirect" url="{R:1}/" redirectType="Permanent" />
</rule>
<!-- Lowercase URLs -->
<rule name="Lowercase URLs" stopProcessing="true">
<match url="[A-Z]" ignoreCase="false" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<action type="Redirect" url="{ToLower:{URL}}" redirectType="Permanent" />
</rule>
<!-- 301 redirect old URLs -->
<rule name="Old Blog Redirect" stopProcessing="true">
<match url="^blog/post/(\d+)$" />
<action type="Redirect" url="/articles/{R:1}" redirectType="Permanent" />
</rule>
</rules>
</rewrite>
Security Rules
<rewrite>
<rules>
<!-- Block common attack patterns -->
<rule name="Block SQL Injection" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{QUERY_STRING}" pattern="(\%27)|(\')|(\-\-)|(\%23)" />
</conditions>
<action type="AbortRequest" />
</rule>
<!-- Block access to sensitive files -->
<rule name="Block Sensitive Files" stopProcessing="true">
<match url=".*\.(env|git|htaccess|htpasswd|ini|log|bak|sql)$" />
<action type="CustomResponse" statusCode="403" statusReason="Forbidden" />
</rule>
<!-- IP-based access restriction -->
<rule name="Restrict Admin" stopProcessing="true">
<match url="^admin/(.*)" />
<conditions>
<add input="{REMOTE_ADDR}" pattern="^203\.0\.113\." negate="true" />
</conditions>
<action type="CustomResponse" statusCode="403" />
</rule>
</rules>
</rewrite>
PowerShell Management
# Add rewrite rule via PowerShell
Add-WebConfigurationProperty -PSPath "IIS:\Sites\Default Web Site" `
-Filter "system.webServer/rewrite/rules" `
-Name "." `
-Value @{
name = "Force HTTPS"
stopProcessing = "true"
}
# List all rewrite rules
Get-WebConfigurationProperty -PSPath "IIS:\Sites\Default Web Site" `
-Filter "system.webServer/rewrite/rules/rule" `
-Name "." | Select-Object Name
# Test URL rewrite (Failed Request Tracing)
Enable-WebRequestTracing -Name "Default Web Site" -StatusCodes "301-302,404" -MaxLogFiles 50
Best Practices
- Use
stopProcessing="true"on rules that should be final to avoid unintended chaining - Order rules carefully: Rules are processed top-to-bottom — put most specific rules first
- Use 301 for permanent redirects and 302 for temporary — search engines treat them differently
- Test with Failed Request Tracing to debug rules that aren't matching
- Use conditions to limit when rules apply (HTTPS only, specific hosts, etc.)
- Back up web.config before making rewrite rule changes
- Be cautious with regex: Complex patterns can impact performance on high-traffic sites