Windows下Nginx与IIS共存:解决80端口冲突的反向代理方案
1. 项目概述与核心挑战
最近在帮一个朋友的公司处理一个棘手的部署问题,他们原有的业务系统跑在Windows Server的IIS上,新开发的一个微服务应用想用Nginx来做反向代理和负载均衡。最头疼的是,服务器只有一个公网IP,80和443端口已经被IIS上的几个重要网站占用了。老板要求新服务必须也能通过80端口访问,而且不能影响现有业务。这个场景其实挺典型的,很多从传统.NET技术栈向现代架构迁移的团队都会遇到。直接让Nginx和IIS硬抢80端口肯定行不通,系统会报错。经过一番折腾和测试,我摸索出了一套稳定共存的方案,不仅解决了端口冲突,还顺带优化了整体架构。这篇文章,我就把从思路梳理、具体配置到避坑排查的全过程详细拆解一遍,如果你也在Windows环境下搞过Nginx和IIS的集成,或者未来可能有类似需求,这篇实操记录应该能帮你省下不少时间。
2. 整体架构设计与思路拆解
2.1 为什么选择Nginx与IIS共存?
很多朋友可能会问,既然IIS已经在了,为什么还要引入Nginx?直接用IIS的ARR(Application Request Routing)模块做反向代理不行吗?这里就涉及到技术选型的核心考量了。ARR功能确实强大,但它深度绑定IIS和Windows,配置管理偏图形化,对于习惯用配置文件声明式管理、或者未来有跨平台部署需求的团队来说,学习成本和迁移成本都不低。Nginx的优势在于其轻量、高性能以及极其灵活的反向代理和负载均衡能力,配置文件清晰,社区资源丰富。更重要的是,我们可以让Nginx作为“流量调度员”站在最前线,根据域名或路径将请求分发给后端的IIS或其他应用(如Tomcat、Node.js、静态文件服务器),实现架构上的解耦。
2.2 解决80端口冲突的核心思路
端口冲突的本质是,在TCP/IP协议栈中,同一个协议(如TCP)的同一个端口号,在同一时刻只能被一个进程监听。所以,让Nginx和IIS的http.sys(Windows的HTTP协议栈驱动程序)同时监听0.0.0.0:80是注定失败的。我们的解决思路不是“抢”,而是“分工”和“让位”。
方案一:端口分流(不推荐)这是最直观但最不优雅的方案:让IIS继续监听80端口,Nginx改用其他端口如8080,然后在防火墙或路由器上做端口映射,把来自不同域名的80流量映射到不同的内部端口。这种方法问题很多,增加了网络设备的配置复杂度,不利于维护,且在某些云环境下操作受限。
方案二:Nginx作为前端代理(推荐)这是我们采用的方案,也是业界通行的最佳实践。让Nginx作为唯一的80/443端口监听者,承担起“总入口”的职责。然后,在Nginx配置中,将需要由IIS处理的请求(比如基于特定域名或URL路径),通过反向代理的方式,转发到IIS监听的另一个内部端口(例如8081)。这样一来:
- 对外统一:所有HTTP/HTTPS流量都从Nginx的80/443端口进入,便于统一管理SSL证书、访问日志、安全策略等。
- 对内分工:IIS从端口的争夺中解放出来,安心监听
127.0.0.1:8081这样的内部地址,只处理它该处理的ASP.NET、ASP.NET Core或静态文件请求。 - 架构清晰:Nginx成了网关,可以轻松添加对其他后端服务(Python、Go、Java应用)的代理,扩展性极好。
这个方案的关键在于,需要修改IIS上原有网站的绑定,从:80改为:8081,并在Nginx中配置相应的proxy_pass规则。
2.3 网络数据流向图
为了更直观地理解数据流向,我们可以看下面这个简化的示意图:
公网用户 | | (请求: http://www.old-site.com 或 http://api.new-app.com) v [ Nginx 进程 ] 监听: 0.0.0.0:80 (对外) | |--- 匹配域名 `www.old-site.com` | | | v | [反向代理转发] | 目标: http://127.0.0.1:8081 | | | v | [IIS / http.sys] | 监听: 127.0.0.1:8081 | 处理: ASP.NET 应用 | |--- 匹配域名 `api.new-app.com` 或路径 `/newapp/` | v [反向代理转发] 目标: http://127.0.0.1:3000 (或其他后端服务)3. 详细配置步骤与实操要点
3.1 环境准备与软件安装
1. Nginx for Windows 安装Nginx官网提供了Windows版本的稳定版压缩包。我推荐直接下载ZIP版本,解压即用,无需安装程序,更干净。
- 前往Nginx官网下载
nginx/Windows-x.x.xZIP包。 - 解压到任意目录,例如
C:\nginx。路径中最好不要有中文或空格。 - 目录结构主要关注
conf\nginx.conf(主配置文件)和logs\(日志目录)。
注意:Windows下的Nginx是以普通进程运行的,不是系统服务。如果希望开机自启,需要额外配置Windows服务,可以使用
winsw等工具,但这不属于本文核心。我们优先保证功能跑通。
2. IIS 配置调整前检查
- 确保你的IIS网站运行正常。打开“IIS管理器”,记下当前需要共存的网站的物理路径、绑定的域名等信息。
- 打开命令提示符(管理员),运行
netstat -ano | findstr :80,确认80端口确实被System进程(PID 4)或IIS工作进程占用,这证明http.sys在监听80端口。
3.2 修改IIS网站绑定
这是让出80端口的关键一步。
- 在IIS管理器中,选中你的目标网站,右侧点击“绑定”。
- 在网站绑定窗口中,你会看到类型为
http、绑定信息为*:80或特定IP:80的记录。编辑这条记录。 - 将“端口”从
80修改为一个未被占用的端口,例如8081。IP地址可以保持“全部未分配”,或者指定为127.0.0.1以增强安全性(只允许本机访问)。 - 点击“确定”保存。IIS可能会提示需要重启站点或应用池,确认即可。
- 修改后,立即在浏览器访问
http://localhost:8081,确认网站在新端口上可以正常访问。此时,原来的http://localhost:80应该无法访问了(如果Nginx还没启动的话,会显示连接失败)。
3.3 配置Nginx作为反向代理
现在我们来配置Nginx,让它接管80端口,并把请求转发给IIS。
1. 备份与编辑主配置文件打开C:\nginx\conf\nginx.conf,先做好备份。我们用文本编辑器(如VS Code、Notepad++)进行编辑。
2. 核心配置段详解我们需要在http { ... }块内,修改server块。默认配置里已经有一个监听80端口的示例server块,我们修改它或在其后添加新的。
http { # 一些全局配置,如日志格式、mime类型等,保持默认或按需调整 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; # 防止网络拥塞 tcp_nopush on; # 保持连接超时时间 keepalive_timeout 65; # 开启Gzip压缩 gzip on; # 第一个Server块:作为总入口,监听80端口 server { listen 80; # 监听所有IP的80端口 server_name _; # 默认服务器,匹配未明确指定的域名 # 可选:配置一个默认首页或错误提示 location / { root html; index index.html index.htm; # 可以返回一个提示页面,说明网关运行正常 } # 最重要的部分:反向代理到IIS # 假设你的旧网站域名为 www.old-site.com server_name www.old-site.com old-site.com; location / { # 核心代理指令 proxy_pass http://127.0.0.1:8081; # 指向我们修改后的IIS端口 # 以下是一组非常重要的代理头设置,确保IIS能获取到真实客户端信息 proxy_set_header Host $host; # 传递原始请求的Host头 proxy_set_header X-Real-IP $remote_addr; # 传递客户端真实IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 传递经过的代理IP链 proxy_set_header X-Forwarded-Proto $scheme; # 传递原始协议(http/https) # 连接超时设置 proxy_connect_timeout 30s; proxy_send_timeout 60s; proxy_read_timeout 60s; # 缓冲设置,应对大请求或慢客户端 proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k; } # 你可以为其他需要IIS服务的域名添加类似的location或server块 # server_name another-site.com; # location / { ... } } # 第二个Server块:代理新的微服务应用(示例) server { listen 80; server_name api.new-app.com; location / { proxy_pass http://127.0.0.1:3000; # 假设Node.js应用跑在3000端口 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } }3. 关键配置解析
proxy_pass http://127.0.0.1:8081;:这是核心,告诉Nginx把匹配到的请求转发到本机8081端口(即IIS)。proxy_set_header:这组指令至关重要。没有它们,IIS收到的所有请求都会显示来自127.0.0.1,丢失了原始客户端的IP、域名等信息,对于依赖这些信息的应用(如日志分析、IP限制、域名判断)会造成严重问题。server_name:用于基于域名的虚拟主机配置。当用户访问www.old-site.com时,Nginx会根据server_name匹配到这个server块,并执行其中的proxy_pass。
3.4 启动、测试与排错
1. 启动与重载Nginx
- 启动:进入Nginx目录(
C:\nginx),在命令行运行start nginx。如果控制台没有报错且迅速返回,通常表示启动成功。可以运行tasklist /fi "imagename eq nginx.exe"查看进程。 - 测试配置语法:在修改配置后,运行
nginx -t可以测试配置文件语法是否正确,它会给出明确的错误提示和行号,非常有用。 - 重载配置:修改配置后,无需重启Nginx(避免中断连接),运行
nginx -s reload即可平滑重载配置。
2. 验证步骤a.检查端口监听:运行netstat -ano | findstr :80,现在应该看到监听80端口的进程是nginx.exe,而不是System。 b.本地Hosts测试:在开发或测试环境,修改本机C:\Windows\System32\drivers\etc\hosts文件,添加一行127.0.0.1 www.old-site.com。然后在浏览器访问http://www.old-site.com。如果配置正确,你应该能看到IIS上的网站内容。 c.检查日志:查看C:\nginx\logs\error.log和access.log,这是排查问题的第一现场。access.log会记录所有经过Nginx的请求,error.log会记录任何警告或错误。
4. 高级配置与优化要点
4.1 静态资源分离与缓存优化
一个常见的优化点是,将网站的静态资源(图片、CSS、JS)通过Nginx直接提供服务,而不是经过IIS和ASP.NET管道,这样可以极大减轻IIS压力,提升响应速度。
server { listen 80; server_name www.old-site.com; location / { proxy_pass http://127.0.0.1:8081; # ... 其他代理头设置 } # 静态资源处理 location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ { # 假设静态资源存放在D:\webroot\static目录下 root D:/webroot; # 开启浏览器缓存 expires 30d; add_header Cache-Control "public, immutable"; # 可选:如果文件不存在,再尝试代理到IIS try_files $uri @backend; } location @backend { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; # ... 其他代理头设置 } }这个配置中,Nginx会优先在本地磁盘查找静态文件,找到就直接返回并告诉浏览器缓存30天。找不到时,才将请求转发给IIS(@backend)。你需要确保root指令指向的路径下确实有这些静态文件。
4.2 HTTPS(SSL/TLS)配置
如果网站需要HTTPS,最佳实践是在Nginx层面统一终止SSL,即由Nginx处理证书和解密,然后以HTTP协议将请求明文转发给后端的IIS(proxy_pass http://...)。这样做的好处是:
- 简化后端:IIS无需配置证书,降低复杂度。
- 性能更优:Nginx的SSL处理效率通常很高。
- 集中管理:证书的更新、更换只需在Nginx操作。
server { listen 443 ssl http2; # 监听443端口,启用SSL和HTTP/2 server_name www.old-site.com; # SSL证书路径 ssl_certificate C:/nginx/conf/ssl/www.old-site.com.crt; ssl_certificate_key C:/nginx/conf/ssl/www.old-site.com.key; # SSL优化配置 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:...; # 使用安全的加密套件 ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; location / { proxy_pass http://127.0.0.1:8081; 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; # 这个头现在会是'https',对IIS很有用 } } # 强制HTTP跳转到HTTPS server { listen 80; server_name www.old-site.com; return 301 https://$server_name$request_uri; }配置后,用户访问http://www.old-site.com会被301重定向到https://版本。IIS应用可以通过检查X-Forwarded-Proto头来判断原始请求是否是HTTPS。
4.3 负载均衡与健康检查
如果你的后端不止一个IIS服务器(例如做了Web Farm),Nginx可以轻松实现负载均衡。
http { # 定义一个上游服务器组,名为 iis_backend upstream iis_backend { # 可以配置权重、健康检查等 server 192.168.1.101:8081 weight=3 max_fails=3 fail_timeout=30s; # 服务器A,权重高 server 192.168.1.102:8081 weight=2 max_fails=3 fail_timeout=30s; # 服务器B # 备份服务器,当主服务器都宕机时启用 server 192.168.1.103:8081 backup; # 可以配置负载均衡算法,如 least_conn(最少连接) least_conn; } server { listen 80; server_name www.old-site.com; location / { proxy_pass http://iis_backend; # 指向上游服务器组 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; # ... 其他配置 } } }Nginx内置了简单的被动健康检查(通过max_fails和fail_timeout)。对于更复杂的主动健康检查,可以使用商业版Nginx Plus或结合第三方模块。
5. 常见问题与排查技巧实录
在实际操作中,我踩过不少坑,这里总结几个最常见的问题和解决方法。
5.1 问题:Nginx启动失败,报错“bind() to 0.0.0.0:80 failed”
原因:80端口被其他程序占用。最常见的就是IIS(或http.sys)没让出端口,或者SQL Server Reporting Services、World Wide Web Publishing Service等其他服务占用了80端口。排查:
netstat -ano | findstr :80找到占用80端口的进程PID。- 打开任务管理器,在“详细信息”标签页,根据PID找到对应进程。如果是
System(PID 4),通常是IIS或http.sys。需要确保IIS网站绑定已修改,并重启IIS服务(iisreset /restart)或重启World Wide Web Publishing Service服务。 - 如果被其他进程占用,根据进程名决定是否停止它。
5.2 问题:通过Nginx访问网站,样式错乱或图片不显示
原因:网页中的资源(CSS、JS、图片)链接使用的是绝对路径或相对路径,但经过Nginx代理后,资源请求的路径可能不对。排查与解决:
- 浏览器按F12打开开发者工具,查看“网络(Network)”标签页,找到加载失败的资源,看其请求URL是什么。
- 情况A:资源URL是类似
/css/style.css的相对路径,但请求被发往了Nginx的默认location /,然后被proxy_pass到了IIS,这没问题。但如果IIS应用内部生成的资源链接是带完整主机名的,就需要确保proxy_set_header Host $host;正确设置,让IIS能生成正确的链接。 - 情况B:资源URL是类似
http://localhost:8081/css/style.css的绝对路径。这说明网页代码硬编码了地址。最好的办法是修改应用代码,使用相对路径或从配置中读取基地址。临时解决方案可以在Nginx中使用sub_filter模块替换响应内容中的字符串,但性能有损耗且复杂。 - 采用前面提到的静态资源分离方案,是根治此类问题并提升性能的最佳实践。
5.3 问题:IIS应用获取到的客户端IP都是127.0.0.1
原因:Nginx转发请求时,没有正确设置X-Forwarded-For或X-Real-IP请求头。解决:确保Nginx配置中的location块里包含了正确的proxy_set_header指令,如proxy_set_header X-Real-IP $remote_addr;。在IIS端验证:对于ASP.NET应用,可以通过HttpContext.Current.Request.ServerVariables["HTTP_X_REAL_IP"]或HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]来读取这个头。可能需要安装和使用IIS Advanced Logging模块或修改应用代码来记录真实IP。
5.4 问题:上传大文件失败,或请求超时
原因:Nginx或IIS的默认请求大小、超时时间限制太小。解决:
- Nginx配置:在
http,server或location块中调整:client_max_body_size 100M; # 允许最大请求体为100MB,根据需求调整 proxy_connect_timeout 60s; proxy_send_timeout 180s; # 发送请求到后端的超时 proxy_read_timeout 180s; # 读取后端响应的超时 - IIS配置:在IIS管理器中,选择对应网站或应用池,在“功能视图”中找到“配置编辑器”。在
system.webServer/security/requestFiltering节点下,修改requestLimits.maxAllowedContentLength(单位是字节,例如104857600表示100MB)。对于ASP.NET应用,可能还需要在web.config中配置<httpRuntime maxRequestLength="..." />。
5.5 问题:WebSocket连接失败
如果IIS上运行了需要WebSocket的应用(如ASP.NET Core SignalR),Nginx代理也需要特殊配置。解决:在Nginx代理WebSocket的location块中,需要添加以下指令:
location /your-ws-path/ { proxy_pass http://127.0.0.1:8081; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; # 适当延长超时时间 proxy_read_timeout 3600s; proxy_send_timeout 3600s; }关键点在于Upgrade和Connection头的处理,它们用于将HTTP协议升级为WebSocket协议。
5.6 日常维护与监控心得
- 日志是黄金:养成定期查看
nginx\logs\error.log的习惯。很多问题(如配置错误、权限不足、上游服务器连接失败)都会在这里留下线索。access.log可以帮你分析流量模式。 - 平滑重载:修改配置后,尽量使用
nginx -s reload而不是先nginx -s stop再start nginx。reload会启动新的工作进程加载新配置,然后优雅地关闭旧进程,实现服务不中断。 - 进程管理:Windows下Nginx是主进程+工作进程模式。
nginx -s stop是快速停止,nginx -s quit是优雅停止(处理完当前请求)。如果进程卡死,可以用taskkill /F /IM nginx.exe强制结束。 - 性能监控:可以使用
nginx -V查看编译参数。在配置中,通过stub_status模块可以开启一个简单的状态页,查看连接数、请求数等基本信息。更详细的监控需要结合Windows性能计数器和日志分析工具。
这套方案在我负责的几个生产环境中已经稳定运行了超过一年。它最大的价值不仅仅是解决了端口冲突,更是为架构演进打开了一扇门。Nginx作为一个功能强大且稳定的前沿网关,后续可以非常方便地集成限流、缓存、安全防护(如WAF基础规则)、灰度发布等功能。而IIS则可以退居二线,专注于它擅长的.NET应用托管。这种分工明确的架构,让运维的边界更清晰,也让技术的迭代更从容。如果你在配置过程中遇到了上面没覆盖到的问题,多看看日志,理清请求从Nginx到IIS的完整路径,大部分问题都能迎刃而解。