ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Windows Server 2022 搭建 Web 服务器:IIS 安装、站点配置与 DNS 解析实战

Windows Server 2022 搭建 Web 服务器:IIS 安装、站点配置与 DNS 解析实战

简介:这份资源面向 IT 运维人员与 Windows 服务器初学者,聚焦 Windows Server 2022 环境下 Web 服务器的搭建与配置,帮助读者从零完成服务器安装、功能验证到站点上线的完整流程。内容围绕服务器安装、功能测试、网站挂载与域名解析四大环节展开,涵盖安装介质准备、语言时区选择、磁盘分区、网络与硬件测试、网站目录创建与素材拷贝、端口与域名配置,以及 DNS 服务器安装和记录配置等关键知识点,适合作为实验操作参考或课程配套资料。资源包共 1 个 docx 文档,约 1016KB,以图文步骤形式组织,便于按章节对照实践。目前已有 2767 人学习下载,可作为搭建 Windows Web 服务环境的入门指引与操作备忘。

1. 从一台裸机到能访问的站点:Windows Server 2022 Web 服务器搭建到底在做什么

手里只有一台刚装好系统的 Windows Server 2022,没有 IIS,没有站点目录,没有域名,连防火墙都还是默认状态——这是很多运维和开发第一次接手 Web 服务器时的真实起点。所谓“搭建 Web 服务器”,拆开看就是四件事:把 IIS 角色装起来、验证网络与系统功能正常、把站点素材挂到 IIS 站点上并配好端口、最后用 DNS 把域名和 IP 对上。这四步任何一步没走通,浏览器里就是一片 404 或者连接超时。这篇笔记面向需要在一台 Windows 服务器上快速落地静态或轻量动态站点的人,从角色安装一路写到 DNS 解析验证,中间把端口冲突、权限、防火墙这些血泪坑都标出来。Windows 服务器这套东西不玄学,但细节多,照着走能少翻几次车。

2. 装 IIS 与功能测试:角色安装、网络自检和最小可用验证

2.1 为什么选 IIS 而不是第三方 Web 服务

在 Windows Server 2022 上搭 Web 服务,常见做法有三条路:IIS、Nginx for Windows、以及各种自带运行时的应用服务器。选 IIS 的理由很实际——它是系统原生角色,和 Windows 的账户体系、防火墙、证书存储、事件日志是一套东西,装完就能用Get-WindowsFeature查状态,出问题能在事件查看器里顺着来源筛。Nginx 在 Windows 上跑静态资源没问题,但作为服务注册、日志轮转、权限继承都要自己补,对“一台服务器挂个站点”这种诉求属于过度工程。IIS 的短板是配置偏重、模块多,但换来的是图形化和 PowerShell 双通道管理,批量部署时WebAdministration模块能省很多事。所以这里的选型结论很直接:单机、内网、轻量站点,IIS 是默认答案;只有当你有跨平台反向代理、高并发静态分发需求时,才考虑把 Nginx 放到前面。

2.2 用 PowerShell 安装 IIS 角色与常用子功能

图形化“服务器管理器 → 添加角色和功能”能装,但一线更常用 PowerShell,因为可复现、可写进部署脚本。下面这段在管理员权限的 PowerShell 里执行,装的是 Web 服务器核心加管理工具、静态内容、默认文档、HTTP 重定向、日志这几块。

# 安装 IIS 核心角色及常用子功能 Install-WindowsFeature -Name Web-Server ` -IncludeManagementTools ` -IncludeAllSubFeature # 只装必要子功能时可用下面这种精确写法 Install-WindowsFeature -Name Web-Server, Web-WebServer, Web-Common-Http, ` Web-Default-Doc, Web-Static-Content, Web-Http-Redirect, Web-Http-Logging, ` Web-Filtering, Web-Mgmt-Console # 确认安装结果,State 应为 Installed Get-WindowsFeature -Name Web-Server, Web-Mgmt-Console | Select-Object Name, InstallState

