Reverse Proxy Websockets in IIS
I have a React web application with a NodeJS backend served up with IIS (looks like version 10 on Windows Server 2016). I have gotten the I am trying to figure out how to correctly reverse proxy my websocket calls. I have a second Node server running just for handling my websockets. I have that running over https with a self-signed certficate. So I have tested websocket calls to my websocket node server from Postman on my computer, a seperate computer from our web server. URL looks like: 'wss://example.domain.local:3010' and those work just fine. Now when I am on the webserver itself and I run the web app with with websocket calls to 'wss://localhost:3010', those work fine. So the issue happens when I run the web app on a remote client and it makes a websocket call to 'wss://example.domain.local:3010', it says "WebSocket connection failed".
Here is my entire web.config file:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<location path="index.html">
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Cache-Control" value="no-store, max-age=0" />
</customHeaders>
</httpProtocol>
</system.webServer>
</location>
<system.webServer>
<rewrite>
<rules>
<clear />
<rule name="WebSocketReverseProxyRule" enabled="true" stopProcessing="true">
<match url="wss://example.domain.local:3010(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false"></conditions>
<action type="Rewrite" url="wss://localhost:3010/{R:1}" />
<serverVariables>
<set name="HTTP_SEC_WEBSOCKET_EXTENSIONS" value="" />
</serverVariables>
</rule>
<rule name="HTTPS Redirect" enabled="true" 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="Redirect to HTTP" enabled="false" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTPS}" pattern="^ON$" />
</conditions>
<action type="Redirect" url="http://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
</rule>
<rule name="Static Assets" enabled="true" stopProcessing="true">
<match url="([\S]+[.](html|htm|svg|js|css|png|gif|jpg|jpeg|json))" />
<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:3008/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:3008/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:3008/{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>
<outboundRules>
<rule name="ReverseProxyOutboundRule1" preCondition="ResponseIsTextHtml" enabled="true" stopProcessing="false">
<match filterByTags="None" pattern="^(.*?)\s" />
<action type="Rewrite" value="{R:1}" />
</rule>
<rule name="RestoreAcceptEncoding" preCondition="NeedsRestoringAcceptEncoding" enabled="true">
<match serverVariable="HTTP_ACCEPT_ENCODING" pattern="^(.*)" />
<action type="Rewrite" value="{HTTP_X_ORIGINAL_ACCEPT_ENCODING}" />
</rule>
<rule name="AnchorTagsRule" preCondition="ResponseIsTextAnything" enabled="false">
<match pattern="href=(.*?)https://example.domain.local:3008/(.*?)\s" />
<action type="Rewrite" value="href={R:1}https://example.domain.local/{R:2}" />
</rule>
<rule name="FormTagsRule" preCondition="ResponseIsTextAnything" enabled="false">
<match pattern="action=(.*?)https://example.domain.local:3008/(.*?)\\" />
<action type="Rewrite" value="action={R:1}https://example.domain.local/{R:2}\" />
</rule>
<preConditions>
<preCondition name="ResponseIsTextHtml">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
<preCondition name="ResponseIsTextAnything">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/(.+)" />
</preCondition>
<preCondition name="NeedsRestoringAcceptEncoding">
<add input="{HTTP_X_ORIGINAL_ACCEPT_ENCODING}" pattern=".*" />
</preCondition>
</preConditions>
</outboundRules>
</rewrite>
<urlCompression doStaticCompression="false" />
<tracing>
<traceFailedRequests>
<add path="*">
<traceAreas>
<add provider="WWW Server" areas="Rewrite,RequestRouting" verbosity="Verbose" />
</traceAreas>
<failureDefinitions statusCodes="200-399" />
</add>
</traceFailedRequests>
</tracing>
</system.webServer>
</configuration>