Windows服务器如何配置IIS网站?从安装到域名绑定完整教程

IIS(Internet Information Services)是 Windows Server 自带的 Web 服务器,装完之后不用额外下载任何东西就能跑静态站点、ASP.NET 应用、做反向代理。相比 Nginx 和 Apache,IIS 的优势是和 Windows 生态深度集成(进程模型、身份验证、服务托管),缺点也是这个——配置项分散在图形界面、applicationHost.config 和 web.config 三处,新手经常改了这里不懂那里。

这篇从零开始走一遍:安装 Web 服务器角色 → 理解应用程序池的关键选项 → 建站点、绑域名、配 SSL → 设目录权限 → 装 URL 重写模块做 HTTP 跳转 HTTPS → 最后给出 500 / 403 / 404 三类错误的快速排查路径。命令在 Server 2016 / 2019 / 2022 上通用。

第一步:安装 IIS 角色

一条 PowerShell 命令搞定,装完不需要重启:

# 安装 Web 服务器角色并包含管理工具(管理器所在的那部分控制台)
Install-WindowsFeature Web-Server -IncludeManagementTools

# 常用附加角色的组合安装(按需要选,不要全装)
Install-WindowsFeature Web-Server, Web-Asp-Net45, Web-ISAPI-Ext, Web-ISAPI-Filter,
  Web-Mgmt-Console, Web-Log-Libraries, Web-Request-Monitor, Web-Http-Tracing -IncludeManagementTools

# 卸载
Uninstall-WindowsFeature Web-Server

# 查看已安装的功能
Get-WindowsFeature | Where-Object Installed | Select-Object DisplayName, Name

-IncludeManagementTools 一定要带上,否则只装了服务没有 IIS 管理器(inetmgr)。Server Core 上也可以装,但图形管理器要用另外的机器做远程管理。

图形路径:服务器管理器 → 添加角色和功能 → 基于角色或基于功能的安装 → 服务器角色 → 勾选"Web 服务器(IIS)" → 角色服务按需勾选。

安装后验证:

iisreset /status
Get-Service W3SVC, WAS | Select-Object Name, Status
Start-Service W3SVC

# 浏览器访问 localhost 应看到默认欢迎页
Invoke-WebRequest http://localhost -UseBasicParsing | Select-Object StatusCode

装完之后马上可以访问 http://localhost,看到 IIS 的蓝色欢迎页说明服务正常。外部访问还需要确认防火墙和安全组放行了 80 / 443。

New-NetFirewallRule -DisplayName "IIS-HTTP" -Direction Inbound -Protocol TCP -LocalPort 80 -Action Allow -Profile Any
New-NetFirewallRule -DisplayName "IIS-HTTPS" -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow -Profile Any

第二步:理解应用程序池

应用程序池(Application Pool)是 IIS 的核心概念:它是 w3wp.exe 工作进程的容器,决定了你的站点跑在什么版本的 .NET 上、用什么身份访问磁盘、什么时候被回收。一个池下面挂站点,池一崩,下面所有站点一起挂。

创建和配置:

Import-Module WebAdministration

New-WebAppPool -Name "MyApp-Pool"
Set-ItemProperty "IIS:\AppPools\MyApp-Pool" -Name managedRuntimeVersion -Value "v4.0"
Set-ItemProperty "IIS:\AppPools\MyApp-Pool" -Name managedPipelineMode -Value "Integrated"
Set-ItemProperty "IIS:\AppPools\MyApp-Pool" -Name processModel.idleTimeout -Value "00:00:00"
Set-ItemProperty "IIS:\AppPools\MyApp-Pool" -Name recycling.periodicRestart.time -Value "00:00:00"

Get-ItemProperty "IIS:\AppPools\MyApp-Pool" | Select-Object *

关键选项该怎么选:

