.NET 应用部署到 Windows Server,绕不开 IIS。但很多人把它当成"托管容器"来用,结果配出来各种 502、500.30,跑不起来还不知道问题出在哪。
其实在 .NET Core 之后的架构里,IIS 的角色已经变了——它不再直接执行你的代码,而是一个网关。搞清楚这一点,部署问题就解决了一大半。
这篇文章说清楚 IIS 作为网关解决什么问题、怎么接入、以及真实项目里最容易踩的坑。
IIS 是什么
IIS(Internet Information Services)是微软出品的 Web 服务器,官网地址为
https://www.iis.net
在 .NET Framework 时代,IIS 通过 aspnet_isapi.dll 直接托管 ASP.NET 应用,工作进程既负责请求接收也负责代码执行。但从 .NET Core 开始,你的应用运行在独立的 Kestrel 进程中,IIS 不再直接执行业务代码,而是通过 ASP.NET Core 模块(ANCM)充当反向代理,将请求转发给 Kestrel。
所以,IIS 在 .NET 部署架构中的核心定位是网关,而非应用容器。它的价值在于:
TLS 终止:
集中管理 SSL 证书,后端 Kestrel 只处理 HTTP 流量。 请求分发:
通过 URL Rewrite 和 ARR 模块,按路径或主机头将请求路由到不同后端。 静态文件处理:
CSS、JS、图片等由 IIS 直接响应,不往后端转发,效率更高。 前置过滤:
IP 黑白名单、请求大小限制等安全策略在网关层统一处理。
怎么引入
第一步:装运行时
服务器上必须安装 .NET Hosting Bundle(托管捆绑包)
下载地址:
https://dotnet.microsoft.com/zh-cn/download/dotnet
注意:先装 IIS,再装 Hosting Bundle,顺序反了模块注册不上。装完建议重启。根据你的项目编译使用的.NET 版本,自行选择对应大版本下载 ,版本必须匹配,高低版本不兼容。
第二步:按需装扩展
大多数单站点 Web API 部署:这两个都不用装,IIS 通过 ASP.NET Core 模块直接转发即可。
装 ARR 后需手动启用代理:IIS 管理器 → 服务器节点 → Application Request Routing Cache → Server Proxy Settings → 勾选 Enable proxy。
第三步:代码配置转发头

注意:如果只调用 Clear() 而不添加信任的代理 IP,.NET 的安全策略会直接忽略来自 IIS 的转发请求头,导致 RemoteIpAddress 依然停留在 127.0.0.1。
验证接入成功: IIS 建站,应用程序池的".NET CLR 版本"设为"无托管代码",访问站点能正常返回即通。
快速上手
以下示例适用于将 IIS 作为独立反向代理、配合 ARR 转发至自主运行的 Kestrel 进程的场景。
示例 1:单站点转发
IIS 站点下配置 URL 重写规则:匹配 .*,重写为 http://127.0.0.1:5000/{R:0},所有请求转发到本地 Kestrel。
示例 2:按路径路由多后端
规则示例:匹配 ^order/(.*),重写为 http://127.0.0.1:5001/api/order/{R:1}。订单、用户、商品三个服务分别监听不同端口,IIS 根据 URL 前缀分发。
示例 3:转发配置实操案例
同一台服务器上 3 个独立 API 服务(订单 5001、用户 5002、商品 5003),通过 IIS 统一暴露 https://api.yourcompany.com 。配置三条重写规则按路径前缀(/order/*、/user/*、/product/*)转发到对应后端。不需要装 ARR,URL Rewrite 就够了。后端如需识别真实 IP,参考上文转发头配置。
常见报错
几个常见的 IIS 配置问题
问题 1:IIS 无法支持 APK 文件的下载
部署 Android 应用下载站点时,IIS 默认不支持 .apk 文件的下载,访问会返回 404。原因是 IIS 的 MIME 类型列表中默认没有注册 APK 文件的 MIME 类型。
解决方法:在 IIS 管理器中找到目标网站,双击"MIME 类型",点击右侧"添加",输入扩展名 .apk,MIME 类型填 application/vnd.android.package-archive,确定即可。添加后 APK 文件就能正常下载了。
问题 2:IIS 首次访问加载慢(冷启动)
部署 ASP.NET Core 项目时,经常会遇到首次访问很慢的情况。IIS 默认的应用程序池空闲超时(Idle Time-out)为 20 分钟,如果 20 分钟没有访问,应用池就会被回收,下次访问会触发重新启动,这就是冷启动。
解决方法:打开 IIS 管理器 → 应用程序池 → 找到你的应用池 → 右键"高级设置",修改两个参数:
Start Mode
设为 AlwaysRunning Idle Time-out
设为 0(永不回收)或调大(如 1440 分钟)
然后进入网站 → 高级设置 → "预加载已启用"设为 true。
真正彻底解决冷启动的组合是:AlwaysRunning + 预加载启用 + 应用程序池高级设置里的"特定时间回收"(如配置在凌晨 3:00 自动回收)。这样回收会伴随自动预热,用户无感知。
适用场景
适合: Windows Server + .NET 部署,需要 SSL 集中管理、多应用路由聚合、前置安全过滤。团队有 Windows 运维能力时,IIS 是成熟稳定的方案。
不太适合: 容器化/Linux 环境、对延迟极敏感的超高并发场景、团队缺乏 Windows 运维经验。
实战建议
坑位 1:应用程序池的 .NET CLR 版本设为 v4.0
沿用 .NET Framework 习惯设为 v4.0 会导致 IIS 错误地加载旧版 CLR 运行时,白白浪费服务器内存与性能。正确做法:改为"无托管代码"。
坑位 2:转发头只 Clear() 没加信任 IP
options.KnownNetworks.Clear() 和 options.KnownProxies.Clear() 都调用后,不添加任何信任 IP,转发头中间件会因安全策略直接忽略转发头,RemoteIpAddress 拿到的仍是 127.0.0.1。修复:清空后显式添加对 Loopback 和 IPv6Loopback 的信任。
坑位 3:ARR 改写了响应头里的 Host
导致重定向地址变成内部地址、Cookie 的 Domain 异常。修复:ARR 的 Server Proxy Settings 里取消勾选 "Reverse rewrite host in response headers"。
选型建议
Windows Server + 几千 QPS 以内的中小规模部署,IIS + Kestrel 完全够用。大规模微服务(几十个服务、需精细化流量治理)则更适合 Ocelot、YARP 或服务网格,IIS 的网关能力相对有限。
总结收尾
IIS 在 .NET Core 之后是网关,不是容器。它做 TLS 终止、路由分发、静态文件,业务代码跑在 Kestrel。搞清这个分工,502 和 500 就知道从哪查了。
Windows Server 上部署 .NET 应用,别把 Kestrel 直接暴露。装好 Hosting Bundle,调对转发头和应用程序池,再顺手把 MIME 类型和冷启动配置好,IIS 能帮你省掉一堆麻烦。