Streamlit on IIS — Step by Step Guide
A complete guide to hosting a Streamlit Python app on Windows Server using IIS as a reverse proxy with full WebSocket support.
What You Need Before Starting
-
Windows Server with IIS installed
-
Python installed (3.9+ recommended)
-
Streamlit app ready (
app.py) -
Domain name (optional but recommended)
-
NSSM downloaded from https://nssm.cc
Step 1 — Install IIS Modules
Download and install both modules from Microsoft:
-
URL Rewrite → URL Rewrite : The Official Microsoft IIS Site
-
ARR (Application Request Routing) → Application Request Routing : The Official Microsoft IIS Site
After installing, restart IIS:
iisreset
Step 2 — Enable ARR Proxy
-
Open IIS Manager
-
Click the server name (root node)
-
Double-click Application Request Routing Cache
-
Click Server Proxy Settings on the right panel
-
Check
Enable proxy -
Click Apply
-
Enable WebSocket support(WebSocket | Microsoft Learn)
Step 3 — Run Streamlit as a Windows Service
Using NSSM to run Streamlit as a background Windows Service ensures it starts automatically and stays alive after reboots.
Open PowerShell as Administrator:
cd C:\nssm\win64
.\nssm.exe install StreamlitApp "C:\Python312\python.exe" "-m streamlit run C:\sites\myapp\app.py"
.\nssm.exe set StreamlitApp AppDirectory "C:\sites\myapp"
net start StreamlitApp
Verify it is running:
# Check service status
sc query StreamlitApp
# Check port is listening
netstat -an | findstr 8501
You should see port 8501 in LISTENING state.
Make sure you point NSSM to your Streamlit file (
app.py), not a FastAPI or other backend entry point.
Step 4 — Configure Streamlit
Create the file .streamlit/config.toml inside your app folder:
C:\sites\myapp\.streamlit\config.toml
[server]
headless = true
port = 8501
enableCORS = false
enableXsrfProtection = false
[browser]
gatherUsageStats = false
| Setting | Value | Reason |
|---|---|---|
headless |
true |
Prevents Streamlit opening a browser on the server |
port |
8501 |
Explicit port so IIS and NSSM configs match |
enableCORS |
false |
IIS handles CORS — Streamlit’s check blocks proxy requests |
enableXsrfProtection |
false |
XSRF tokens break when accessed through a proxy domain |
Restart the service after saving:
net stop StreamlitApp
net start StreamlitApp
Step 5 — Create IIS Site
-
Open IIS Manager
-
Right-click Sites → Add Website
-
Fill in:
-
Site name:
myapp -
Physical path:
C:\sites\myapp -
Binding: HTTPS, port 443
-
Hostname:
myapp.yourdomain.com -
SSL certificate: select your certificate
-
-
Click OK
Step 6 — Create web.config
Create web.config inside your site’s physical path folder C:\sites\myapp\:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<!-- Enable WebSocket at IIS level -->
<webSocket enabled="true" />
<rewrite>
<rules>
<!-- Rule 1: WebSocket upgrade requests — MUST come first -->
<rule name="StreamlitWebSocket" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll">
<add input="{HTTP_CONNECTION}" pattern="Upgrade" ignoreCase="true" />
<add input="{HTTP_UPGRADE}" pattern="websocket" ignoreCase="true" />
</conditions>
<action type="Rewrite" url="ws://127.0.0.1:8501/{R:1}" />
</rule>
<!-- Rule 2: All regular HTTP requests -->
<rule name="StreamlitHTTP" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="http://127.0.0.1:8501/{R:1}" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Rule order matters. The WebSocket rule MUST come before the HTTP rule. IIS processes rules top to bottom and stops at the first match.
Step 7 — DNS Setup
In your DNS provider (Namecheap, Cloudflare, GoDaddy, etc.):
| Type | Host | Value |
|---|---|---|
| A Record | myapp |
YOUR_SERVER_IP |
Critical: If your DNS provider shows a CDN, Proxy, or HTTPS toggle next to the record — turn it OFF.
CDN/proxy layers intercept WebSocket upgrade headers and silently block them. Your Streamlit app will load but freeze immediately. Always point DNS directly to your server IP.
Verify DNS is resolving correctly:
# Via Google DNS
nslookup myapp.yourdomain.com 8.8.8.8
# Via Cloudflare DNS (faster propagation)
nslookup myapp.yourdomain.com 1.1.1.1
Both should return your server’s IP. DNS propagation can take 5–30 minutes after making changes.
Step 8 — Verify Everything Works
Run these checks one by one:
# 1. Is the Streamlit service running?
sc query StreamlitApp
# 2. Is port 8501 listening?
netstat -an | findstr 8501
# 3. Does Streamlit respond locally?
Invoke-WebRequest -Uri "http://127.0.0.1:8501" -UseBasicParsing
# 4. Is DNS pointing to your server?
nslookup myapp.yourdomain.com 8.8.8.8
Then open your browser and navigate to https://myapp.yourdomain.com.
Press F12 → go to the Console tab → you should see no WebSocket errors.
A working connection looks like:
WebSocket connection to 'wss://myapp.yourdomain.com/_stcore/stream' established
Common Problems & Fixes
| Problem | Cause | Fix |
|---|---|---|
| Page loads but immediately freezes | WebSocket rule missing in web.config |
Add the WebSocket rule above the HTTP rule |
wss:// connection keeps failing |
CDN/proxy toggle is ON in DNS settings | Turn OFF the CDN toggle — use plain A Record |
| Streamlit service won’t start | Wrong file in NSSM (e.g. main.py) |
Make sure NSSM points to app.py |
enableCORS or XSRF errors in logs |
Default Streamlit settings | Set both to false in config.toml |
After iisreset WebSocket breaks |
IIS drops the proxy connection pool | Use Restart-WebAppPool instead of iisreset |
| DNS showing wrong IP after update | DNS propagation delay | Wait 5–30 min, run ipconfig /flushdns as Admin |
Quick Reference Commands
# --- Streamlit Service ---
sc query StreamlitApp # Check status
net start StreamlitApp # Start
net stop StreamlitApp # Stop
net stop StreamlitApp && net start StreamlitApp # Restart
# --- IIS Management ---
iisreset # Full restart (avoid for routine use)
Restart-WebAppPool "myapp" # Restart one app pool only
Stop-Website "myapp" # Stop one site
Start-Website "myapp" # Start one site
# --- DNS Checks ---
nslookup myapp.yourdomain.com 8.8.8.8 # Check via Google DNS
nslookup myapp.yourdomain.com 1.1.1.1 # Check via Cloudflare DNS
# --- Network ---
ipconfig /flushdns # Clear DNS cache (run as Admin)
netstat -an | findstr 8501 # Check Streamlit port
# --- Test Locally ---
Invoke-WebRequest -Uri "http://127.0.0.1:8501" -UseBasicParsing
Summary Checklist
-
[ ] URL Rewrite module installed
-
[ ] ARR module installed
-
[ ] ARR proxy + WebSocket enabled in IIS Manager
-
[ ] Streamlit running as NSSM Windows Service on port 8501
-
[ ]
.streamlit/config.tomlcreated with CORS and XSRF disabled -
[ ] IIS site created with HTTPS binding and SSL certificate
-
[ ]
web.configplaced in site root with WebSocket rule first -
[ ] DNS A record pointing directly to server IP (CDN toggle OFF)
-
[ ]
nslookupconfirms correct IP -
[ ] Browser console shows no WebSocket errors
All problems listed in this guide were encountered and resolved in a real production deployment on Windows Server with IIS 10.