LHIDC

在Windows Server生产环境部署Web服务,如何配置反向代理与进程守护

面向具备基础操作能力的运维人员,介绍在Windows Server中使用IIS、ARR、URL Rewrite与WinSW部署Web服务,涵盖回环端口、HTTPS证书、服务恢复、日志与资源边界,并提供分层验证、故障排查及安全回滚步骤。

在Windows Server生产环境部署Web服务,如何配置反向代理与进程守护

生产环境的目标状态应当是:IIS 负责监听 80/443、终止 TLS 并将请求转发到本机应用端口;Web 应用以 Windows 服务运行,由服务控制管理器负责开机启动和失败恢复;应用端口只绑定到回环地址,不直接暴露给外部网络。

以下方案以 Windows Server 2019/2022/2025、IIS、URL Rewrite、Application Request Routing(ARR)和 WinSW 为例。操作前应核对具体系统版本、应用启动方式以及 URL Rewrite、ARR 与当前 IIS 的兼容性。ASP.NET Core 等原生支持 Windows 服务的应用,应优先使用框架自身的服务托管能力,不必额外套一层服务包装器。

先核对现状与影响范围

不要在生产服务器上直接安装组件、修改绑定或覆盖 web.config。先确认端口占用、现有 IIS 站点、证书和应用健康状态。

以管理员身份打开 PowerShell,执行:

Get-ComputerInfo |
    Select-Object WindowsProductName, WindowsVersion, OsBuildNumber

Get-WindowsFeature Web-Server, Web-Mgmt-Console, Web-WebSockets

Get-Service W3SVC, WAS -ErrorAction SilentlyContinue

Get-NetTCPConnection -State Listen -ErrorAction SilentlyContinue |
    Where-Object { $_.LocalPort -in 80, 443, 3000 } |
    Select-Object LocalAddress, LocalPort, OwningProcess

Import-Module WebAdministration -ErrorAction SilentlyContinue
Get-Website
Get-WebBinding

其中 3000 只是本文的后端示例端口,应替换为应用的真实监听端口。检查结果需要回答以下问题:

  • 80、443 是否已经被其他 IIS 站点或非 IIS 程序占用。
  • 目标域名是否已有重复的主机名绑定。
  • 后端应用能否独立启动并响应健康检查。
  • 应用是否把上传文件、SQLite 数据库或运行时文件写在发布目录。
  • 当前证书是否包含目标域名、私钥和完整证书链。
  • 应用是否依赖 WebSocket、大文件上传或长时间请求。
  • 服务器上是否存在其他共用 IIS 的业务。

如果后端已经运行,先绕过代理直接检查:

Test-NetConnection 127.0.0.1 -Port 3000

Invoke-WebRequest `
    -Uri "http://127.0.0.1:3000/health" `
    -UseBasicParsing

应用没有 /health 时,可以临时检查一个不会修改数据的普通页面。健康检查接口不应执行耗时查询,也不应返回密钥、连接字符串和详细异常。

变更前完成配置与文件备份

IIS 配置备份只覆盖 IIS 配置,不包含应用文件、证书私钥和数据库。它们需要分别处理。

先创建 IIS 全局配置备份:

$backupName = "Pre-WebProxy-{0}" -f (Get-Date -Format "yyyyMMdd-HHmmss")

& "$env:windir\System32\inetsrv\appcmd.exe" `
    add backup $backupName

& "$env:windir\System32\inetsrv\appcmd.exe" `
    list backups

如果目标站点已有 web.config,单独复制一份:

$backupDir = "C:\Backup\WebProxy\$(Get-Date -Format 'yyyyMMdd-HHmmss')"

New-Item -ItemType Directory -Path $backupDir -Force | Out-Null

