How to Install NGINX on Windows Server 2025
While IIS is the native web server on Windows Server, NGINX has become a popular alternative and complement for Windows-based deployments — particularly as a high-performance reverse proxy, load balancer, or static file server sitting in front of an IIS or Node.js backend. NGINX for Windows is a fully functional port that supports the majority of the Linux feature set, and when combined with NSSM (Non-Sucking Service Manager) it runs reliably as a Windows service with automatic restarts. This tutorial covers downloading and extracting NGINX, configuring nginx.conf, installing it as a Windows service with NSSM, setting up virtual hosts, configuring a reverse proxy to IIS or application backends, and managing logs on Windows.
Prerequisites
- Windows Server 2025 with Administrator access
- PowerShell 5.1 or later (used for download and service management)
- Internet access on the server for downloading NGINX and NSSM (or pre-download to a network share)
- TCP port 80 and/or 443 available (not in use by IIS on the same IP — use separate bindings or a different port if IIS is already running)
- An SSL certificate if you plan to serve HTTPS (PEM format — NGINX uses .crt and .key files, not Windows certificate store by default)
- Windows Firewall access on the ports you configure
Step 1: Download and Extract NGINX
Download the latest stable mainline build of NGINX for Windows from the official site. As of 2025, download the mainline build for Windows (nginx/Windows):
# Download NGINX for Windows (adjust version to latest stable)
$nginxVersion = "1.27.3"
$downloadUrl = "https://nginx.org/download/nginx-$nginxVersion.zip"
$zipPath = "C:Tempnginx-$nginxVersion.zip"
$installPath = "C:nginx"
# Download
New-Item -Path "C:Temp" -ItemType Directory -Force | Out-Null
Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath -UseBasicParsing
# Extract
Expand-Archive -Path $zipPath -DestinationPath "C:" -Force
# Rename the extracted folder for convenience
if (Test-Path "C:nginx-$nginxVersion") {
Rename-Item -Path "C:nginx-$nginxVersion" -NewName "nginx"
}
# Verify the directory structure
Get-ChildItem -Path $installPath
The NGINX directory structure under C:nginx should include:
nginx.exe— the NGINX binaryconfnginx.conf— the main configuration filehtml— default web root with sampleindex.htmllogs— access and error logstemp— temporary files for proxy buffering and body storage
Step 2: Configure nginx.conf
The main configuration file is C:nginxconfnginx.conf. Replace its contents with a production-ready configuration:
worker_processes auto;
error_log logs/error.log warn;
pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main;
sendfile on;
keepalive_timeout 65;
# Gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
# Default server — serves static files from C:/nginx/html
server {
listen 80;
server_name localhost;
root html;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
Key directives explained:
worker_processes auto— NGINX uses one worker per logical CPU coreworker_connections 1024— maximum simultaneous connections per workersendfile on— efficient file transfer using OS-level sendfile (on Windows, this maps to TransmitFile)try_files— serves a file if it exists, otherwise returns 404
Step 3: Test Configuration and Start NGINX Manually
Before setting up a Windows service, verify the configuration is valid and start NGINX manually:
# Test the configuration for syntax errors
Set-Location -Path "C:nginx"
.nginx.exe -t
# Start NGINX (runs in the foreground — use for initial testing only)
Start-Process -FilePath "C:nginxnginx.exe" -WorkingDirectory "C:nginx" -WindowStyle Hidden
# Verify NGINX is running
Get-Process nginx
# Test with curl
curl -v http://localhost/
# To stop the running NGINX process gracefully
.nginx.exe -s stop
# To reload configuration without downtime
.nginx.exe -s reload
Step 4: Install NGINX as a Windows Service with NSSM
NGINX has no native Windows service support. NSSM (Non-Sucking Service Manager) wraps any executable as a Windows service with automatic restart, stdout/stderr logging, and standard service lifecycle management:
# Download NSSM
$nssmUrl = "https://nssm.cc/release/nssm-2.24.zip"
$nssmZip = "C:Tempnssm.zip"
Invoke-WebRequest -Uri $nssmUrl -OutFile $nssmZip -UseBasicParsing
Expand-Archive -Path $nssmZip -DestinationPath "C:Tempnssm" -Force
# Copy the 64-bit nssm.exe to a permanent location
Copy-Item -Path "C:Tempnssmnssm-2.24win64nssm.exe" -Destination "C:WindowsSystem32nssm.exe"
# Install NGINX as a Windows service named "nginx"
nssm install nginx "C:nginxnginx.exe"
# Set the working directory (critical — NGINX looks for conf/ relative to its cwd)
nssm set nginx AppDirectory "C:nginx"
# Set service description
nssm set nginx Description "NGINX HTTP Server"
# Configure stdout and stderr to log files
nssm set nginx AppStdout "C:nginxlogsservice-stdout.log"
nssm set nginx AppStderr "C:nginxlogsservice-stderr.log"
nssm set nginx AppRotateFiles 1
nssm set nginx AppRotateOnline 1
nssm set nginx AppRotateBytes 10485760
# Set service to start automatically
nssm set nginx Start SERVICE_AUTO_START
# Start the service
Start-Service -Name nginx
Get-Service -Name nginx
Step 5: Configure Virtual Hosts (Multiple Server Blocks)
NGINX uses server blocks to host multiple sites. Organize virtual host configurations in a separate directory and include them from the main config:
# Create a directory for virtual host configs
New-Item -Path "C:nginxconfsites-enabled" -ItemType Directory -Force
Edit C:nginxconfnginx.conf and add this line inside the http {} block before the closing brace:
include sites-enabled/*.conf;
Create a virtual host file at C:nginxconfsites-enabledcontoso.conf:
server {
listen 80;
server_name contoso.com www.contoso.com;
root C:/websites/contoso/public;
index index.html index.php;
access_log logs/contoso-access.log main;
error_log logs/contoso-error.log warn;
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets
location ~* .(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
}
Step 6: Configure NGINX as a Reverse Proxy to IIS or App Backend
One of the most common NGINX-on-Windows patterns is fronting IIS (running on a non-standard port like 8080) or a Node.js/Python app backend:
server {
listen 80;
server_name app.contoso.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeouts
proxy_connect_timeout 60s;
proxy_read_timeout 120s;
proxy_send_timeout 60s;
# Buffer settings
proxy_buffering on;
proxy_buffer_size 16k;
proxy_buffers 4 32k;
}
}
After saving, reload NGINX to apply the new configuration:
# Reload NGINX configuration via nssm-managed service
# Send reload signal via nginx.exe -s reload (works even when running as service)
C:nginxnginx.exe -s reload
# Verify with curl
curl -H "Host: app.contoso.com" http://127.0.0.1/ -v
Step 7: Open Windows Firewall Rules
# Allow HTTP and HTTPS inbound
New-NetFirewallRule -Name "NGINX-HTTP" -DisplayName "NGINX HTTP (80)" -Protocol TCP -LocalPort 80 -Direction Inbound -Action Allow
New-NetFirewallRule -Name "NGINX-HTTPS" -DisplayName "NGINX HTTPS (443)" -Protocol TCP -LocalPort 443 -Direction Inbound -Action Allow
Step 8: Log Rotation on Windows
NGINX on Linux uses the USR1 signal for log rotation. On Windows, use a scheduled task that sends the reopen signal:
# Create a PowerShell log rotation script at C:nginxrotate-logs.ps1
$rotateScript = @'
$date = Get-Date -Format "yyyyMMdd-HHmmss"
$logDir = "C:nginxlogsarchive"
New-Item -Path $logDir -ItemType Directory -Force | Out-Null
# Move current logs to archive
Get-ChildItem "C:nginxlogs*.log" | ForEach-Object {
Move-Item $_.FullName "$logDir$($_.BaseName)-$date$($_.Extension)"
}
# Send reopen signal so NGINX creates fresh log files
& "C:nginxnginx.exe" -s reopen
'@
Set-Content -Path "C:nginxrotate-logs.ps1" -Value $rotateScript
# Register a daily scheduled task for log rotation at midnight
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NonInteractive -File C:nginxrotate-logs.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At "00:00"
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 5)
Register-ScheduledTask -TaskName "NGINX Log Rotation" -Action $action -Trigger $trigger -Settings $settings -RunLevel Highest -Force
NGINX on Windows Server 2025 delivers a lightweight, high-performance HTTP server and reverse proxy that integrates naturally into Windows infrastructure through NSSM-managed services and Windows Firewall rules. Whether you use it as a standalone static file server, a TLS termination layer in front of IIS, or a load balancer distributing traffic across multiple application instances, the configuration model is identical to NGINX on Linux — making cross-platform teams immediately productive. With scheduled log rotation and NSSM’s built-in process monitoring, the deployment is operationally robust and requires minimal ongoing maintenance.