IAnswerable

Run Node.js Behind IIS on Windows Server

17 Apr 2026

IIS wasn’t built to run Node.js, and there are two ways to make it work anyway: iisnode, which lets IIS host the process directly, and a reverse proxy, which lets Node run on its own and IIS just forwards traffic to it. Pick the reverse proxy unless you have a specific reason not to.

Why reverse proxy wins

iisnode Reverse proxy (ARR)
Node process managed by IIS/w3wp.exe Your own process manager (PM2, a Windows service)
Debugging Harder — output goes through IIS’s pipeline Easy — it’s just a normal Node process, logs to stdout
Maintenance status Effectively unmaintained since ~2019 IIS’s ARR module is standard, still supported
Restart on crash Handled by IIS’s app pool recycling You control it directly via your process manager
Matches production Linux setups No — Windows-specific model Yes — same nginx-style proxy pattern you’d use elsewhere

iisnode was reasonable when it was actively maintained, but it hasn’t kept pace, and debugging a Node crash through IIS’s process model is worse than just running Node normally and fronting it.

Here’s the request path the reverse proxy setup below builds:

flowchart LR
    A["Browser"] --> B["IIS\n(port 80/443)"]
    B -->|"static asset"| C["Served by IIS"]
    B -->|"everything else"| D["ARR reverse proxy"]
    D --> E["Node\n(localhost:3000)"]
  

Set up the reverse proxy

1. Install IIS modules

Through Server Manager → Add Roles and Features, or:

Install-WindowsFeature -Name Web-Server, Web-Http-Redirect

Then install two IIS extensions (not PowerShell modules — download from Microsoft’s IIS site): Application Request Routing (ARR) and URL Rewrite.

2. Enable proxy in ARR

In IIS Manager: click the server node → Application Request Routing Cache → Server Proxy Settings → check Enable proxy.

3. Run your Node app on a local port

node server.js   # listening on localhost:3000

Keep it off port 80/443 — IIS owns those. Manage the Node process itself with PM2 or a Windows service (NSSM) so it survives a reboot and restarts on crash — IIS is only handling the HTTP routing, not the process lifecycle.

4. Add the rewrite rule

In your site’s web.config:

<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="ReverseProxyToNode" stopProcessing="true">
          <match url="(.*)" />
          <action type="Rewrite" url="http://localhost:3000/{R:1}" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

This forwards every request on the IIS site to your local Node process. IIS handles TLS termination, static files if you want it to, and can sit in front of multiple Node apps on different ports/bindings.

Serve static files through IIS, not Node

If your Node app is also serving a built frontend, let IIS handle those requests directly instead of proxying them through — it’s faster at static file serving than Node is, and it’s what IIS is actually good at:

<rule name="StaticFiles" stopProcessing="true">
  <match url="^(assets|static)/.*" />
  <action type="None" />
</rule>

Put this rule before the catch-all proxy rule — IIS rewrite rules run in order, first match wins.

Process management: don’t skip this

A reverse proxy setup means IIS has no idea if your Node process dies. Use PM2 in Windows Service mode or NSSM (Non-Sucking Service Manager) to wrap the Node process as an actual Windows service with auto-restart:

nssm install MyNodeApp "C:\Program Files\nodejs\node.exe" "C:\apps\myapp\server.js"
nssm set MyNodeApp AppDirectory "C:\apps\myapp"
nssm start MyNodeApp

Without this, a Node crash means IIS keeps returning 502s until someone notices and manually restarts the process.

When iisnode is still the right call

If you’re maintaining a legacy app that already runs on iisnode and it’s stable, there’s no urgency to migrate it. For anything new, the reverse proxy pattern is less Windows-specific, easier to debug, and matches how you’d deploy the same app on Linux — one less thing to relearn if the app ever moves off Windows Server.