Copy-Item `
    -Path "C:\Sites\WebProxy\web.config" `
    -Destination $backupDir `
    -ErrorAction SilentlyContinue

应用发布文件建议采用版本目录,而不是直接覆盖正在运行的目录:

C:\Apps\MyWebApp\
├─ releases\
│  ├─ 20260910-1200\
│  └─ 20260912-1800\
├─ current\
├─ shared\
│  ├─ config\
│  ├─ logs\
│  └─ uploads\
└─ service\

配置、日志和上传文件放在 shared 中,可以避免版本回滚时被旧发布包覆盖。若应用包含数据库迁移,应另外制定数据库备份和向后兼容方案,不能把代码目录备份当作数据库回滚手段。

证书备份需要包含私钥,并保存在限制访问的安全位置。使用 PFX 时不要把密码写入脚本、工单或 Git 仓库。

让后端只监听本机地址

反向代理部署后,外部请求应只能到达 IIS。后端应用建议监听:

127.0.0.1:3000

不建议监听:

0.0.0.0:3000

具体配置方式取决于应用框架。例如 Node.js 应用需要在代码或环境变量中指定监听地址;ASP.NET Core 可以设置 URL;Java、Python 服务也应使用各自框架的绑定参数。

修改后再次检查:

Get-NetTCPConnection -State Listen |
    Where-Object { $_.LocalPort -eq 3000 } |
    Select-Object LocalAddress, LocalPort, OwningProcess

如果结果是 127.0.0.1::1,说明只接受本机连接。若结果是 0.0.0.0::,则应用仍在所有网卡上监听。此时不应仅依赖防火墙掩盖配置问题,而应优先修正应用监听地址。

将应用注册为受控 Windows 服务

进程守护不是简单写一个无限循环启动脚本。生产环境至少需要满足以下条件:

  • 服务器启动后自动拉起应用。
  • 应用进程异常退出后可以按策略重启。
  • 停止服务时允许应用完成连接释放和日志写入。
  • 服务使用低权限账户,而不是长期运行在管理员会话中。
  • 标准输出、错误输出和应用日志能够滚动保存。
  • 重启次数和退出原因可在服务状态、事件日志或监控中观察。

使用 WinSW 包装普通应用

从 WinSW 的正式发布渠道取得与系统架构匹配的程序,固定版本并校验文件来源。不同 WinSW 版本支持的配置项可能存在差异,应先在测试环境验证。

将可执行文件放入:

C:\Apps\MyWebApp\service\WebAppService.exe

并在同一目录创建同名配置文件:

C:\Apps\MyWebApp\service\WebAppService.xml

以下是 Node.js 应用示例,其他运行时需要替换 executablearguments

<service>
  <id>WebApp</id>
  <name>My Web Application</name>
  <description>Production web application backend</description>

  <executable>C:\Program Files\nodejs\node.exe</executable>
  <arguments>C:\Apps\MyWebApp\current\server.js</arguments>
  <workingdirectory>C:\Apps\MyWebApp\current</workingdirectory>

  <env name="NODE_ENV" value="production" />
  <env name="HOST" value="127.0.0.1" />
  <env name="PORT" value="3000" />

  <startmode>Automatic</startmode>
  <delayedAutoStart>true</delayedAutoStart>
  <stoptimeout>30 sec</stoptimeout>

  <logpath>C:\Apps\MyWebApp\shared\logs\service</logpath>
  <log mode="roll"></log>
</service>

不要把数据库密码、API 密钥和证书密码直接写进 XML。可根据应用能力使用受 ACL 保护的配置文件、Windows 凭据机制或企业密钥管理系统。

安装服务前,先验证应用命令可以在指定工作目录中正常启动。然后以管理员身份执行:

Set-Location "C:\Apps\MyWebApp\service"

.\WebAppService.exe install
.\WebAppService.exe start

Get-Service -Name WebApp

对于 ASP.NET Core 自包含发布,可以直接把 <executable> 指向应用的 .exe。如果程序已经按 Windows Service 模式开发,则直接使用其原生服务入口更合适。

设置服务失败恢复策略

WinSW 负责跟踪子进程,Windows 服务控制管理器负责失败恢复。下面的配置会在服务异常失败时分阶段重启:

sc.exe failure WebApp `
    reset= 86400 `
    actions= restart/5000/restart/15000/restart/60000

sc.exe failureflag WebApp 1

sc.exe qfailure WebApp

注意,失败恢复只能处理“进程退出”。如果应用进程仍在运行但已经死锁、线程池耗尽或无法响应请求,服务控制管理器不会自动发现。因此还需要外部健康检查和告警,不能把进程存在等同于业务可用。

使用低权限服务账户

默认使用 LocalSystem 会赋予应用过大的本机权限。更合理的做法是创建专用服务账户,并在“服务”管理器中为 WebApp 设置“登录”账户。

服务账户通常只需要:

  • 应用发布目录的读取和执行权限。
  • 日志、上传或缓存目录的修改权限。
  • 访问后端数据库或网络资源所需的最小权限。
  • “作为服务登录”的权限。

在授权前先查看现有 ACL:

icacls "C:\Apps\MyWebApp"
icacls "C:\Apps\MyWebApp\shared"

下面是授权形式示例,需要替换为实际账户,并根据目录用途分别授予权限:

icacls "C:\Apps\MyWebApp\current" `
    /grant "DOMAIN\svc_web:(OI)(CI)RX"