选项 取值 怎么选
.NET CLR 版本 v2.0 / v4.0 / 无托管代码 ASP.NET 4.x 用 v4.0;ASP.NET Core 和纯静态、PHP 选"无托管代码"
托管管道模式 集成 Integrated / 经典 Classic 新站点一律 Integrated;跑老旧 WebForms 或需要 MapRequestHandler 的老组件时用 Classic
标识 ApplicationPoolIdentity / LocalSystem / NetworkService / 指定账户 默认 ApplicationPoolIdentity 最安全,访问网络共享时改用域账户
空闲超时 默认 20 分钟 企业站点建议调成 0(不回收),否则每天首次访问会慢
回收时间 默认 1740 分钟(29 小时) 建议改到业务低峰的固定时间,避免高峰 recycle 造成 Session 丢失
快速故障保护 5 次失败 5 分钟内 程序异常状况下会自动停池,调试时留意症状,别只看站点挂了

关于回收,最常踩的坑是:站点莫名其妙每天早上变慢,其实是空闲超时导致进程被杀,第一个请求要重新 JIT 编译 + 建连接池。设置 idleTimeout=0 并且把回收排到凌晨,基本能解决。

再看一组实用的运维命令:

# 停止 / 启动某个池(不影响其他站点)
Stop-WebAppPool -Name "MyApp-Pool"
Start-WebAppPool -Name "MyApp-Pool"
Restart-WebAppPool -Name "MyApp-Pool"

# 查看该池下有几个工作进程(Web Garden)
Get-ItemProperty "IIS:\AppPools\MyApp-Pool" -Name processModel.maxProcesses

# 查看当前运行的 w3wp 及其所属池
Get-WmiObject Win32_Process -Filter "Name='w3wp.exe'" | Select-Object ProcessId, CommandLine

最后一行很有用,CommandLine 里带了 -ap "MyApp-Pool",可以直接把任务管理器里看到的高 CPU 进程对应到具体站点。

第三步:建站点、绑域名

推荐的做法是先把文件放到 D:\wwwroot\mysite,不要放在 C:\inetpub\wwwroot 下面和默认站点混在一起。

New-Item -ItemType Directory -Path D:\wwwroot\mysite -Force
# 把站点文件拷进去后:

New-IISSite -Name "mysite" -PhysicalPath "D:\wwwroot\mysite" -BindingInformation "*:80:www.example.com"

# 或者用老的 AppCmd / PowerShell 组合方式
New-Website -Name "mysite" -PhysicalPath "D:\wwwroot\mysite" -Port 80 -HostHeader "www.example.com" -ApplicationPool "MyApp-Pool"
Set-ItemProperty "IIS:\Sites\mysite" -Name applicationPool -Value "MyApp-Pool"

绑定(Binding)的三要素:IP 地址、端口、主机名。主机名留空表示匹配所有;填了主机名后,只有 Host 头匹配的请求才会落到这个站点,这也是一台服务器托管多个域名的基础。

绑定写法 含义
*:80: 任意 IP、80 端口、任意主机名(兜底)
*:80:www.example.com 只接受 Host 为 www.example.com 的请求
10.0.0.10:8080: 只绑定特定网卡 IP 的 8080 端口,通常用于内网或管理后台
*:443:www.example.com + SNI HTTPS,多域名共享 443 时必须开启 SNI

一个域名同时有多个前缀(example.com 和 www.example.com)时,加第二条绑定即可:

New-IISSiteBinding -Name "mysite" -BindingInformation "*:80:example.com" -Protocol http
Get-IISSiteBinding -Name "mysite"
Remove-IISSiteBinding -Name "mysite" -BindingInformation "*:80:example.com" -Protocol http

HTTPS 绑定需要先有证书,导入后再绑:

# 导入 PFX 证书到本机个人存储
$pwd = ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force
Import-PfxCertificate -FilePath D:\cert\example.com.pfx -CertStoreLocation Cert:\LocalMachine\My -Password $pwd

