
1. 项目概述与核心挑战最近在帮一个朋友的公司处理一个棘手的部署问题他们原有的业务系统跑在Windows Server的IIS上新开发的一个微服务应用想用Nginx来做反向代理和负载均衡。最头疼的是服务器只有一个公网IP80和443端口已经被IIS上的几个重要网站占用了。老板要求新服务必须也能通过80端口访问而且不能影响现有业务。这个场景其实挺典型的很多从传统.NET技术栈向现代架构迁移的团队都会遇到。直接让Nginx和IIS硬抢80端口肯定行不通系统会报错。经过一番折腾和测试我摸索出了一套稳定共存的方案不仅解决了端口冲突还顺带优化了整体架构。这篇文章我就把从思路梳理、具体配置到避坑排查的全过程详细拆解一遍如果你也在Windows环境下搞过Nginx和IIS的集成或者未来可能有类似需求这篇实操记录应该能帮你省下不少时间。2. 整体架构设计与思路拆解2.1 为什么选择Nginx与IIS共存很多朋友可能会问既然IIS已经在了为什么还要引入Nginx直接用IIS的ARRApplication Request Routing模块做反向代理不行吗这里就涉及到技术选型的核心考量了。ARR功能确实强大但它深度绑定IIS和Windows配置管理偏图形化对于习惯用配置文件声明式管理、或者未来有跨平台部署需求的团队来说学习成本和迁移成本都不低。Nginx的优势在于其轻量、高性能以及极其灵活的反向代理和负载均衡能力配置文件清晰社区资源丰富。更重要的是我们可以让Nginx作为“流量调度员”站在最前线根据域名或路径将请求分发给后端的IIS或其他应用如Tomcat、Node.js、静态文件服务器实现架构上的解耦。2.2 解决80端口冲突的核心思路端口冲突的本质是在TCP/IP协议栈中同一个协议如TCP的同一个端口号在同一时刻只能被一个进程监听。所以让Nginx和IIS的http.sysWindows的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天。找不到时才将请求转发给IISbackend。你需要确保root指令指向的路径下确实有这些静态文件。4.2 HTTPSSSL/TLS配置如果网站需要HTTPS最佳实践是在Nginx层面统一终止SSL即由Nginx处理证书和解密然后以HTTP协议将请求明文转发给后端的IISproxy_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 FarmNginx可以轻松实现负载均衡。http { # 定义一个上游服务器组名为 iis_backend upstream iis_backend { # 可以配置权重、健康检查等 server 192.168.1.101:8081 weight3 max_fails3 fail_timeout30s; # 服务器A权重高 server 192.168.1.102:8081 weight2 max_fails3 fail_timeout30s; # 服务器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找到对应进程。如果是SystemPID 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 SignalRNginx代理也需要特殊配置。解决在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的完整路径大部分问题都能迎刃而解。