-IncludeManagementTools会带上 IIS 管理器,省得后面再补;-IncludeAllSubFeature适合实验环境图省事,生产环境建议用第二段那种精确列表,少装一个模块就少一个攻击面。Web-Filtering是请求筛选,后面配端口和 IP 限制会用到;Web-Http-Logging不开的话排错时连访问记录都没有,属于后悔药级别的功能,别省。装完Get-WindowsFeature返回Installed才算数,如果显示Available说明角色名写错或源文件缺失。

装好后验证 IIS 是否真的在跑,别只看管理器界面,直接查服务和端口:

# 查看 W3SVC 服务状态 Get-Service W3SVC | Select-Object Name, Status, StartType # 确认 80 端口处于监听 Get-NetTCPConnection -LocalPort 80 -State Listen | Select-Object LocalAddress, LocalPort, OwningProcess # 本机发起一次 HTTP 请求,看默认页是否返回 200 Invoke-WebRequest -Uri "http://localhost" -UseBasicParsing | Select-Object StatusCode

W3SVC是 IIS 的万维网发布服务,它没起来后面全白搭;Get-NetTCPConnection比netstat -ano好在能直接拿到OwningProcess,端口被谁占了一眼可见;Invoke-WebRequest返回 200 说明默认站点已经能响应。默认站点根目录在C:\inetpub\wwwroot,看到 IIS 欢迎页就代表角色层通了。

2.3 网络与系统功能自检:别把环境问题当成 IIS 问题

角色装完不等于服务器可用。功能测试这一步很多人跳过,结果后面站点打不开,花了半天以为是 IIS 配置,其实是网卡没配网关或者系统时间漂移导致证书校验失败。我一般按三层查:网络层、系统层、服务层。

网络层先确认 IP、网关、DNS 是否配全,用ipconfig /all看,重点看默认网关和 DNS 服务器两项。然后测连通性:

# 查看本机网络配置摘要 Get-NetIPConfiguration | Select-Object InterfaceAlias, IPv4Address, IPv4DefaultGateway, DNSServer # 测试到网关和外部地址的连通性 Test-NetConnection -ComputerName 192.168.1.1 -InformationLevel Quiet Test-NetConnection -ComputerName 8.8.8.8 -Port 53 -InformationLevel Quiet # 检查系统时间和时区,证书与日志都依赖它 Get-Date Get-TimeZone

Test-NetConnection的-InformationLevel Quiet只回True/False,适合写进脚本做判断;测 53 端口是验证 DNS 出站是否被拦,这一步能提前暴露防火墙策略问题。系统层还要看磁盘剩余空间和事件日志里有没有反复出现的硬件或驱动告警,Get-EventLog -LogName System -EntryType Error -Newest 20扫一眼,别等站点跑起来才发现磁盘满了写不进日志。

服务层就是前面那组W3SVC和端口检查。三层都过了,才算“服务器功能测试”完成。这里有个容易忽略的点:Windows Server 2022 默认启用了 IE 增强安全配置,服务器上用浏览器访问 localhost 可能弹拦截提示,别被这个吓到,用Invoke-WebRequest或curl.exe验证更干净。

3. 挂载站点与端口配置:目录、权限、绑定三件事

3.1 站点目录规划与素材拷贝

挂载网站第一步不是打开 IIS 管理器,而是先把目录结构定下来。默认的C:\inetpub\wwwroot能用,但多个站点混在一起后期很难维护。常见做法是按站点名建独立目录,比如D:\Sites\demo,把网站素材(HTML、CSS、JS、图片)整体拷进去。拷贝用Copy-Item或资源管理器都行,关键是拷完检查两件事:入口文件是否存在(index.html或default.aspx),以及目录里有没有残留的.git、备份压缩包这类不该对外暴露的东西。

