通过 IIS URL Rewrite 还原原始请求协议(HTTPS)

2026-07-26#IIS#Windows#ASP.NET#CDN#HTTPS

将应用部署到 Kubernetes 集群时,通常会使用 Ingress Controller、Gateway 等组件完成 TLS 终止(TLS Termination),即客户端与 Ingress Controller 之间使用 HTTPS 协议,而 Ingress Controller 与服务之间使用 HTTP 协议。默认情况下,ASP.NET 会根据请求的协议来判断是否使用 HTTPS,但此时 ASP.NET 看到的请求是 HTTP,因此会误判为非 HTTPS 请求,从而导致一些功能无法正常工作,比如生成的重定向 URL 不正确,或者安全相关的功能失效。解决方法之一,就是使用 URL Rewrite 模块还原请求的协议。

安装 URL Rewrite 模块 🔗

Windows 镜像默认没有安装 URL Rewrite 模块,需要手动安装。可以从 IIS 官方网站 下载并安装 URL Rewrite 模块,然后根据请求头里的 X-Forwarded-Proto 值来还原请求的协议。

在 Dockerfile 中,可以添加以下指令来安装和配置:


RUN $ErrorActionPreference = 'Stop'; \
    Write-Host 'Downloading IIS URL Rewrite Module...'; \
    $msi = Join-Path $env:TEMP 'rewrite_amd64.msi'; \
    (New-Object Net.WebClient).DownloadFile('https://download.microsoft.com/download/1/2/8/128E2E22-C1B9-44A4-BE2A-5859ED1D4592/rewrite_amd64_en-US.msi', $msi); \
    Write-Host 'Installing URL Rewrite Module...'; \
    $proc = Start-Process msiexec.exe -ArgumentList '/i', $msi, '/quiet', '/norestart' -NoNewWindow -Wait -PassThru; \
    if ($proc.ExitCode -ne 0) { throw ('URL Rewrite install failed with exit code ' + $proc.ExitCode) }; \
    Remove-Item $msi -Force; \
    Write-Host 'Configuring global URL Rewrite rule for gateway TLS termination...'; \
    $config = 'C:\Windows\System32\inetsrv\config\applicationHost.config'; \
    [xml]$xml = Get-Content $config; \
    $webServer = $xml.configuration.'system.webServer'; \
    $rewrite = $webServer.rewrite; \
    if (-not $rewrite) { \
    $rewrite = $webServer.AppendChild($xml.CreateElement('rewrite')) \
    }; \
    $fragment = $xml.CreateDocumentFragment(); \
    $fragment.InnerXml = '<allowedServerVariables><add name=''HTTPS'' /><add name=''SERVER_PORT'' /></allowedServerVariables><globalRules><rule name=''Fix-Forwarded-Proto''><match url=''.*'' /><conditions><add input=''{HTTP_X_FORWARDED_PROTO}'' pattern=''https'' /></conditions><serverVariables><set name=''HTTPS'' value=''on'' /><set name=''SERVER_PORT'' value=''443'' /></serverVariables><action type=''None'' /></rule></globalRules>'; \
    $null = $rewrite.AppendChild($fragment); \
    $xml.Save($config); \
    Write-Host 'URL Rewrite rule successfully applied.'

配置的变化 🔗

使用上面的命令安装和配置 URL Rewrite 模块后,C:\Windows\System32\inetsrv\config\applicationHost.config 文件中会新增以下内容:

<configuration>
  <!-- 其他配置 -->
  <configSections>
    <sectionGroup name="system.webServer">
      <!-- 以下为新增 -->
      <sectionGroup name="rewrite">
        <section name="globalRules" overrideModeDefault="Deny" allowDefinition="AppHostOnly" />
        <section name="rules" overrideModeDefault="Allow" />
        <section name="outboundRules" overrideModeDefault="Allow" />
        <section name="providers" overrideModeDefault="Allow" />
        <section name="rewriteMaps" overrideModeDefault="Allow" />
        <section name="allowedServerVariables" overrideModeDefault="Deny" />
      </sectionGroup>
      <!-- 以上为新增 -->
    </sectionGroup>
    <!-- 其他 -->
  </configSections>

  <system.webServer>
    <!-- 其他配置 -->
    <globalModules>
      <!-- 以下为新增 -->
      <add name="RewriteModule" image="%SystemRoot%\system32\inetsrv\rewrite.dll" />
      <!-- 以上为新增 -->
    </globalModules>

    <tracing>
      <traceProviderDefinitions>
        <add name="WWW Server" guid="{......}">
          <areas>
            <!-- 以下为新增 -->
            <add name="Rewrite" value="1024" />
            <!-- 以上为新增 -->
          </areas>
        </add>
        <!-- 其他配置 -->
      </traceProviderDefinitions>
    </tracing>
    <!-- 以下为新增 -->
    <rewrite>
      <allowedServerVariables>
        <add name="HTTPS" />
        <add name="SERVER_PORT" />
      </allowedServerVariables>
      <globalRules>
        <rule name="Fix-Forwarded-Proto">
          <match url=".*" />
          <conditions>
            <add input="{HTTP_X_FORWARDED_PROTO}" pattern="https" />
          </conditions>
          <serverVariables>
            <set name="HTTPS" value="on" />
            <set name="SERVER_PORT" value="443" />
          </serverVariables>
          <action type="None" />
        </rule>
      </globalRules>
    </rewrite>
    <!-- 以上为新增 -->
    <modules>
      <!-- 以下为新增 -->
      <add name="RewriteModule" />
      <!-- 以上为新增 -->
    </modules>
  </system.webServer>
</configuration>

为何不在代码中实现? 🔗

在应用代码中根据 X-Forwarded-Proto 恢复请求协议当然也是可行的,例如在 ASP.NET 中使用 Forwarded Headers Middleware。

但在 IIS 场景下,某些重定向可能发生在请求进入应用之前。比较常见的是 IIS 对路径末尾斜杠(trailing slash)的规范化跳转,这类跳转由 IIS 直接返回响应,请求不会进入应用代码。

常见例子包括:

  1. 访问目录但 URL 没有末尾斜杠,例如 /docs 被规范化为 /docs/
  2. 访问子应用根路径但没有末尾斜杠,例如 /myapp 被规范化为 /myapp/
  3. 目录配合默认文档时,请求 /portal 往往会先跳转到 /portal/,再由 IIS 返回默认文档。

如果只在代码层处理协议恢复,这类“代码之外”的跳转仍可能使用 HTTP 视角生成结果,导致跳转协议不符合预期。将协议恢复放在 IIS URL Rewrite 层处理,可以更早生效,覆盖这类前置处理路径。