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 binary
  • confnginx.conf — the main configuration file
  • html — default web root with sample index.html
  • logs — access and error logs
  • temp — 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 core
  • worker_connections 1024 — maximum simultaneous connections per worker
  • sendfile 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.