Skip to content

Python WSGI on Windows: Waitress behind IIS or Nginx

Verified 1 min read

Run a WSGI app with Waitress in a venv, supervise it as a service, and reverse-proxy it without pretending Gunicorn works on Windows.

Tested onStandard / Datacenter · Build 26100.33650 · x64
E1
Build
26100.33650
Edition
Standard / Datacenter
Architecture
x64
Support
Documentation (E1)
Evidence
E1Official documentation
Verified
2026-10-29
Page type
Task

On Windows, use Waitress as the WSGI server:

Terminal window
C:\apps\api-a\.venv\Scripts\python.exe -m pip install waitress
C:\apps\api-a\.venv\Scripts\python.exe -m waitress --listen=127.0.0.1:8000 app:app

Then put IIS (HttpPlatformHandler/ARR) or Nginx in front. Gunicorn is Unix-only; do not copy a Gunicorn command to Windows.

  • pure-Python, works on Windows;
  • multi-threaded, no fork;
  • WSGI-compatible (Flask/Django);
  • simple --listen and thread count.

It is not a process manager. Use a service wrapper (NSSM/WinSW) to keep it running.

Terminal window
nssm install api-a "C:\apps\api-a\.venv\Scripts\python.exe" "-m waitress --listen=127.0.0.1:8000 app:app"
nssm set api-a AppDirectory "C:\apps\api-a"
nssm set api-a AppEnvironmentExtra APP_ENV=production
nssm set api-a AppStdout "C:\logs\api-a\out.log"
nssm set api-a AppStderr "C:\logs\api-a\err.log"
nssm start api-a

Pin the venv python, not python from PATH. Set AppDirectory so relative templates/config resolve.

Two options:

  • HttpPlatformHandler launches the Python process; IIS manages lifecycle (see HPH page).
  • ARR reverse-proxies to 127.0.0.1:8000; the Python process runs as a service.

Do not do both on the same port. Pick one supervisor.

location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

The app must trust the proxy headers; otherwise redirects/absolute URLs use the internal host.

Terminal window
C:\apps\api-a\.venv\Scripts\python.exe -c "import sys; print(sys.executable)"
curl.exe -I http://127.0.0.1:8000/
Get-CimInstance Win32_Process -Filter "Name='python.exe'" |
Select-Object ProcessId, ExecutablePath, CommandLine

Check sys.executable is the venv, and only one process listens on 8000.

Start with --threads=4 to 8 for a small app; measure before raising. Set the front proxy’s read timeout above the slowest legitimate request. A long report should be async, not a 30-minute HTTP request.

E1. The lab did not install Waitress or run a WSGI app; the commands are the Windows deployment pattern to validate on Server 2025.

Sources