# 创建站点目录并拷贝素材 New-Item -ItemType Directory -Path "D:\Sites\demo" -Force Copy-Item -Path "\\fileserver\assets\demo\*" -Destination "D:\Sites\demo" -Recurse -Force # 确认入口文件存在 Test-Path "D:\Sites\demo\index.html" # 列出目录内容,排查是否混入敏感文件 Get-ChildItem "D:\Sites\demo" -Force | Select-Object Name, Length, LastWriteTime

-Recurse保证子目录一起拷,-Force覆盖同名文件;Test-Path返回False就说明素材路径写错或入口文件名不对,这是后面 404 的头号原因。Get-ChildItem -Force会把隐藏文件也列出来,.git目录如果被拷进站点根目录,等于把源码历史公开了,看到就删。

3.2 用 PowerShell 新建站点并绑定端口

IIS 里“站点”的本质是:一个物理路径 + 一组绑定(协议、IP、端口、主机名)+ 一个应用程序池。图形化新建站点很快,但端口和主机名这些参数用命令行更不容易点错。下面新建一个监听 8080 端口的站点,物理路径指向刚才的目录。

# 新建应用程序池,指定 .NET CLR 版本(静态站点用 No Managed Code 即可) New-WebAppPool -Name "demoPool" Set-ItemProperty -Path "IIS:\AppPools\demoPool" -Name managedRuntimeVersion -Value "" # 新建站点,绑定 8080 端口 New-Website -Name "demo" ` -PhysicalPath "D:\Sites\demo" ` -ApplicationPool "demoPool" ` -Port 8080 ` -IPAddress "*" ` -Force # 查看站点状态与绑定信息 Get-Website -Name "demo" | Select-Object Name, State, PhysicalPath Get-WebBinding -Name "demo" | Select-Object protocol, bindingInformation

managedRuntimeVersion设为空字符串对应“无托管代码”,纯静态站点不需要加载 .NET 运行时,设成 v4.0 反而多一层依赖。-IPAddress "*"表示监听所有网卡地址,如果服务器有多块网卡、只想对内网提供服务,就改成具体 IP。-Port 8080是这里的关键参数——端口不能和已有站点重复,IIS 允许同一端口配不同主机名(靠 HTTP Host 头区分),但同一 IP 同一端口不能有两个站点同时监听,否则后建的会启动失败,事件日志里报“无法绑定”。

端口选的时候避开系统常用段:80/443 留给正式站点,8080/8081 这类做测试或内部服务,别用 135、139、445 这些系统服务端口。改端口后记得同步放行防火墙:

# 放行 8080 入站 New-NetFirewallRule -DisplayName "IIS Demo 8080" ` -Direction Inbound -Protocol TCP -LocalPort 8080 -Action Allow # 验证规则生效 Get-NetFirewallRule -DisplayName "IIS Demo 8080" | Select-Object DisplayName, Enabled, Direction, Action

防火墙没放行是“本机能访问、别的机器访问不了”的经典原因,New-NetFirewallRule建完规则默认就是启用的,Get-NetFirewallRule确认Enabled为True。到这一步,用http://服务器IP:8080应该能看到站点内容。

3.3 应用程序池身份与目录权限

站点能起来但报 401 或 500.19,八成是权限问题。IIS 的工作进程默认以应用程序池身份运行,Windows Server 2022 上新建池的默认身份是ApplicationPoolIdentity,它在文件系统里表现为虚拟账户IIS AppPool\demoPool。站点目录如果放在 D 盘且没给这个账户读权限,就会出现“本机管理员能打开、IIS 返回 401.3”的现象。

# 给应用程序池虚拟账户授予站点目录读取和执行权限 $acl = Get-Acl "D:\Sites\demo" $rule = New-Object System.Security.AccessControl.FileSystemAccessRule( "IIS AppPool\demoPool", "ReadAndExecute", "ContainerInherit,ObjectInherit", "None", "Allow") $acl.SetAccessRule($rule) Set-Acl -Path "D:\Sites\demo" -AclObject $acl # 确认权限条目 (Get-Acl "D:\Sites\demo").Access | Where-Object { $_.IdentityReference -like "*demoPool*" }

