Docs / Windows Server / Configure IIS URL Rewrite Rules

Configure IIS URL Rewrite Rules

By Admin · Mar 15, 2026 · Updated Apr 24, 2026 · 271 views · 6 min read

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

Was this article helpful?