icacls "C:\Apps\MyWebApp\shared\logs" `
    /grant "DOMAIN\svc_web:(OI)(CI)M"

不要为了省事给应用目录授予 Everyone:F。修改服务登录账户后,应重新启动服务并检查事件查看器中的登录失败和权限错误。

安装并启用 IIS 反向代理组件

如果服务器尚未安装 IIS,可以通过 Windows Server PowerShell 添加角色:

Install-WindowsFeature `
    Web-Server, Web-Mgmt-Console `
    -IncludeManagementTools

只有业务需要 WebSocket 时,再添加对应功能:

Install-WindowsFeature Web-WebSockets

执行后检查结果中的 Restart Needed,不要在未确认影响范围时直接重启生产服务器。

随后安装与当前 IIS 兼容的 URL Rewrite 和 Application Request Routing。安装包应来自可核验的正式来源。完成后检查模块是否已载入:

& "$env:windir\System32\inetsrv\appcmd.exe" list modules |
    Select-String "Rewrite|ApplicationRequestRouting"

在 IIS 管理器的服务器级别进入 Application Request Routing Cache → Server Proxy Settings,勾选 Enable proxy。如应用需要获取原始主机名,应启用保留主机头,或者通过受控转发头传递。

也可以在已经完成 IIS 备份的前提下执行:

& "$env:windir\System32\inetsrv\appcmd.exe" `
    set config `
    -section:system.webServer/proxy `
    /enabled:"True" `
    /preserveHostHeader:"True" `
    /commit:apphost

这是服务器级配置,会影响同一 IIS 实例上的代理站点。共用 IIS 时必须先确认其他业务是否依赖不同设置。

创建独立代理站点

先检查目标名称和绑定不存在冲突,再创建站点目录、应用程序池和 HTTP 绑定:

Import-Module WebAdministration

New-Item `
    -ItemType Directory `
    -Path "C:\Sites\WebProxy" `
    -Force | Out-Null

New-WebAppPool -Name "WebProxyPool"

Set-ItemProperty `
    "IIS:\AppPools\WebProxyPool" `
    -Name managedRuntimeVersion `
    -Value ""

New-Website `
    -Name "WebProxy" `
    -PhysicalPath "C:\Sites\WebProxy" `
    -ApplicationPool "WebProxyPool" `
    -Protocol http `
    -Port 80 `
    -HostHeader "www.example.com"

www.example.com 替换为真实域名。若服务器已有同名站点或绑定,不要重复运行创建命令,应修改现有站点或使用新的变更方案。

C:\Sites\WebProxy\web.config 中写入:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Redirect HTTP to HTTPS" stopProcessing="true">
          <match url="(.*)" />
          <conditions>
            <add input="{HTTPS}" pattern="off" />
          </conditions>
          <action
            type="Redirect"
            url="https://{HTTP_HOST}/{R:1}"
            redirectType="Permanent" />
        </rule>

        <rule name="Reverse Proxy to WebApp" stopProcessing="true">
          <match url="(.*)" />
          <serverVariables>
            <set name="HTTP_X_FORWARDED_PROTO" value="https" />
          </serverVariables>
          <action
            type="Rewrite"
            url="http://127.0.0.1:3000/{R:1}"
            appendQueryString="true" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

该配置有两个动作:HTTP 请求先重定向到 HTTPS;进入 HTTPS 站点的请求再转发到本机 3000 端口。

使用 HTTP_X_FORWARDED_PROTO 前,需要在 IIS 管理器的服务器级 URL Rewrite 设置中,将它加入允许的服务器变量列表。否则站点可能因不允许设置该变量而返回 500 错误。

应用还需要正确识别代理头。启用“信任代理”时,应把信任范围限制在本机代理,而不是无条件信任来自公网的 X-Forwarded-ForX-Forwarded-Proto。后端只监听回环地址,正是建立这一信任边界的重要前提。

导入证书并建立 HTTPS 绑定

PFX 文件应包含证书私钥。导入前先确认目标域名、有效期和证书链。

$pfxPassword = Read-Host "请输入PFX密码" -AsSecureString

$cert = Import-PfxCertificate `
    -FilePath "C:\Secure\www.example.com.pfx" `
    -CertStoreLocation "Cert:\LocalMachine\My" `
    -Password $pfxPassword

$cert |
    Format-List Subject, Thumbprint, NotBefore, NotAfter, HasPrivateKey

确认 HasPrivateKeyTrue 后创建 SNI HTTPS 绑定:

Import-Module WebAdministration

New-WebBinding `
    -Name "WebProxy" `
    -Protocol https `
    -Port 443 `
    -HostHeader "www.example.com" `
    -SslFlags 1