# 创建 HTTPS 绑定并指定证书(需 thumbprint)
$thumb = (Get-ChildItem Cert:\LocalMachine\My | Where-Object Subject -like "*example.com*").Thumbprint
New-IISSiteBinding -Name "mysite" -BindingInformation "*:443:www.example.com" `
  -Protocol https -SslFlag 1 -CertificateThumbPrint $thumb     # SslFlag 1 = 启用 SNI

提示:多个 HTTPS 站点共用 443 时,必须勾选"需要服务器名称指示(SNI)"。不支持 SNI 的老客户端(Windows XP 上的 IE)会拿到证书告警,考虑到当前几乎没有这种流量,可以直接启用 SNI。

第四步:目录权限,这一环决定 403 还是正常

IIS 访问磁盘文件时用的是应用程序池标识。默认的 ApplicationPoolIdentity 在文件系统上表现为虚拟账户 IIS AppPool\MyApp-Pool。还有一个内置组 IIS_IUSRS,所有池标识默认都在里面。

给目录授权的正确做法:

$acl = Get-Acl D:\wwwroot\mysite
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
  "IIS AppPool\MyApp-Pool", "Modify", "ContainerInherit,ObjectInherit", "None", "Allow")
$acl.AddAccessRule($rule)
Set-Acl D:\wwwroot\mysite $acl

# 或者直接对组授权(更简单,隔离性差一点)
icacls D:\wwwroot\mysite /grant "IIS_IUSRS:(OI)(CI)(M)" /T
icacls D:\wwwroot\mysite /grant "IIS AppPool\MyApp-Pool:(OI)(CI)(M)" /T

# 只看有哪些权限
icacls D:\wwwroot\mysite

权限给的粒度建议:

目录 推荐权限 原因
静态内容、程序 DLL 目录 读取 + 执行 防止被上传写入 Webshell
上传目录 / App_Data 修改(不含执行) 允许写入但禁止运行脚本
日志目录 修改 应用需要落盘日志
临时目录 修改 某些框架需要

风险提示:不要用 Everyone:Full Control 也不要把站点目录放到桌面或用户目录下图省事。上传目录要在 IIS 里额外禁用脚本执行权限:Set-WebConfigurationProperty -Filter /system.webServer/handlers ... 或图形界面在"处理程序映射"里编辑功能权限为"只读"。

第五步:默认文档与 URL 重写

默认文档就是访问 / 时自动找哪个文件,IIS 默认有 Default.htm、Default.aspx、index.htm 等,但没有 index.php 或 index.html(小写 html 也没在默认列表里,Windows 文件系统不区分大小写所以实际能匹配)。

Get-WebConfigurationProperty -Filter "//defaultDocument/files" -PSPath "IIS:\Sites\mysite" -Name Collection
Add-WebConfigurationProperty -Filter "//defaultDocument/files" -PSPath "IIS:\Sites\mysite" -Name Collection -Value "index.php"
Add-WebConfigurationProperty -Filter "//defaultDocument/files" -PSPath "IIS:\Sites\mysite" -Name Collection -Value "index.html"
Remove-WebConfigurationProperty -Filter "//defaultDocument/files" -PSPath "IIS:\Sites\mysite" -Name Collection -Value "iisstart.htm"

URL 重写模块(URL Rewrite Module)需要额外下载安装,官方安装包名是 rewrite_amd64_zh-CN.msi(中文版)或 rewrite_amd64_en-US.msi。装完之后 inetmgr 里站点下会出现"URL 重写"图标,或者直接写 web.config:



  
    
      
        
          
          
            
            
          
          
        
      
    
  

风险提示:跳转规则用 redirectType="Permanent"(301)会被浏览器长期缓存。上线前先改成 Found(302)验证没问题再改成 301,否则规则配错时用户端会有很长的缓存残留。

安装重写模块后用 PowerShell 验证:

Get-WebGlobalModule | Where-Object Name -eq "RewriteModule"
Invoke-WebRequest -Uri "http://www.example.com" -MaximumRedirection 0 -ErrorAction SilentlyContinue |
  Select-Object StatusCode, Headers

第六步:500 / 403 / 404 怎么查

错误码 常见原因 怎么查
500.19 web.config 配置错误、模块没装、节被父级锁定 页面本身会给出"配置文件"和"错误行号",或看 HTTPERR 日志
500.21 处理程序没匹配到,通常是 ASP.NET Core 没装 Hosting Bundle 检查 AspNetCoreModuleV2 模块是否注册
500.23 ASP.NET 未在集成模式下配置 确认 下有相应 handler
403.14 目录浏览被禁用且没有默认文档 加默认文档,或临时启用目录浏览验证
401.3 文件 ACL 拒绝 给 IIS AppPool\<池名> 授权,见第四步
404.3 MIME 类型缺失或处理程序未注册 加 MIME 类型或安装该功能
503 应用程序池停止 Get-IISAppPool 看状态,可能是快速故障保护触发

日志位置:

# 站点访问日志
Get-ChildItem C:\inetpub\logs\LogFiles\W3SVC* -Recurse | Sort-Object LastWriteTime -Descending | Select-Object -First 5

# HTTP 错误日志(500 系列的细节常在这里)
Get-Content C:\Windows\System32\LogFiles\HTTPERR\httperr*.log -Tail 30

# Windows 事件
Get-EventLog -LogName Application -EntryType Error -Newest 20 |
  Where-Object Source -like "*ASP.NET*" | Format-List *

通用诊断顺序:先看能否本地 curl http://localhost:端口 访问(能通说明是网络/绑定问题),再打开详细错误(IIS 管理器 → 错误页 → 编辑功能设置 → 详细错误),最后看 HTTPERR 日志里的 s-reason 字段。记得详情页只应该对内网 IP 显示,公网站点要改回"自定义错误页"。

# 允许详细错误仅限内网访问(改完记得恢复)
Set-WebConfigurationProperty -PSPath "IIS:\Sites\mysite" -Filter "/system.webServer/httpErrors" `
  -Name errorMode -Value "DetailedLocalOnly"
