Run Node.js Behind IIS on Windows Server
17 Apr 2026IIS 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.