How to Install NGINX on Windows Server 2022
NGINX has a native Windows port that runs as a standard Windows process. While NGINX on Windows has notable limitations compared to its Linux counterpart, it is a capable web server and reverse proxy for development environments, internal tooling, and scenarios where a lightweight HTTP server is needed without installing IIS. This guide covers downloading NGINX, configuring it as a Windows service using NSSM, configuring virtual hosts, setting up a reverse proxy, enabling HTTPS, and understanding the Windows-specific limitations.
Downloading NGINX for Windows
NGINX provides prebuilt Windows binaries from the official NGINX download page. The mainline version has the latest features; the stable version is recommended for production. Download the zip archive — there is no installer. The current stable release as of this writing is 1.26.x.
Using PowerShell to download and extract NGINX:
$version = "1.26.2"
$url = "https://nginx.org/download/nginx-$version.zip"
$dest = "C:nginx"
Invoke-WebRequest -Uri $url -OutFile "C:nginx-$version.zip"
Expand-Archive -Path "C:nginx-$version.zip" -DestinationPath "C:"
Rename-Item -Path "C:nginx-$version" -NewName "nginx"
The resulting directory structure at C:nginx will contain:
C:nginx
nginx.exe -- the main executable
conf
nginx.conf -- main configuration file
mime.types -- MIME type mappings
fastcgi_params
scgi_params
uwsgi_params
html
index.html -- default welcome page
50x.html -- default error page
logs
access.log
error.log
temp -- temporary files (proxy cache, client body, etc.)
Running NGINX as a Windows Service with NSSM
NGINX does not include a native Windows service wrapper. The standard approach is to use NSSM (Non-Sucking Service Manager), a free utility that wraps any executable as a Windows service and handles crash recovery, stdout/stderr logging, and service dependencies.
Download NSSM from nssm.cc and extract it. Then install NGINX as a service:
# Add NSSM to PATH or use its full path
$nssm = "C:toolsnssmwin64nssm.exe"
# Install the service
& $nssm install nginx "C:nginxnginx.exe"
# Set the working directory so nginx.exe can find conf/ and logs/
& $nssm set nginx AppDirectory "C:nginx"
# Set startup type to automatic
& $nssm set nginx Start SERVICE_AUTO_START
# Redirect stdout and stderr to log files
& $nssm set nginx AppStdout "C:nginxlogsservice-stdout.log"
& $nssm set nginx AppStderr "C:nginxlogsservice-stderr.log"
# Start the service
Start-Service nginx
Verify the service is running:
Get-Service nginx
To remove the service:
& $nssm remove nginx confirm
nginx.conf Basics
The main configuration file is C:nginxconfnginx.conf. NGINX configuration uses a block-based syntax with directives and contexts. The top-level contexts are http, events, and mail. Most web server and proxy configuration lives inside the http context.
A minimal working nginx.conf for Windows looks like this:
worker_processes 1;
error_log logs/error.log;
pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
access_log logs/access.log;
sendfile off;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
Note that sendfile is set to off on Windows. The sendfile() system call is implemented in the Linux kernel but the Windows equivalent (TransmitFile) has limitations with NGINX’s worker model. Leaving sendfile on will cause corrupted file transfers on Windows.
Configuring Virtual Hosts (Server Blocks)
In NGINX terminology, virtual hosts are called server blocks. Each server block defines a virtual host — a combination of listen port and server_name. NGINX matches incoming requests to server blocks using the Host header.
A cleaner way to manage multiple sites is to use separate configuration files included from nginx.conf. Create a sites directory and include it:
# In nginx.conf, inside the http {} block:
include C:/nginx/conf/sites/*.conf;
Then create individual site configuration files. Note that NGINX on Windows requires forward slashes in paths even though Windows uses backslashes:
# C:nginxconfsitessite1.conf
server {
listen 80;
server_name site1.example.com;
root C:/websites/site1;
index index.html index.htm;
access_log C:/nginx/logs/site1-access.log;
error_log C:/nginx/logs/site1-error.log;
location / {
try_files $uri $uri/ =404;
}
}
# C:nginxconfsitessite2.conf
server {
listen 80;
server_name site2.example.com;
root C:/websites/site2;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
After modifying configuration files, test the configuration syntax before reloading:
C:nginxnginx.exe -t
NGINX will report configuration file test is successful or show the specific line with an error.
Starting, Stopping, and Reloading NGINX
NGINX accepts signals via the -s flag to its executable. Common signals are:
# Graceful reload (apply new config without dropping connections)
C:nginxnginx.exe -s reload
# Fast shutdown (drop active connections immediately)
C:nginxnginx.exe -s stop
# Graceful shutdown (wait for active connections to finish)
C:nginxnginx.exe -s quit
# Reopen log files (for log rotation)
C:nginxnginx.exe -s reopen
When NGINX is running as a Windows service via NSSM, you can also use standard service commands. However, the -s reload mechanism is preferred for configuration changes because it applies the new configuration without downtime:
Stop-Service nginx
Start-Service nginx
Restart-Service nginx
Check the NGINX error log immediately after a reload to confirm no errors:
Get-Content "C:nginxlogserror.log" -Tail 20
Configuring NGINX as a Reverse Proxy on Windows
NGINX is commonly used as a reverse proxy in front of application servers running on localhost. For example, proxying to a Node.js application on port 3000 or an ASP.NET Core application on port 5000.
A reverse proxy server block that forwards all requests to a backend on port 3000:
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_send_timeout 300;
}
}
For load balancing across multiple backend instances on the same server:
upstream backend_pool {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://backend_pool;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Enabling HTTPS with NGINX on Windows
NGINX handles TLS termination using the ssl module, which is included in the Windows binary. You need a certificate and private key in PEM format. If you have a PFX file, convert it first:
openssl pkcs12 -in "C:certscertificate.pfx" -nokeys -out "C:nginxconfsslcert.pem" -passin pass:YourPassword
openssl pkcs12 -in "C:certscertificate.pfx" -nocerts -nodes -out "C:nginxconfsslkey.pem" -passin pass:YourPassword
OpenSSL is available via Git for Windows, Windows Subsystem for Linux, or as a standalone installer. An HTTPS server block in NGINX:
server {
listen 443 ssl;
server_name secure.example.com;
ssl_certificate C:/nginx/conf/ssl/cert.pem;
ssl_certificate_key C:/nginx/conf/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
root C:/websites/secure;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name secure.example.com;
return 301 https://$host$request_uri;
}
Open port 443 in Windows Firewall:
New-NetFirewallRule -DisplayName "NGINX HTTPS (443)" -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow
New-NetFirewallRule -DisplayName "NGINX HTTP (80)" -Direction Inbound -Protocol TCP -LocalPort 80 -Action Allow
Testing with curl
curl is an essential tool for testing NGINX configuration. Test a basic HTTP response:
curl -v http://localhost/
Test HTTPS with a self-signed certificate (skip verification) and show response headers:
curl -v --insecure https://localhost/
Test that the reverse proxy is forwarding correctly and that X-Forwarded-For is set:
curl -v http://app.example.com/ -H "Host: app.example.com"
Test the HTTP to HTTPS redirect:
curl -I http://secure.example.com/
You should see a 301 response with a Location header pointing to https://secure.example.com/.
Windows-Specific Limitations vs Linux NGINX
NGINX on Windows has several important limitations that users coming from Linux should be aware of:
Worker processes: On Linux, NGINX can use multiple worker processes to take advantage of all CPU cores. On Windows, NGINX runs with a single worker process (worker_processes 1) because the Windows implementation uses select() for I/O multiplexing rather than epoll or kqueue. Setting worker_processes auto on Windows may create multiple processes but they all use the single-threaded select() model and will not scale as expected.
Sendfile: As mentioned, sendfile must be disabled on Windows (sendfile off). This means file serving performance will be lower than Linux NGINX because every byte is copied through userspace rather than being transferred directly from the file descriptor to the socket.
Connections per worker: The select() model limits each worker to a maximum of 1024 simultaneous connections (FD_SETSIZE on Windows). Under heavy load, connections will queue or fail. Linux NGINX with epoll supports hundreds of thousands of concurrent connections per worker.
Signals: On Linux, NGINX workers are managed with Unix signals. On Windows, signals are approximated using Windows named pipes and the -s flag to nginx.exe. The behavior is functional but the implementation is different.
Path handling: Windows paths with backslashes must be written with forward slashes in nginx.conf. Drive letters are supported: root C:/websites/site1; is valid NGINX configuration on Windows.
Despite these limitations, NGINX on Windows Server 2022 is a practical choice for development environments, reverse proxy in front of .NET or Node.js applications, and low-to-medium traffic sites where the connection limit of 1024 per worker is not a bottleneck. For high-traffic production workloads on Windows, IIS with the Application Request Routing (ARR) module is the native alternative, or consider running NGINX inside WSL2 (Windows Subsystem for Linux) where it runs the full Linux build without the Windows-specific limitations.