Windows服务器如何配置IIS网站?站点绑定、证书与权限设置

开发把站点包发过来了,说“部署一下就行”。你在 Windows Server 上装好 IIS,新建站点,浏览器打开却看到 403;改成 HTTPS 后变成 404;上传功能报“拒绝访问”;好不容易能访问了,第二天发现日志把 C 盘写满了。IIS(Internet Information Services)本身并不复杂,但站点绑定、证书、权限这三件事只要有一件没配对,表现出的错误又都长得差不多,于是排查成本陡增。

本文按真实部署顺序展开:安装角色 → 建应用池与站点 → 配置绑定(含 SNI 多证书)→ 申请与绑定 SSL 证书 → 配置 NTFS 权限与应用池标识 → 常见错误速查。所有操作同时给出图形入口(inetmgr)和命令行(appcmd / PowerShell),方便写进部署脚本。

一、安装 IIS 角色与必要组件

图形入口:服务器管理器(servermanager.exe)→ 添加角色和功能 → 基于角色或基于功能的安装 → 服务器角色 → 勾选“Web 服务器(IIS)”。关键是角色服务不要只勾默认项,ASP.NET 站点必须勾选“应用程序开发”下的对应版本。

# PowerShell 安装 Web 服务器(含管理工具)
Install-WindowsFeature -Name Web-Server -IncludeManagementTools

# 安装 ASP.NET 4.x 与常见组件(按需组合)
Install-WindowsFeature -Name `
  Web-Asp-Net45, Web-Net-Ext45, Web-ISAPI-Ext, Web-ISAPI-Filter, `
  Web-Default-Doc, Web-Dir-Browsing, Web-Http-Errors, Web-Static-Content, `
  Web-Http-Redirect, Web-Http-Logging, Web-Log-Libraries, Web-Request-Monitor, `
  Web-Http-Tracing, Web-Filtering, Web-Url-Auth, Web-Basic-Auth, `
  Web-Windows-Auth, Web-Digest-Auth, Web-Client-Auth, Web-Mgmt-Console

# 确认安装结果
Get-WindowsFeature | Where-Object { $_.Name -like 'Web-*' -and $_.Installed } |
  Select-Object Name, DisplayName | Format-Table -AutoSize
REM DISM 等价方式(脱机映像或 Server Core 常用)
DISM /Online /Enable-Feature /FeatureName:IIS-WebServerRole /FeatureName:IIS-WebServer /FeatureName:IIS-ManagementConsole /FeatureName:IIS-ASPNET45
DISM /Online /Get-Features /Format:Table | findstr IIS

验证安装:浏览器访问 http://localhost,出现 IIS 欢迎页说明成功(默认站点 Default Web Site 占用 80 端口、站点 ID 为 1)。远程访问不通时,先放行防火墙:

REM 放行 HTTP / HTTPS
netsh advfirewall firewall add rule name="Web-HTTP-80"  dir=in action=allow protocol=TCP localport=80
netsh advfirewall firewall add rule name="Web-HTTPS-443" dir=in action=allow protocol=TCP localport=443

二、创建应用池与站点

强烈建议先建应用池再建站点,一个站点一个池,避免互相拖累。图形路径:inetmgr → 应用程序池 → 添加应用程序池 → 设置 .NET CLR 版本(v4.0 或无托管代码)与托管管道模式(集成 Integrated)。

2.1 appcmd 方式(CMD,最通用)

REM 切换到 inetsrv 目录
cd /d %windir%\system32\inetsrv

REM 创建应用池(无托管代码站点用 v4.0 也无妨,纯静态/Node/Python 选"无托管代码")
appcmd add apppool /name:MySitePool /managedRuntimeVersion:v4.0 /managedPipelineMode:Integrated

REM 创建站点并指定物理路径与绑定
appcmd add site /name:"MySite" /id:10 /physicalPath:"D:\www\mysite" /bindings:http/*:80:www.example.com

REM 把站点指定到应用池
appcmd set site /site.name:"MySite" /[path='/'].applicationPool:MySitePool

REM 设置默认文档
appcmd set config "MySite" /section:defaultDocument /+files.[value='index.html']

REM 启动/停止/回收
appcmd start site "MySite"
appcmd stop site "MySite"
appcmd recycle apppool /apppool.name:MySitePool

REM 查看站点、应用池、工作进程
appcmd list site
appcmd list apppool
appcmd list wp

2.2 PowerShell 方式

# 使用老的 WebAdministration 模块(Server 2012 R2 / IIS 8.5 及以前)
Import-Module WebAdministration
New-WebAppPool -Name "MySitePool"
Set-ItemProperty IIS:\AppPools\MySitePool -Name managedRuntimeVersion -Value "v4.0"
Set-ItemProperty IIS:\AppPools\MySitePool -Name processModel.identityType -Value ApplicationPoolIdentity
New-WebSite -Name "MySite" -Port 80 -HostHeader "www.example.com" -PhysicalPath "D:\www\mysite" -ApplicationPool "MySitePool"
Start-WebSite -Name "MySite"
Get-Website | Select-Object Name, ID, State, PhysicalPath, Bindings
Get-IISSite
# 使用新的 IISAdministration 模块(Server 2016+ / IIS 10 推荐)
Import-Module IISAdministration
New-IISAppPool -Name "MySitePool"
New-IISSite -Name "MySite" -PhysicalPath "D:\www\mysite" `
  -BindingInformation "*:80:www.example.com" -Protocol http
