Setting up reverse proxy for Node server in IIS

mdodge 21 Reputation points
2023-02-24T20:04:53.78+00:00

I found out I need to secure our local web app on our local intranet because of this issue: https://github.com/mabelanger/react-html5-camera-photo#required-working-environment-for-getusermedia

I have the front-end correctly secured loading on HTTPS. Now I learned I don't need to secure the backend if I use a reverse proxy.

So I have been following this thorough tutorial on setting up a reverse proxy in IIS for our Node server. https://techcommunity.microsoft.com/t5/iis-support-blog/setup-iis-with-url-rewrite-as-a-reverse-proxy-for-real-world/ba-p/846222#M343

I thought I did everything correctly, but for some reason, all my api calls are sending back a response that is just the front end react site: <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport"...

If this would help, here is my entire web.config, the actual URL is not example.domain.local.


<?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>
				<!--  this rule will route all requests through the index.html so React can load the requested URL correctly -->
				<rule name="React Routes" stopProcessing="true">
					<match url=".*" />
					<conditions logicalGrouping="MatchAll">
					  <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
					  <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
					</conditions>
					<action type="Rewrite" url="/" />
				</rule>
                <rule name="ReverseProxyInboundRule1" stopProcessing="true">
                    <match url="^https://example.domain.local/api/(.*)" />
                    <action type="Rewrite" url="http://example.domain.local:3008/{R:1}" logRewrittenUrl="true" />
                    <serverVariables>
                        <set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCODING}" />
                        <set name="HTTP_ACCEPT_ENCODING" value="" />
                    </serverVariables>
                </rule>
            </rules>
            <outboundRules>
                <rule name="ReverseProxyOutboundRule1" preCondition="ResponseIsHtml1">
                    <match filterByTags="None" pattern="^http://example.domain.local:3008/(.*?)\s" />
                    <action type="Rewrite" value="https://example.domain.local/{R:2}" />
                </rule>
                <rule name="RestoreAcceptEncoding" preCondition="NeedsRestoringAcceptEncoding">
                    <match serverVariable="HTTP_ACCEPT_ENCODING" pattern="^(.*)" />
                    <action type="Rewrite" value="{HTTP_X_ORIGINAL_ACCEPT_ENCODING}" />
                </rule>
                <rule name="AnchorTagsRule" preCondition="ResponseIsHtml1">
                    <match pattern="href=(.*?)http://example.domain.local:3008/(.*?)\s" />
                    <action type="Rewrite" value="href={R:1}https://example.domain.local/{R:2}" />
                </rule>
                <rule name="FormTagsRule" preCondition="ResponseIsHtml1">
                    <match pattern="action=(.*?)http://example.domain.local:3008/(.*?)\\" />
                    <action type="Rewrite" value="action={R:1}https://example.domain.local/{R:2}\" />
                </rule>
                <preConditions>
                    <preCondition name="ResponseIsHtml1">
                        <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>

Windows development Internet Information Services
{count} votes

1 answer

Sort by: Most helpful
  1. Yurong Dai-MSFT 2,846 Reputation points Microsoft External Staff
    2023-02-28T02:01:26.36+00:00

    Hi @mdodge,

    For rule patterns:

    In the URL rewriting module of IIS, only when the URL of the rule pattern matches successfully, other rules can play a role. The matching URL setting of the rule pattern is mainly to match the path part in the URL. Assuming that the URL address of the current website is https://example.domain.local/api/test, the matching part of the rule pattern is api/test.

    So I think <match url="^https://example.domain.local/api/(.*)" /> should be changed to <match url="^api/(.*)" />

    For rule evaluation order:

    Each configuration level in IIS can have zero or more rewrite rules defined. The rules are evaluated in the same order in which they are specified. The URL Rewrite Module processes the set of rules by using the following algorithm:

    1. First, the URL is matched against the pattern of a rule. If it does not match, the URL Rewrite Module immediately stops processing that rule, and goes on to the next rule.
    2. If a pattern matches and there are no conditions for the rule, the URL Rewrite Module performs the action specified for this rule and then goes on to the next rule, where it uses the substituted URL as an input for that rule.
    3. If a pattern matches and there are conditions for the rule, the URL Rewrite Module evaluates the conditions. If the evaluation is successful, the specified rule action is performed, and then the rewritten URL is used as input to the subsequent rule

    A rule may have the StopProcessing flag turned on. When the rule action is performed (i.e. the rule matched) and this flag is turned on, it means that no more subsequent rules will be processed and the request will be passed to the IIS request pipeline. By default, this flag is turned off.

    I see that stopProcessing="true" is set in your React Routes rule, so this causes subsequent rules not to be processed after that React rule is processed, what you need to do is change this setting to stopProcessing="false" (That is, uncheck the Stop processing of subsequent rules in the IIS rewrite module).

    You can learn more from this official Microsoft document: URL Rewrite Module Configuration Reference


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the email notification for this thread.

    Best regards,

    Yurong Dai

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.