ReadAndExecute对静态站点足够,需要上传功能的站点才给Modify,但范围要收窄到具体上传目录,别整个站点根目录放开写权限。ContainerInherit,ObjectInherit让权限往下继承,子目录和文件自动生效。如果站点还涉及匿名身份验证,确认 IIS 里“匿名身份验证”用的是应用程序池身份而不是IUSR,两者混用会导致权限判断对不上。

4. DNS 解析落地:把域名和 IP 对上,别只改 hosts

4.1 装 DNS 服务器角色与建正向区域

站点用 IP 加端口能访问之后,下一步是让域名指向这台服务器。内网环境常见做法是在 Windows Server 2022 上装 DNS 角色,建正向查找区域,把demo.internal这类内部域名解析到服务器 IP。装角色和建区域都可以用 PowerShell 完成:

# 安装 DNS 服务器角色 Install-WindowsFeature -Name DNS -IncludeManagementTools # 新建正向查找区域(主区域) Add-DnsServerPrimaryZone -Name "demo.internal" -ZoneFile "demo.internal.dns" -DynamicUpdate None # 新建 A 记录,把主机名指向服务器 IP Add-DnsServerResourceRecordA -Name "www" -ZoneName "demo.internal" -IPv4Address "192.168.1.50" # 查看区域内的记录 Get-DnsServerResourceRecord -ZoneName "demo.internal" -RRType A

-DynamicUpdate None表示不允许客户端动态注册,内网小环境这样更可控,避免测试机乱注册记录污染区域。Add-DnsServerResourceRecordA的-Name "www"生成的是www.demo.internal,如果要解析根域名本身,-Name用@。建完记录后用Resolve-DnsName验证:

# 指定本机 DNS 服务器解析,确认返回正确 IP Resolve-DnsName -Name "www.demo.internal" -Server 127.0.0.1 -Type A # 清本机 DNS 缓存后再测一次 Clear-DnsClientCache Resolve-DnsName -Name "www.demo.internal" -Type A

-Server 127.0.0.1强制走本机 DNS 服务,排除客户端缓存干扰;Clear-DnsClientCache是排错时的常规动作,改完记录不生效先清缓存再看。如果解析返回的 IP 不对,检查是不是区域里存在同名多条 A 记录,或者服务器网卡上配了多个 IP 导致注册了错误地址。

4.2 让 IIS 站点响应域名:主机头绑定与解析联动

DNS 只负责“域名 → IP”,浏览器拿到 IP 后还要靠 HTTP 请求里的 Host 头让 IIS 找到对应站点。所以光有 A 记录不够,站点绑定里要加主机名。给前面的 demo 站点补一个 80 端口、主机头为www.demo.internal的绑定:

# 为站点添加主机头绑定(80 端口) New-WebBinding -Name "demo" -Protocol "http" -Port 80 -HostHeader "www.demo.internal" -IPAddress "*" # 查看该站点全部绑定 Get-WebBinding -Name "demo" | Format-Table protocol, bindingInformation -AutoSize

bindingInformation会显示成*:80:www.demo.internal这种格式,三段分别是 IP、端口、主机头。同一台服务器上多个站点共用 80 端口时,就是靠主机头区分,这也是为什么“端口不能重复使用”要补一句:端口可以复用,但同一 IP 端口下主机头不能重复,否则后绑定的站点启动报错。配完后用域名访问验证:

# 通过域名访问,确认返回 200 且内容正确 Invoke-WebRequest -Uri "http://www.demo.internal" -UseBasicParsing | Select-Object StatusCode, StatusDescription