Get-IISAppPool | Select-Object Name, Status, @{n='Runtime';e={$_.ManagedRuntimeVersion}}

三、站点绑定:端口、主机头与 SNI

绑定(Binding)决定“什么样的请求会被这个站点接收”,由 协议 + IP + 端口 + 主机头(Host Header)四元组组成。一台服务器用 80 端口承载多个域名,靠的就是主机头。

绑定示例含义适用场景
http/*:80:(主机头为空)该 IP 上 80 端口的所有请求单站点,或作为默认兜底站点
http/*:80:www.a.com仅匹配 Host 为 www.a.com 的请求多域名共用 80 端口(最常用)
http/10.0.0.5:80:仅监听指定网卡 IP多网卡服务器、内外网分离
https/*:443:www.a.comHTTPS + 主机头需配合证书与 SNI
https/*:443:www.a.com(SNI 勾选)同一 IP:443 绑定多张证书多 HTTPS 域名共用一 IP
REM 追加一条 https 绑定(SslFlags: 0=IP 绑定 1=SNI 2=中央证书存储)
appcmd set site /site.name:"MySite" /+bindings.[protocol='https',bindingInformation='*:443:www.example.com',sslFlags='1']

REM 修改已有绑定
appcmd set site /site.name:"MySite" /bindings.[protocol='http',bindingInformation='*:80:'].bindingInformation:'*:80:www.newdomain.com'

REM 删除绑定
appcmd set site /site.name:"MySite" /-bindings.[protocol='https',bindingInformation='*:443:old.example.com']
# PowerShell:添加 / 查看绑定
New-WebBinding -Name "MySite" -Protocol https -Port 443 -HostHeader "www.example.com" -SslFlags 1
New-WebBinding -Name "MySite" -Protocol http  -Port 80  -HostHeader "example.com"
Get-WebBinding -Name "MySite" | Select-Object protocol, bindingInformation, sslFlags
Remove-WebBinding -Name "MySite" -BindingInformation "*:80:old.example.com"

四、证书:申请、导入与 HTTPS 绑定

4.1 生成 CSR 并提交 CA

图形入口:inetmgr → 服务器节点 → 服务器证书 → 右侧“创建证书申请”。填写通用名称(必须与域名一致)、组织信息、加密位长(RSA 2048 起步),生成 CSR 文本文件后提交给证书颁发机构。

REM 用 certreq 生成 CSR:先准备 request.inf
REM [NewRequest]
REM Subject = "CN=www.example.com, OU=IT, O=Example Inc, C=CN"
REM KeyLength = 2048
REM KeySpec = 1
REM MachineKeySet = TRUE
REM Exportable = TRUE
certreq -new request.inf csr.txt

REM 查看生成的请求、提交给 CA、接受返回的证书
certreq -submit csr.txt cert.cer
certreq -accept cert.cer

REM 查看本机证书存储
certutil -store My
# 生成自签名证书(仅测试环境)
New-SelfSignedCertificate -DnsName "www.example.com","example.com" `
  -CertStoreLocation "cert:\LocalMachine\My" -FriendlyName "MySite-SSL" `
  -NotAfter (Get-Date).AddYears(1)

# 查看证书指纹(绑定时需要)
Get-ChildItem cert:\LocalMachine\My |
  Select-Object Subject, FriendlyName, NotAfter, Thumbprint | Format-List

4.2 完成申请并绑定到站点

REM 把 CA 返回的证书安装到"个人/My"存储
certreq -accept cert.cer

REM 老系统(Server 2008 R2)用 netsh 绑定证书到 IP:Port
netsh http add sslcert ipport=0.0.0.0:443 certhash=0123456789abcdef0123456789abcdef01234567 appid={5d89a20c-beab-4389-9447-324788eb944a}

REM 查看 / 删除绑定
netsh http show sslcert
netsh http delete sslcert ipport=0.0.0.0:443

在 IIS 管理器里绑定更简单:选中站点 → 右侧“绑定” → 添加 → 类型 https → 主机名填域名 → 勾选“需要服务器名称指示(SNI)” → 在 SSL 证书下拉框选择证书。若下拉框为空,说明证书没有私钥或不在“个人”存储中,需要先用 certreq -accept 或 certlm.msc 导入(证书管理器:运行 mmc → 添加/删除管理单元 → 证书 → 计算机账户 → 本地计算机)。

证书续期提醒:Let's Encrypt 等免费证书 90 天有效,建议用 win-acme 之类的工具配合任务计划程序(taskschd.msc)自动续期。证书过期是 HTTPS 站点最常见的“突然打不开”。

五、权限设置:应用池标识与 NTFS 权限

这是最多人卡住的一环。IIS 访问磁盘文件时,使用的身份是应用池标识(处理动态内容)与 IUSR(匿名访问静态文件),而不是登录服务器的管理员账号。默认的应用池标识 ApplicationPoolIdentity 是一个虚拟账号,名称格式为 IIS AppPool\<池名>。

标识类型说明优点缺点
ApplicationPoolIdentity(默认,推荐)虚拟账号 IIS AppPool\池名隔离性好、无需管密码访问网络共享时身份为机器账号
NetworkService内置账号,域中以计算机账号身份访问网络可访问网络资源权限较宽,隔离性差
LocalService低权限本地账号权限最小网络访问受限,不常用
LocalSystem最高权限几乎不会有权限问题安全风险极大,禁止使用
SpecificUser(自定义域账号)指定账号便于访问域内共享与数据库需管理密码,改密后站点起不来
REM 给应用池标识授予站点目录读取与执行权限(RX)
icacls "D:\www\mysite" /grant "IIS AppPool\MySitePool:(OI)(CI)(RX)" /T /Q

REM 给上传/日志等需要写入的目录授予修改权限(M)
icacls "D:\www\mysite\upload" /grant "IIS AppPool\MySitePool:(OI)(CI)(M)" /T /Q

REM 静态文件匿名访问还需 IUSR 读取权限
icacls "D:\www\mysite" /grant "IUSR:(OI)(CI)(RX)" /T /Q

REM 查看当前权限
icacls "D:\www\mysite"
# 修改应用池标识为指定账号(需输入密码)
appcmd set config /section:applicationPools /[name='MySitePool'].processModel.identityType:SpecificUser
appcmd set config /section:applicationPools /[name='MySitePool'].processModel.userName:DOMAIN\svc_web
appcmd set config /section:applicationPools /[name='MySitePool'].processModel.password:P@ssw0rd

# 设置闲置超时与回收(避免默认 20 分钟回收导致会话丢失)
Set-ItemProperty IIS:\AppPools\MySitePool -Name processModel.idleTimeout -Value ([TimeSpan]::FromMinutes(0))
Set-ItemProperty IIS:\AppPools\MySitePool -Name recycling.periodicRestart.time -Value ([TimeSpan]::FromMinutes(0))

身份验证配置(inetmgr → 站点 → 身份验证):匿名身份验证默认使用 IUSR;Windows 身份验证需禁用匿名;ASP.NET 模拟(impersonation)开启后,会以访问者身份访问资源,配置复杂,非必要不开。

六、常见错误速查

错误常见原因处理
403.14 Forbidden目录浏览关闭且无默认文档添加 index.html / default.aspx 为默认文档
401.3 UnauthorizedNTFS 权限不足用 icacls 给应用池标识或 IUSR 授权
500.19 配置错误web.config 节点未解锁或模块未安装看错误详情里的 configSource,安装对应角色服务
500.21 处理程序未注册未安装 ASP.NET / .NET Core 托管捆绑包安装 Web-Asp-Net45 或 Hosting Bundle 后重启
503 Service Unavailable应用池已停止或连续崩溃启动应用池;查应用程序日志与快速失败保护
404 找不到主机头不匹配、物理路径错、绑定端口错核对绑定与 appcmd list site
站点无法启动“端口被占用”80/443 被其他进程或 HTTP.SYS 保留netsh http show servicestate view=requestq
HTTPS 证书下拉为空证书无私钥或不在个人存储用 certlm.msc 重新导入 pfx(含私钥)
REM 查看 HTTP.SYS 的 URL 保留与监听队列(端口占用排查神器)
netsh http show servicestate view=requestq
netsh http show urlacl
netsh http show iplisten

REM 查看 IIS 站点日志目录(HTTP 状态码 + 子状态码是定位关键)
cd /d C:\inetpub\logs\LogFiles
dir
appcmd list site
# 查看最近的应用池崩溃与 WAS 日志
Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='Microsoft-Windows-WAS'} -MaxEvents 20 |
  Select-Object TimeCreated, Id, LevelDisplayName, Message

# 统计日志中各类状态码出现次数
Get-ChildItem "C:\inetpub\logs\LogFiles\W3SVC10" -Filter *.log |
  Get-Content | ForEach-Object { ($_ -split ' ')[-4] } |
  Group-Object | Sort-Object Count -Descending | Select-Object -First 10 Count, Name

七、部署步骤清单

  1. 安装 Web-Server 角色及所需角色服务(ASP.NET、ISAPI、日志、跟踪等)。
  2. 放行防火墙 80 / 443,云主机同时配置安全组。
  3. 规划目录结构,站点根目录放在非系统盘(如 D:\www\)。
  4. 为每个站点创建独立应用池,设置 .NET 版本与管道模式。
  5. 创建站点,配置绑定(IP、端口、主机头)。
  6. 用 icacls 给 IIS AppPool\<池名> 授予读取执行权限,写目录单独授予修改权限。
  7. 配置默认文档与身份验证方式。
  8. 生成 CSR → 申请证书 → certreq -accept 导入 → 绑定 HTTPS(多域名启用 SNI)。
  9. 把 IIS 日志目录改到数据盘,配置日志滚动与归档清理。
  10. 本地 curl http://localhost 验证,再从外网访问验证,最后检查 HTTPS 证书链与重定向。
  11. 配置监控:站点可用性、应用池状态、磁盘余量、证书到期天数。

小结与避坑建议

Windows 服务器配置 IIS 网站,把“应用池 → 站点 → 绑定 → 证书 → 权限”这条链路走顺,八成的坑就绕开了。记住三条命令基本能自证:appcmd list site 看站点与 ID、netsh http show servicestate view=requestq 看端口占用、icacls 看文件权限。

实战中最值得记牢的几条:

  • 一个站点一个应用池。共用一个池,某个站点内存泄漏会拖垮全部站点。
  • 不要用 LocalSystem 跑应用池。权限问题是解决了,但一次上传漏洞就等于整台服务器沦陷。默认 ApplicationPoolIdentity 加精确 NTFS 授权才是正解。
  • 给写入权限要精确到目录。整个站点给 Modify 等于给攻击者开后门,只有 upload、cache、logs 这类目录需要写权限。
  • 绑定主机头不要留空。留空的站点会抢走该 IP:端口上所有未匹配的请求,导致其他站点“莫名 404”。
  • 证书一定要含私钥。只导入 .cer 公钥证书,IIS 的 SSL 证书下拉框会是空的,此时用 certlm.msc 重新导入包含私钥的 .pfx。
  • 把 IIS 日志迁出系统盘。默认 C:\inetpub\logs,高流量站点几天就能写满 C 盘,改目录用 appcmd set config -section:system.applicationHost/sites -siteDefaults.logFile.directory:"D:\IISLogs"。
  • 关注证书到期。配置自动续期或至少提前 30 天告警,证书过期导致的业务中断在行业里每年都在重演。
  • 改配置前先备份。appcmd add backup "before-change" 一条命令,回滚时 appcmd restore backup "before-change",比手动改回去可靠得多。