Get-WebConfigurationProperty -PSPath "IIS:\Sites\mysite" -Filter "/system.webServer/httpErrors" -Name errorMode

常见问题

Q:装完 IIS 外网访问不了?
三件事:服务器上的 Windows 防火墙放行 80/443;云服务器安全组入方向放行 80/443;确认默认站点是否已占用端口,或者被新建站点的主机头抢占。用这些命令逐条验证:netstat -ano | findstr :80,Test-NetConnection 公网IP -Port 80。

Q:同一个 IP 想放两个网站?
用不同主机名(Host 头)区分,两个站点都绑 80 端口但主机名不同。HTTP/1.1 靠 Host 头路由,HTTPS 场景则需要 SNI。

Q:ASP.NET Core 站点报 500.21?
没有安装 .NET Core Hosting Bundle。装上后执行 iisreset,并确认应用程序池的 .NET CLR 版本 为"无托管代码",进程不带 CLR 才是托管宿主模型的正确姿势。

Q:修改了配置需要重启吗?
多数改动不需要。web.config 保存后立即生效(会自动回收该站点对应的 AppDomain);改应用程序池标识、站点绑定需要 restart 对应池;只有安装角色/模块才需要 iisreset 或重启。

Q:如何查看某个站点当前占多少内存?
Get-WmiObject Win32_Process -Filter "Name='w3wp.exe'" | Select-Object ProcessId,CommandLine,WorkingSetSize,按 CommandLine 里的池名对应站点。也可以用 perfmon 的 Process(w3wp*) 系列计数器做长期观察。

总结

  • 安装用 Install-WindowsFeature Web-Server -IncludeManagementTools,别忘了防火墙和安全组两层放行 80/443。
  • 应用程序池的两个高价值调整:idleTimeout 设为 0,回收时间挪到业务低峰,能显著改善"每天首访问慢"的现象。
  • 绑定配置记住三要素:IP、端口、主机名;多 HTTPS 站点共享 443 必须开 SNI。
  • 目录权限出问题大多是 500.19 或 401.3;给 IIS AppPool\<池名> 授权,上传目录另加"禁止脚本执行"。
  • HTTP 跳 HTTPS 先在 URLs 重写模块里跑 302 验证,确认无误再切 301;详细错误页只在内网开放。