如果返回 404 而不是连接失败,说明 DNS 和端口都通了,问题在站点绑定或默认文档;如果返回 400,多半是 Host 头和绑定不匹配。这一步把“域名解析”和“站点挂载”真正串起来了,也是整套流程里最容易只做一半的地方——很多人 A 记录建完就以为完事,结果浏览器还是打到默认站点。

5. 避坑与排查:端口、权限、防火墙、DNS 缓存这四类翻车现场

5.1 端口被占用导致站点启动失败

现象:新建站点后状态显示“已停止”,手动启动报错,事件日志里出现“无法绑定到端口”。原因:同一 IP 同一端口已有其他站点或进程在监听,IIS 不允许两个站点抢同一个 IP:端口组合。解决:先用Get-NetTCPConnection -LocalPort 8080 -State Listen查占用进程,Get-Process -Id <PID>看是谁;如果是旧站点,改端口或停掉旧站点;如果是系统进程占用,换端口最省事。改完端口记得同步改防火墙规则,否则又变成“本机能开、外部打不开”。

5.2 目录权限不足返回 401.3

现象:服务器上用管理员账户能直接打开站点文件,浏览器访问却返回 401.3 或 500.19。原因:应用程序池身份IIS AppPool\<池名>对站点目录没有读取权限,尤其是站点放在非系统盘、由其他账户创建目录时。解决:按 3.3 的脚本给池身份授ReadAndExecute,并确认匿名身份验证用的是应用程序池身份。排查时看 IIS 日志里的sc-win32-status,401.3 基本就是 ACL 问题,别去翻 web.config。

5.3 防火墙放行了端口但外部仍访问不了

现象:本机Invoke-WebRequest http://localhost:8080正常,另一台机器访问超时。原因:Windows 防火墙入站规则没放行该端口,或者放行了但作用域限制在本机/特定网段。解决:Get-NetFirewallRule查规则是否存在且Enabled,再看Get-NetFirewallRule -DisplayName "..." | Get-NetFirewallPortFilter确认端口和协议对得上。还有一种情况是服务器所在网络的上层设备做了策略限制,这种就不在服务器层面解决了,先确认同网段其他机器能否访问,缩小范围。

5.4 DNS 改完不生效,客户端还在用旧地址

现象:A 记录已经改成新 IP,Resolve-DnsName -Server 127.0.0.1返回正确,但客户端浏览器还是访问到旧地址。原因:客户端 DNS 缓存、或者客户端网卡上配的 DNS 服务器不是这台,走了别的解析源。解决:客户端执行ipconfig /flushdns,再用Resolve-DnsName -Name www.demo.internal -Server <DNS服务器IP>指定服务器验证。如果客户端配的是公共 DNS,内部域名根本解析不到,这种情况要么改客户端 DNS 指向,要么在 hosts 里临时写死做验证——hosts 只用于排错,别当长期方案。

5.5 默认文档缺失导致目录列表或 404

现象:域名和端口都通了,访问根路径返回 404 或者列出目录文件。原因:站点目录里没有index.html/default.aspx这类默认文档,或者 IIS 默认文档列表里没有对应文件名。解决:确认入口文件名,然后在 IIS 里把该文件名加到默认文档列表顶部,或用Add-WebConfiguration追加。目录列表能关就关,Web-Dir-Browsing不开,避免把目录结构暴露出去。

6. 进阶验证与批量落地:用脚本把整套流程固化下来

单台服务器手工点一遍没问题,但一旦要重复部署或者交接给别人,图形化操作就是黑匣子。我的习惯是把第 2 到第 4 章的动作收敛成一个幂等脚本:装角色、建目录、建池、建站点、配绑定、放行防火墙、建 DNS 记录,每步先判断再执行,重复跑不报错。下面这段是核心骨架,参数抽成变量,换站点只改变量区。

