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.