Qlik Sense Reverse Proxy Config for IIS

This is a copy of a working version of an iis reverse proxy in front of Qlik integrated with SAML Add these to the allowed server variables section

  • CAPTURED_ORIGIN
  • HTTP_ACCEPT_ENCODING
  • HTTP_X_ORIGINAL_ACCEPT_ENCODING
  • ORIGINAL_HOST

Additional Replacements

  • internalHost and externalHost appropriately

If you get errors…

Engine errors usually mean you have not enabled websockets, or whitelisted the frontend hostname, so be sure you have websockets and url rewrite enabled

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.web>
    <httpRuntime enableVersionHeader="false" />
  </system.web>
  <system.webServer>
    <rewrite>
      <outboundRules>
                <clear />
                <rule name="QlikOutboundProxyPreserveResponseLocation" preCondition="IsRedirect">
                    <match serverVariable="RESPONSE_Location" pattern="^http(s)?://internalHost(.*)" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="true" />
                    <action type="Rewrite" value="http{R:1}://externalHost{R:2}" />
                </rule>
                <rule name="QlikOutboundProxyRestoreAcceptEncoding" preCondition="NeedsRestoringAcceptEncoding">
                    <match serverVariable="HTTP_ACCEPT_ENCODING" pattern="^(.*)" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="true" />
                    <action type="Rewrite" value="{HTTP_X_ORIGINAL_ACCEPT_ENCODING}" />
                </rule>
                <rule name="QlikOutboundProxyMainReverseProxyOutboundRule" preCondition="ResponseIsHtml2">
                    <match filterByTags="A, Area, Base, Form, Frame, Head, IFrame, Img, Input, Link, Script" pattern="^http(s)?://internalHost(.*)" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="true" />
                    <action type="Rewrite" value="http{R:1}://externalHost{R:2}" />
                </rule>   
        <preConditions>
          <preCondition name="IsRedirect" logicalGrouping="MatchAny">
                        <add input="{RESPONSE_STATUS}" pattern="^30[1237]$" />
          </preCondition>
                    <preCondition name="NeedsRestoringAcceptEncoding">
                        <add input="{HTTP_X_ORIGINAL_ACCEPT_ENCODING}" pattern=".+" />
                    </preCondition>
                       <preCondition name="ResponseIsHtml2">
                        <add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
                    </preCondition>
        </preConditions>
      </outboundRules>
    <rules> 
        <rule name="QlikReverseProxyInboundRule" stopProcessing="true">
            <match url="(.*)" />
            <conditions>
                <add input="{CACHE_URL}" pattern="^(https?)://" />
            </conditions>
            <action type="Rewrite" url="{C:1}://internalHost/{R:1}" appendQueryString="true" logRewrittenUrl="true" />
            <serverVariables>
                <set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCODING}" />
                <set name="HTTP_ACCEPT_ENCODING" value="" />
            </serverVariables>
        </rule>
    </rules>
     <rewriteMaps>
                <rewriteMap name="AllowedOrigins">
                    <add key="internalHost" value="externalHost" />
                    <add key="externalHost" value="externalHost" />
                    <add key="https://internalHost" value="https://externalHost" />
                    <add key="https://externalHost" value="https://externalHost" />
                </rewriteMap>
            </rewriteMaps> 
    </rewrite>
    <caching enabled="false" enableKernelCache="false" />
    <httpProtocol>
      <customHeaders>
        <remove name="X-Powered-By" />
        <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
         <add name="Access-Control-Allow-Methods" value="POST,GET,OPTIONS,PUT,DELETE,UPDATE,PATCH" />
                <add name="Access-Control-Allow-Origin" value="https://externalHost, wss://internalHost" />
                <add name="Vary" value="Origin" />
      </customHeaders>
    </httpProtocol>
    <security>
      <requestFiltering>
        <verbs>
          <!-- <add verb="OPTIONS" allowed="false" /> -->
        </verbs>
      </requestFiltering>
    </security>
        <!-- <tracing>
            <traceFailedRequests>
                <add path="*">
                    <traceAreas>
                        <add provider="ASP" verbosity="Verbose" />
                        <add provider="ASPNET" areas="Infrastructure,Module,Page,AppServices" verbosity="Verbose" />
                        <add provider="ISAPI Extension" verbosity="Verbose" />
                        <add provider="WWW Server" areas="Authentication,Security,Filter,StaticFile,CGI,Compression,Cache,RequestNotifications,Module,FastCGI,WebSocket" verbosity="Verbose" />
                    </traceAreas>
                    <failureDefinitions statusCodes="300-999" verbosity="Warning" />
                </add>
            </traceFailedRequests>
        </tracing> -->
        <urlCompression doStaticCompression="false" doDynamicCompression="false" />
  </system.webServer>
</configuration>