Nginx TLS on Windows: certificates, HTTP/2 and sane defaults
Configure PEM certificates, TLS 1.2/1.3, HTTP/2, SNI and HSTS on Windows Nginx, and test the handshake instead of trusting a browser padlock.
- Build
- 26100.33650
- Edition
- Standard / Datacenter
- Architecture
- x64
- Support
- Documentation (E1)
- Evidence
- E1Official documentation
- Verified
- 2026-10-29
- Page type
- Task
The short answer
Section titled “The short answer”Nginx on Windows reads PEM files, not the Windows certificate store:
server { listen 443 ssl; http2 on; server_name lab.local;
ssl_certificate C:/nginx/certs/lab.crt; ssl_certificate_key C:/nginx/certs/lab.key;
ssl_protocols TLSv1.2 TLSv1.3; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d;}Export the certificate + private key as PEM (full chain first), restrict the key’s ACL, and test with nginx -t.
HTTP to HTTPS
Section titled “HTTP to HTTPS”server { listen 80; server_name lab.local; return 301 https://$host$request_uri;}Do not add HSTS until HTTPS works on every name; HSTS is cached by browsers.
Headers
Section titled “Headers”add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;add_header X-Content-Type-Options "nosniff" always;add_header Referrer-Policy "strict-origin-when-cross-origin" always;CSP is application-specific; do not paste a random CSP into every site. A wrong CSP breaks the app and gets removed, which teaches nothing.
SNI and multiple names
Section titled “SNI and multiple names”One server block per certificate/name group. Nginx selects the block by SNI. A client that does not send SNI gets the default_server certificate.
server { listen 443 ssl default_server; server_name _; ssl_certificate C:/nginx/certs/default.crt; ssl_certificate_key C:/nginx/certs/default.key; return 444;}return 444 closes the connection for unmatched names; do not serve a random site’s certificate.
Verify
Section titled “Verify”cd C:\nginx.\nginx.exe -t.\nginx.exe -s reloadcurl.exe -I --resolve lab.local:443:127.0.0.1 https://lab.local/ -kopenssl s_client -connect 127.0.0.1:443 -servername lab.local -tls1_3Check:
- certificate subject/SAN matches the hostname;
- TLS 1.0/1.1 are not offered;
- HTTP/2 is negotiated (
curl --http2showsHTTP/2); - redirect goes to the public hostname, not an internal one.
Windows certificate notes
Section titled “Windows certificate notes”- Nginx does not read
Cert:\LocalMachine\My; export PFX, then convert to PEM/key. - Keep the private key off the web root; ACL it to the Nginx service account + Administrators.
- Renewal must reload Nginx (
nginx -s reload), not just replace the file. - Self-signed is lab-only; clients must trust it explicitly.
Lab boundary
Section titled “Lab boundary”E1. The lab served Nginx over HTTP on 8081; TLS, SNI and HTTP/2 were not reproduced on Windows. Validate on Server 2025 with a real certificate before claiming E2.