在 Windows 上部署 NGINX 作为 IIS 前置负载均衡(HTTPS/HTTP/2、WebSocket、服务化全攻略)
在 Windows 上部署 NGINX 作为 IIS 前置负载均衡的实战指南:配置 HTTPS/HTTP/2 与 WebSocket,least_conn+权重调度,fullchain 证书与安全加固,基于粘性Cookie得会话保持,服务化运行与排错清单,附完整可复制示例。
1.架构与适用场景
将 TLS 终止、负载均衡与流量治理集中到 NGINX,IIS 只专注于应用本身。
- 证书集中:TLS 只在 NGINX 终止,IIS 走内网纯 HTTP,维护成本更低;
- 流量治理:支持负载均衡、灰度、蓝绿、压测分流;
- 跨技术栈:无论后端是 IIS/.NET 还是其它服务,都能统一被接入(WebSocket 也 OK)。
说明:本文中的域名与 IP 统一使用示例值(如
app.example.com、10.0.0.x),请替换为你的真实信息。
2.准备与下载(Windows 稳定版)
- 前往 https://nginx.org/en/download.html 下载 Stable 版本(稳定分支)。
- 解压到例如
D:/nginx - 在服务器放通 80/443(入口)以及到后端 IIS 的 8080/8081 等内网端口(按你的配置)。
D:/nginx/
├─ certs/ # 证书(fullchain 与私钥)
├─ conf/
│ ├─ conf.d/ # 每个站点的 server 配置
│ ├─ snippets/ # 可复用片段(代理默认、TLS 参数等)
│ ├─ upstreams.d/ # 上游(负载均衡池)定义
│ └─ nginx.conf # 主配置
└─ logs/ # 访问与错误日志
Windows 上
nginx.conf默认在conf/目录中,include snippets/*.conf等相对路径会以conf/为基准。
3.主配置 nginx.conf(含 WebSocket 与统一日志)
将以下内容保存为 conf/nginx.conf(核心与你提供的一致,已整理可直接用):
worker_processes auto;
events {
worker_connections 4096;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server_tokens off;
# 统一日志(可按需分域名单独写)
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;
error_log logs/error.log warn;
# WebSocket 连接升级的辅助变量
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# 通用片段(代理默认头、强TLS参数等)
include snippets/proxy_defaults.conf;
include snippets/ssl_params.conf;
# 所有上游(负载均衡池)
include upstreams.d/*.conf;
# 每个站点/应用的 server 配置
include conf.d/*.conf;
}
4.snippets 片段
在 conf/snippets/ 下创建以下两个文件。
4.1 proxy_defaults.conf(反代默认头与 WS 支持)
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;
# WebSocket 必需
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# 长连接/WS 建议拉长
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
# 上游失败切换策略(被动健康检查配合)
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
# 如后端对流式响应敏感,可视情况关闭缓冲
# proxy_buffering off;
4.2 ssl_params.conf(TLS 安全参数与响应头)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!3DES:!ADH:!RC4:!DH:!DHE;
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 10m;
# OCSP Stapling(需要 fullchain 及可用 DNS)
ssl_stapling on;
ssl_stapling_verify on;
resolver 223.5.5.5 223.6.6.6 114.114.114.114 119.29.29.29 valid=300s ipv6=off;
resolver_timeout 5s;
# 安全响应头(可按需调整)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options SAMEORIGIN always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
证书务必使用 fullchain;如果中间证书缺失,客户端可能握手失败。
5.定义上游池 upstream(负载均衡)
在 conf/upstreams.d/ 下创建 app_upstream.conf,按 CPU 核心数 经验分配权重,并启用最少连接法:
# conf/upstreams.d/app_upstream.conf
upstream app_upstream {
least_conn; # 最少连接策略
# 💡 请替换为你的真实内网 IP 与端口(示例注释中的“16/8核”为权重参考)
server 10.0.0.2:8080 weight=2 max_fails=3 fail_timeout=10s; # 16核
server 10.0.0.3:8080 weight=1 max_fails=3 fail_timeout=10s; # 8核
server 10.0.0.4:8080 weight=1 max_fails=3 fail_timeout=10s; # 8核
keepalive 64; # 复用到上游的长连接数量
}
max_fails/fail_timeout属于被动健康检查,当连接/响应失败累计到阈值后,暂时将该节点视为不可用;可结合proxy_next_upstream做快速切换。
6.站点 server 配置(HTTP→HTTPS、HTTP/2、证书路径)
在 conf/conf.d/ 下创建 app.example.com.conf:
# conf/conf.d/app.example.com.conf
# 80 端口:强制跳转到 HTTPS
server {
listen 80;
server_name app.example.com;
return 301 https://$host$request_uri;
}
# 443 端口:主站(启用 HTTP/2)
server {
listen 443 ssl;
http2 on;
server_name app.example.com;
# 证书路径(fullchain + 私钥),请替换为你的真实文件位置
ssl_certificate D:/nginx/certs/app.example.com.pem;
ssl_certificate_key D:/nginx/certs/app.example.com.key;
# 引用 TLS 安全参数 & 通用反代设置
include snippets/ssl_params.conf;
include snippets/proxy_defaults.conf;
# 应用转发(根路径)
location / {
proxy_pass http://app_upstream;
}
# 如需限制上传大小(示例 50M):
# client_max_body_size 50m;
}
7.IIS 侧设置
IIS 操作:
- 站点 A:HTTP 绑定
*:8080,主机名留空; - 站点 B:HTTP 绑定
*:8081,主机名留空; - 其它站点以此类推(8082、8083…)。
NGINX 配合(示例:两个域名分别指向不同端口站点):
# conf/upstreams.d/appA_upstream.conf
upstream appA_upstream {
least_conn;
server 10.0.0.2:8080 weight=2 max_fails=3 fail_timeout=10s;
server 10.0.0.3:8080 weight=1 max_fails=3 fail_timeout=10s;
keepalive 64;
}
# conf/upstreams.d/appB_upstream.conf
upstream appB_upstream {
least_conn;
server 10.0.0.2:8081 weight=2 max_fails=3 fail_timeout=10s;
server 10.0.0.4:8081 weight=1 max_fails=3 fail_timeout=10s;
keepalive 64;
}
# conf/conf.d/app.example.com.conf
server {
listen 80;
server_name app.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name app.example.com;
ssl_certificate D:/nginx/certs/app.example.com.pem;
ssl_certificate_key D:/nginx/certs/app.example.com.key;
include snippets/ssl_params.conf;
include snippets/proxy_defaults.conf;
location / { proxy_pass http://appA_upstream; }
}
# conf/conf.d/admin.example.com.conf
server {
listen 80;
server_name admin.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name admin.example.com;
ssl_certificate D:/nginx/certs/admin.example.com.pem;
ssl_certificate_key D:/nginx/certs/admin.example.com.key;
include snippets/ssl_params.conf;
include snippets/proxy_defaults.conf;
location / { proxy_pass http://appB_upstream; }
}
8.将 NGINX 以 Windows 服务运行(服务化)
Windows 版本的 nginx 被视为测试版本,截止至1.29.1,nginx作为服务运行,仍然是future。
幸运的是,我找到了一个开源的解决方案 https://github.com/winsw/winsw,它是用.net构建的,允许将任何可执行文件作为服务运行。
winsw允许将任何.exe 文件作为 Windows 服务使用。它使用 XML 来处理服务的配置和安装。 对于Windows x64可以直接下载
https://github.com/winsw/winsw/releases/download/v2.12.0/WinSW-x64.exe
下载后,进入nginx目录,并将文件复制到目录下,在这个例子中是 D:/nginx,并将其重命名为 nginx-service.exe。
接下来,创建一个 nginx-service.xml 来描述服务,注意调整nginx路径:
<service>
<id>nginx</id>
<name>Nginx</name>
<description>Nginx web server</description>
<executable>D:\nginx\nginx.exe</executable>
<workingdirectory>D:\nginx</workingdirectory>
<!-- 停服务时优雅退出 -->
<stopexecutable>D:\nginx\nginx.exe</stopexecutable>
<stoparguments>-s quit</stoparguments>
<startmode>Automatic</startmode>
<logpath>D:\nginx\logs</logpath>
<log mode="roll-by-size">
<sizeThreshold>10485760</sizeThreshold>
<keepFiles>5</keepFiles>
</log>
</service>
以管理员权限打开 PowerShell 或 CMD:
# 安装为服务(nginx-service.exe)
.\nginx-service.exe install
# 启动服务
.\nginx-service.exe start
# 停止服务
.\nginx-service.exe stop
# 重启服务
.\nginx-service.exe stop
# 卸载服务
.\nginx-service.exe uninstall
nginx自检仍然使用 .\nginx.exe -t
如果出现意外,比如自己用.\nginx.exe 启动了nginx 而导致脱离了服务的控制,无法通过nginx-service.exe对服务进行重启的,就需要强制结束了。 所以,不要自己的用.\nginx启动,要重启之前,先自检,因为重启报错也可能导致脱离服务管控。
taskkill /F /IM nginx.exe
注意:
- 确认进程权限与工作目录正确,否则热加载/服务管理可能失败;
- 路径含空格时,建议简化安装路径或使用引号。
9.(补充)实现基于粘性Cookie的会话保持
前面配置的upstream,使用least_conn更均衡的分配。但是用到websocket就不行了。 这里我参考了阿里云负载均衡服务(SLB)中CLB的 植入Cookie方式,来保持会话粘性。
9.1 修改nginx.conf
在 http {} 片段中插入代码,其中SRV_STICKY就是我们要植入的cookie,记住等会会用到,当然也可以根据实际情况修改。
# 是否需要种黏性Cookie
map $cookie_SRV_STICKY $need_sticky {
"" 1; # 没有 -> 需要下发
default 0;
}
# 生成要下发的Cookie值(只在 need_sticky=1 时使用)
map $request_id $sticky_value {
default $request_id;
}
9.3 增加upstream以支持sticky
修改conf/upstreams.d/app_upstream.conf 增加app_signalr_cookie
# conf/upstreams.d/app_upstream.conf
upstream app_upstream {
... 原来的不要动
}
# 新增的
upstream app_signalr_cookie {
hash $cookie_SRV_STICKY consistent; # 按我们种的 cookie 黏性
# 💡 请替换为你的真实内网 IP 与端口(示例注释中的“16/8核”为权重参考)
server 10.0.0.2:8080 weight=2 max_fails=3 fail_timeout=10s; # 16核
server 10.0.0.3:8080 weight=1 max_fails=3 fail_timeout=10s; # 8核
server 10.0.0.4:8080 weight=1 max_fails=3 fail_timeout=10s; # 8核
keepalive 64; # 复用到上游的长连接数量
}
9.2 对指定的websocket路径设置粘性会话
修改conf/conf.d/app.example.com.conf, 增加一条规则。
location ^~ /ws/ {
# 第一次没 Cookie 才种
if ($need_sticky) {
add_header Set-Cookie "SRV_STICKY=$sticky_value; Path=/; Max-Age=3600; HttpOnly; Secure; SameSite=Lax" always;
}
proxy_pass http://app_signalr_cookie;
proxy_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
接下来
- 重启nginx
- 在浏览器中验证Cookie中是否有key:
SRV_STICKY
10.常见问题与排错清单
- 502/504:上游不可达或超时。检查:
- 内网防火墙是否放通 8080/8081;
- IIS 站点是否启动、应用池是否健康;
max_fails/fail_timeout是否过于严格;proxy_read_timeout/proxy_send_timeout是否偏短。
- 413(Payload Too Large):增加 client_max_body_size(例如 50m)。
- 无法握手/证书错误:确保证书为 fullchain,密钥与证书匹配,域名一致。
- WebSocket 400/426:确认已设置
proxy_http_version 1.1、Upgrade/Connection头与 map $http_upgrade。 - 真实客户端 IP:应用/日志读取
X-Forwarded-For;IIS可在高级日志字段中记录该头。 - 日志定位:查看
logs/error.log与logs/access.log,快速筛选关键字符串或状态码。 - 端口占用:Windows 上用
netstat -ano | findstr :80 / :443 / :8080定位占用进程。
11.发布前检查清单(Checklist)
- ✅ 域名解析正确,公网仅指向 NGINX 所在机器;
- ✅ 证书(fullchain/privkey)路径与权限无误;
- ✅ Windows 防火墙、云安全组等放通 80/443(入口)与 8080/8081(到 IIS);
- ✅ NGINX 配置
nginx -t自检通过,-s reload生效; - ✅ 访问 https://app.example.com/ 正常;后端 8080/8081 健康;
- ✅ WebSocket/长连接业务已验证,超时时间设置合理;
- ✅ 日志中出现 200/301/101 等预期状态码,后端无异常 5xx。
Comments