You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

配置反向代理的HTTPS IIS站点中Secure WebSocket连接失败

问题分析与解决方案

你的WebSocket反向代理配置核心错误在于match url匹配规则不符合IIS Rewrite逻辑:IIS的<match url>仅匹配请求的路径部分(不含协议、域名),但你写了完整的wss://example.domain.local/ws(.*),导致规则无法命中实际请求。同时,WebSocket握手需要特定HTTP头支持,原规则缺少相关判断与头传递配置。

修正后的WebSocket反向代理规则

替换原web.config中的WS reverse proxy规则为以下内容:

<rule name="WS reverse proxy" enabled="true" stopProcessing="true">
    <match url="^ws(.*)" />
    <conditions>
        <add input="{HTTP_UPGRADE}" pattern="^WebSocket$" />
        <add input="{HTTP_CONNECTION}" pattern="^Upgrade$" />
    </conditions>
    <serverVariables>
        <set name="HTTP_UPGRADE" value="websocket" />
        <set name="HTTP_CONNECTION" value="Upgrade" />
        <set name="HTTP_SEC_WEBSOCKET_EXTENSIONS" value="{HTTP_SEC_WEBSOCKET_EXTENSIONS}" />
    </serverVariables>
    <action type="Rewrite" url="ws://example.domain.local:3009/{R:1}" />
</rule>

关键修正点说明

  1. 正确匹配请求路径:^ws(.*)仅匹配以ws开头的请求路径(对应前端发起的wss://example.domain.local/ws请求),符合IIS Rewrite的匹配规则。
  2. 精准识别WebSocket握手:通过HTTP_UPGRADE和HTTP_CONNECTION头判断,确保仅处理WebSocket握手请求,避免误匹配其他类型请求。
  3. 传递必要握手头:将WebSocket握手必需的Upgrade、Connection头原样传递给后端Node服务,同时保留SEC_WEBSOCKET_EXTENSIONS头(原配置清空该头可能导致握手失败)。

额外注意事项

  • 确保IIS已安装Application Request Routing (ARR) 和WebSocket Protocol模块:
    • ARR是反向代理核心组件,需通过IIS管理器的“Web平台安装程序”搜索安装。
    • WebSocket模块需在服务器管理器的“添加角色和功能”中,勾选“Web服务器->应用程序开发”下的“WebSocket协议”。
  • 规则顺序:保持WS reverse proxy规则在所有规则最顶部,确保WebSocket请求优先被处理,不会被静态资源、React路由等规则拦截。

修正后的完整rules段

若需要替换整个rules部分,可参考以下内容:

<rules>
    <clear />
    <rule name="WS reverse proxy" enabled="true" stopProcessing="true">
        <match url="^ws(.*)" />
        <conditions>
            <add input="{HTTP_UPGRADE}" pattern="^WebSocket$" />
            <add input="{HTTP_CONNECTION}" pattern="^Upgrade$" />
        </conditions>
        <serverVariables>
            <set name="HTTP_UPGRADE" value="websocket" />
            <set name="HTTP_CONNECTION" value="Upgrade" />
            <set name="HTTP_SEC_WEBSOCKET_EXTENSIONS" value="{HTTP_SEC_WEBSOCKET_EXTENSIONS}" />
        </serverVariables>
        <action type="Rewrite" url="ws://example.domain.local:3009/{R:1}" />
    </rule>
    <rule name="HTTPS Redirect" stopProcessing="true">
        <match url="(.*)" />
        <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
            <add input="{HTTPS}" pattern="^OFF$" />
        </conditions>
        <action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" appendQueryString="false" />
    </rule>
    <rule name="Static Assets" enabled="true" stopProcessing="true">
        <match url="([\S]+[.](html|htm|svg|js|css|png|gif|jpg|jpeg))" />
        <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
        <action type="Rewrite" url="/{R:1}" />
    </rule>
    <rule name="Reverse Proxy Thumbnails to Node Server" enabled="true" stopProcessing="true">
        <match url="^thumbs/(.*)" />
        <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
        <serverVariables>
            <set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCODING}" />
            <set name="HTTP_ACCEPT_ENCODING" value="" />
        </serverVariables>
        <action type="Rewrite" url="http://example.domain.local:3009/thumbs/{R:1}" />
    </rule>
    <rule name="Reverse Proxy Images to Node Server" enabled="true" stopProcessing="true">
        <match url="^images/(.*)" />
        <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
        <serverVariables>
            <set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCODING}" />
            <set name="HTTP_ACCEPT_ENCODING" value="" />
        </serverVariables>
        <action type="Rewrite" url="http://example.domain.local:3009/images/{R:1}" />
    </rule>
    <rule name="Reverse Proxy API Calls to Node Server" enabled="true" stopProcessing="true">
        <match url="^api/(.*)" />
        <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
        <serverVariables>
            <set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCODING}" />
            <set name="HTTP_ACCEPT_ENCODING" value="" />
        </serverVariables>
        <action type="Rewrite" url="http://example.domain.local:3009/{R:1}" logRewrittenUrl="true" />
    </rule>
    <rule name="React Routes" stopProcessing="true">
        <match url=".*" />
        <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
            <add input="{REQUEST_URI}" matchType="Pattern" pattern="api/(.*)" negate="true" />
        </conditions>
        <action type="Rewrite" url="/index.html" />
    </rule>
    <!--rule name="SSL" patternSyntax="ExactMatch" stopProcessing="true">
        <match url="*" />
        <conditions>
            <add input="{HTTPS}" pattern="On" />
        </conditions>
        <action type="Rewrite" url="https://example.domain.local/{R:1}" />
    </rule-->
    <!--  this rule will route all requests through the index.html so React can load the requested URL correctly -->
</rules>

内容的提问来源于stack exchange,提问作者dmikester1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 11:17:03