# 参数区:换站点只改这里 $SiteName = "demo" $SitePath = "D:\Sites\demo" $Port = 8080 $HostHeader = "www.demo.internal" $ZoneName = "demo.internal" $ServerIP = "192.168.1.50" # 1. 角色(已装则跳过) if (-not (Get-WindowsFeature -Name Web-Server).Installed) { Install-WindowsFeature -Name Web-Server -IncludeManagementTools } # 2. 目录与应用程序池 New-Item -ItemType Directory -Path $SitePath -Force | Out-Null if (-not (Get-WebAppPool -Name "${SiteName}Pool" -ErrorAction SilentlyContinue)) { New-WebAppPool -Name "${SiteName}Pool" Set-ItemProperty -Path "IIS:\AppPools\${SiteName}Pool" -Name managedRuntimeVersion -Value "" } # 3. 站点(存在则只补绑定) if (-not (Get-Website -Name $SiteName -ErrorAction SilentlyContinue)) { New-Website -Name $SiteName -PhysicalPath $SitePath -ApplicationPool "${SiteName}Pool" -Port $Port -IPAddress "*" } if (-not (Get-WebBinding -Name $SiteName | Where-Object { $_.bindingInformation -like "*:$Port`:$HostHeader" })) { New-WebBinding -Name $SiteName -Protocol "http" -Port $Port -HostHeader $HostHeader -IPAddress "*" } # 4. 防火墙 if (-not (Get-NetFirewallRule -DisplayName "IIS $SiteName $Port" -ErrorAction SilentlyContinue)) { New-NetFirewallRule -DisplayName "IIS $SiteName $Port" -Direction Inbound -Protocol TCP -LocalPort $Port -Action Allow } # 5. DNS 记录 if (-not (Get-DnsServerResourceRecord -ZoneName $ZoneName -Name "www" -ErrorAction SilentlyContinue)) { Add-DnsServerResourceRecordA -Name "www" -ZoneName $ZoneName -IPv4Address $ServerIP }

-ErrorAction SilentlyContinue配合if (-not ...)是幂等的关键,存在就跳过、不存在才建,脚本可以反复跑。bindingInformation的匹配串里端口和主机头之间是冒号,写的时候注意转义。这套骨架跑完,再用一组验证命令收尾:Get-Website看状态、Invoke-WebRequest看状态码、Resolve-DnsName看解析结果,三项全绿才算部署完成。

验证方法上我一般分三层做,比单看浏览器靠谱:

验证层命令通过标准
服务层Get-Service W3SVCStatus 为 Running
站点层Invoke-WebRequest http://<IP>:<Port>StatusCode 200
解析层Resolve-DnsName www.<zone> -Server <DNS IP>返回预期 IP

三层里任何一层失败,排查顺序从下往上:先确认 DNS 解析对不对,再看端口和防火墙通不通,最后才查 IIS 站点绑定和权限。反过来查容易在 IIS 配置里绕圈,实际问题是解析没生效。

有个细节值得单独说:Windows Server 2022 的 IIS 日志默认在C:\inetpub\logs\LogFiles,按站点 ID 分目录,排错时Get-Content配合Select-String筛状态码比翻事件日志快得多。比如筛 404 和 500:

# 在最新日志里筛非 200 状态码的请求 Get-ChildItem "C:\inetpub\logs\LogFiles" -Recurse -Filter "*.log" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 | Get-Content | Select-String -Pattern " (404|500|401) "

日志字段是空格分隔的,状态码在倒数几位,Select-String用前后空格夹住数字能减少误匹配。这个习惯帮我省过不少时间——很多“站点时好时坏”的问题,日志里一看就是某类请求稳定 500,直接定位到具体页面而不是瞎猜。

从那以后我每次搭完站点,都强制走一遍“服务 → 端口 → 防火墙 → 解析 → 绑定”这条链路,每步留一条验证命令的输出,交接时把输出贴进文档,比口头说“配好了”可靠得多。希望这套流程帮到你,少在端口和权限上翻车。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进