New-Item `
    "IIS:\SslBindings\0.0.0.0!443!www.example.com" `
    -Value $cert `
    -SSLFlags 1

如果目标 Windows Server 上的 WebAdministration 提供程序行为不同,可以在 IIS 管理器的“站点 → 绑定”中完成证书关联。绑定前必须检查是否已存在相同 IP、端口和主机名组合,避免覆盖其他站点。

证书部署完成后还要记录续期负责人、到期告警和更换流程。仅导入新证书但没有更新 IIS 绑定,并不会自动完成证书切换。

日志、超时与资源边界怎么设置

生产环境至少要保留四类观察入口:

日志类型 常见位置或入口 主要用途
IIS 访问日志 C:\inetpub\logs\LogFiles\W3SVC* 查看状态码、子状态码、请求耗时和客户端信息
HTTP.sys 错误日志 C:\Windows\System32\LogFiles\HTTPERR 检查请求是否在进入 IIS 前被拒绝或中断
Windows 事件日志 事件查看器的 Application、System 检查服务退出、IIS、WAS、证书和权限错误
应用及服务包装日志 C:\Apps\MyWebApp\shared\logs 查看应用异常、启动失败和标准错误输出

日志必须配置滚动与保留策略。磁盘预算可以按以下公式估算:

日志预留空间 = 每日增长量 × 保留天数 × 安全系数

例如观察到日志每日约增长 1.5 GB,保留 14 天,安全系数取 1.3,则至少需要:

1.5 × 14 × 1.3 ≈ 27.3 GB

这只是计算示例,不是所有服务器的建议值。每日增长量应从本机实际日志变化中取得。

ARR 超时必须与应用行为一致。普通页面接口不应设置没有边界的长超时;文件导出、上传和 WebSocket 则不能照搬短请求配置。设置时需要满足:

  • ARR 等待时间应覆盖应用正常请求时间,但不能无限放大。
  • 应用自身超时应小于上游调用的整体超时预算。
  • 数据库、外部 API 和代理不能各自无限重试。
  • 上传大小限制要同时核对 IIS、应用框架和业务接口。
  • WebSocket 业务需要安装 IIS WebSocket 功能,并验证连接升级与空闲超时。

资源边界也不能只看 IIS 应用程序池,因为真实业务进程运行在独立 Windows 服务中。

资源 建议观察项 达到边界时的处理方向
CPU 持续利用率、请求耗时、进程数 查找计算热点,避免用无限重启代替性能修复
内存 工作集、私有字节、提交量、分页 设置运行时合理上限,排查泄漏和大对象
磁盘 日志增长、临时文件、剩余空间、I/O 延迟 设置滚动、清理策略和容量告警
连接 活跃连接、队列、后端连接失败 核对线程池、连接池、代理超时和下游容量
重启次数 服务退出码、单位时间重启次数 触发告警,避免形成无休止重启循环

进程守护只能恢复进程,不能解决资源耗尽。服务连续重启时,应停止自动扩散影响并保留现场日志,而不是不断缩短重启间隔。

按层验证代理与守护是否生效

变更完成后按“后端进程、IIS 本机、HTTPS 域名、外部客户端”的顺序检查,便于快速定位失败发生在哪一层。

检查服务和后端端口

Get-Service WebApp

Get-NetTCPConnection -State Listen |
    Where-Object { $_.LocalPort -eq 3000 }

Invoke-WebRequest `
    -Uri "http://127.0.0.1:3000/health" `
    -UseBasicParsing

成功条件是服务处于 Running,端口绑定到回环地址,健康检查返回预期状态。

检查 IIS 配置与绑定

Get-Website -Name "WebProxy"
Get-WebBinding -Name "WebProxy"

& "$env:windir\System32\inetsrv\appcmd.exe" `
    list config "WebProxy"

不要为了让配置“生效”直接执行 iisreset。大部分 IIS 配置可以动态加载,iisreset 会影响同一服务器上的其他站点。确需重启应用程序池时,只操作目标池:

Restart-WebAppPool -Name "WebProxyPool"

在 DNS 切换前验证 HTTPS

系统存在 curl.exe 时,可以在服务器上将域名临时解析到本机,绕过正式 DNS:

curl.exe `
    --resolve www.example.com:443:127.0.0.1 `
    -I `
    https://www.example.com/health

同时验证 HTTP 是否跳转:

curl.exe `
    --resolve www.example.com:80:127.0.0.1 `
    -I `
    http://www.example.com/health

检查重点包括:

  • HTTP 是否重定向到正确的 HTTPS 域名。
  • HTTPS 证书是否匹配域名且证书链正常。
  • 代理后的页面、静态资源和接口是否可用。
  • 查询字符串是否完整传递。
  • 应用生成的跳转链接是否仍为 HTTPS。
  • 客户端 IP、主机名和协议识别是否符合预期。
  • WebSocket、上传和长请求是否按业务需要工作。

验证失败恢复

直接停止 Windows 服务属于正常停止,通常不会触发“失败恢复”。如果需要验证崩溃恢复,应在维护窗口内先准确找到目标子进程,确认没有匹配到其他业务:

Get-CimInstance Win32_Process |
    Where-Object {
        $_.CommandLine -like "*C:\Apps\MyWebApp\current\server.js*"
    } |
    Select-Object ProcessId, Name, CommandLine

只有在确认 PID、允许短暂中断且已有回滚手段时,才可以终止该特定测试进程,再观察服务是否按策略恢复。不要使用模糊名称一次性结束服务器上的全部 node.exejava.exepython.exe

常见失败对应的检查位置

现象 优先检查 常见原因
IIS 返回 502 后端端口、WebApp 服务、应用日志、ARR 超时 应用未启动、监听地址错误、端口错误或后端超时
IIS 返回 500 IIS 日志、事件日志、web.config XML 格式错误、模块缺失、转发变量未加入允许列表
出现 HTTPS 重定向循环 应用代理信任设置、X-Forwarded-Proto 应用始终认为原始请求是 HTTP
域名返回错误站点 IIS 绑定和 SNI 设置 主机名冲突、HTTPS 绑定未设置域名
证书不匹配 HTTPS 绑定、证书主题和 SAN 绑定了错误证书或访问域名不在证书中
服务启动后立即停止 WinSW 日志、Application 日志、服务账户权限 启动命令错误、工作目录错误、配置缺失或无读取权限
服务运行但请求无响应 应用健康检查、线程和连接池、下游依赖 死锁、资源耗尽或数据库、外部接口阻塞
WebSocket 频繁断开 IIS WebSocket 功能、代理和应用日志 功能未安装、升级请求失败或空闲超时不匹配

处理 502 时,不要先重装 IIS。先直接请求 127.0.0.1:3000:如果直连也失败,问题在应用或服务;如果直连成功而代理失败,再检查 ARR、Rewrite、站点绑定和超时设置。

上线观察窗口与回滚触发条件

切流后应持续观察 IIS 状态码、后端健康检查、服务重启次数、CPU、内存、磁盘和应用错误。旧应用版本、旧代理配置和原证书绑定不要立即删除。

出现以下情况之一,应暂停继续扩大流量并评估回滚:

  • 5xx 明显持续增加,且无法在观察窗口内定位。
  • WebApp 服务进入连续重启状态。
  • 应用健康检查间歇性失败或响应时间持续恶化。
  • HTTPS 证书、域名跳转或登录回调异常。
  • CPU、内存、磁盘或连接数触及既定资源边界。
  • 新版本包含不可逆的数据结构变更,而兼容性尚未确认。

应用回滚时,先将服务切回上一个发布目录,再重启 WebApp 并验证本机健康检查。IIS 配置需要整体恢复时,可使用变更前创建的备份:

& "$env:windir\System32\inetsrv\appcmd.exe" list backups

确认备份名称和影响范围后,才执行恢复:

& "$env:windir\System32\inetsrv\appcmd.exe" `
    restore backup "Pre-WebProxy-YYYYMMDD-HHMMSS"

该操作恢复的是 IIS 全局配置,可能撤销备份后其他站点的合法变更。共用 IIS 环境应优先恢复目标站点的 web.config、绑定和代理设置,不要未经核对直接恢复全局备份。回滚完成后仍需重新验证后端直连、HTTP 跳转、HTTPS 证书、代理请求和服务恢复状态,并保留故障期间的日志供后续分析。

上一篇 PHP-FPM是进程管理器还是PHP解释器:作用范围与判断边界解析

LHIDC 产品中心

继续查看可购买的海外服务器产品

文章用于辅助选型,最终价格、库存与配置请以产品详情页和下单页面展示为准。

查看产品 查看方案