On Windows Server, IIS can do much of what Nginx does on Linux: rewrite and redirect URLs, and act as a reverse proxy in front of applications running on other ports (Node.js, Python, Java, or another server). You need two free Microsoft modules: URL Rewrite and Application Request Routing (ARR).

Install the modules

  1. Download and install URL Rewrite 2.1 from Microsoft.
  2. Download and install Application Request Routing 3.0 (only needed for reverse proxying).
  3. Restart IIS Manager. You’ll see “URL Rewrite” on each site, and “Application Request Routing Cache” at the server level.

Enable the proxy (ARR)

IIS Manager → server node → Application Request Routing Cache → Server Proxy Settings → check Enable proxy → Apply.

Or with PowerShell:

Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/proxy" -name "enabled" -value "True"

Reverse proxy a backend app

Example: a Node.js app on http://127.0.0.1:3000, served at https://app.example.com. Create an IIS site for app.example.com (with an HTTPS binding; see IIS SSL certificates), and put this web.config in its folder:

<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="ReverseProxy" stopProcessing="true">
          <match url="(.*)" />
          <action type="Rewrite" url="http://127.0.0.1:3000/{R:1}" />
          <serverVariables>
            <set name="HTTP_X_FORWARDED_PROTO" value="https" />
          </serverVariables>
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

To set custom server variables such as HTTP_X_FORWARDED_PROTO, allow them first: URL Rewrite → View Server Variables → Add. ARR adds X-Forwarded-For automatically (configurable in Server Proxy Settings).

Keep the backend bound to 127.0.0.1, so it’s only reachable through IIS. Run it as a Windows service (NSSM, or the app’s own service wrapper), so it starts on boot.

WebSockets: install the WebSocket Protocol feature (Install-WindowsFeature Web-WebSockets). ARR then proxies WebSocket connections.

Common rewrite and redirect rules

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>

Single-page app (React/Vue/Angular) fallback to index.html:

<rule name="SPA fallback" stopProcessing="true">
  <match url=".*" />
  <conditions logicalGrouping="MatchAll">
    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
    <add input="{REQUEST_URI}" pattern="^/api/" negate="true" />
  </conditions>
  <action type="Rewrite" url="/index.html" />
</rule>

Old URL to new URL (301):

<rule name="Old pricing page" stopProcessing="true">
  <match url="^pricing\.aspx$" />
  <action type="Redirect" url="/pricing/" redirectType="Permanent" />
</rule>

HTTP to HTTPS: see IIS SSL certificates.

Rewrite vs Redirect

  • Rewrite: IIS internally serves a different URL (or proxies to a backend). The browser’s address bar doesn’t change.
  • Redirect: IIS tells the browser to go to a new URL (301 permanent, or 302 temporary). The address changes, and search engines follow 301s.

Importing Apache .htaccess rules

URL Rewrite can import mod_rewrite rules: site → URL Rewrite → Import Rules → paste the .htaccess content. Review the converted rules, because not everything translates.

Where rules live

Rules can be defined per site (in the site’s web.config) or globally (server level, stored in applicationHost.config). Keep site-specific rules in web.config, next to the application, so they’re deployed and version-controlled with it. Use global rules only for things that apply to every site, such as blocking a bad bot’s user agent.

Troubleshooting

Problem Fix
502.3 Bad Gateway Backend not running, or wrong port; increase ARR timeout for slow apps
500.52 URL Rewrite error Server variable not allowed. Add it in “View Server Variables”
Proxy rules ignored ARR proxy not enabled at the server level
Redirect loop HTTPS detection wrong behind a load balancer or CDN; check the {HTTPS} condition

Enable Failed Request Tracing in IIS to see exactly which rule matched.

Quick reference

Need Module
Redirects, pretty URLs URL Rewrite
Reverse proxy URL Rewrite + ARR (proxy enabled)
WebSockets Web-WebSockets feature
.htaccess conversion URL Rewrite → Import Rules

Related: install IIS on Windows Server and Nginx reverse proxy on Linux.

  • IIS
  • Windows Server
  • Reverse Proxy