# HttpPlatformHandler: run Node or Python behind IIS without FastCGI

> Use the IIS HttpPlatformHandler extension to launch a Node or Python process, pass the dynamic port, capture stdout, and avoid process-per-request mistakes.

**Tested on**

- Build: 26100.33650
- Edition: Standard / Datacenter
- Architecture: x64
- Evidence: E1
- Verified: 2026-10-29

## The short answer

**HttpPlatformHandler (HPH)** is an IIS extension that starts and supervises a long-running process (Node, Python, Go, etc.) and forwards requests to it. It is not FastCGI and not a process-per-request CGI.

Use it when:

- the app is a normal HTTP server listening on a port;
- you want IIS to handle TLS/bindings/logging;
- the app can read a dynamic port from `%HTTP_PLATFORM_PORT%`.

For PHP, use FastCGI instead. For .NET, use the ASP.NET Core Module (ANCM), not HPH.

## web.config

```xml
<configuration>
  <system.webServer>
    <httpPlatform processPath="C:\Program Files\nodejs\node.exe"
                  arguments="server.js"
                  startupTimeLimit="60"
                  stdoutLogEnabled="true"
                  stdoutLogFile=".\logs\node">
      <environmentVariables>
        <environmentVariable name="PORT" value="%HTTP_PLATFORM_PORT%" />
        <environmentVariable name="NODE_ENV" value="production" />
      </environmentVariables>
    </httpPlatform>
  </system.webServer>
</configuration>
```

The app must listen on the port from `%HTTP_PLATFORM_PORT%`, not a hard-coded 3000. HPH assigns a port per app pool/process and proxies to it.

Python (Waitress) example:

```xml
<httpPlatform processPath="C:\Python311\python.exe"
              arguments="-m waitress --listen=127.0.0.1:%HTTP_PLATFORM_PORT% app:app"
              stdoutLogEnabled="true"
              stdoutLogFile=".\logs\python">
</httpPlatform>
```

The exact argument syntax depends on the app server; do not copy a Gunicorn command to Windows.

## Process lifecycle

- HPH starts the process when the app pool starts and restarts it if it exits.
- `startupTimeLimit` controls how long IIS waits for the first listen.
- `rapidFailsPerMinute` (where available) prevents a crash loop from spinning forever.
- The process runs as the **app pool identity**, so file/log permissions follow that identity.

Do not run the app as a Windows service *and* HPH on the same port. Pick one supervisor.

## Verify

```powershell
Get-WebGlobalModule -Name HttpPlatformHandler
Invoke-WebRequest http://localhost:8080/ -UseBasicParsing | Select-Object StatusCode
Get-Content .\logs\node\*.log -Tail 50
```

If IIS returns 502/503:

- the process did not start (path/permissions/runtime);
- it started but did not listen on `%HTTP_PLATFORM_PORT%`;
- it crashed after start; read the stdout log;
- the app pool identity cannot read the app directory.

> **Caution: Do not expose stdout logs**
>
> HPH stdout logs can contain tokens, connection strings and request data. Keep them outside the web root, restrict ACLs, and rotate them.

## When not to use HPH

- PHP: FastCGI.
- ASP.NET Core: ANCM/Hosting Bundle.
- Static files: IIS static content.
- A process that must be shared by multiple sites: run one service and reverse-proxy with ARR instead of one HPH per site.

## Lab boundary

This is E1. The lab installed IIS and PHP FastCGI, but not HPH or Node/Python. Validate the process path, port and identity on Server 2025 before calling it E2.

## Sources

- [Microsoft Learn — HttpPlatformHandler configuration](https://learn.microsoft.com/en-us/iis/extensions/httpplatformhandler/httpplatformhandler-configuration-reference)
- [Microsoft Learn — ASP.NET Core Module](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/aspnet-core-module)

---

Source: https://winbook.fyi/en/server/web/iis-node-python/
