Score:0

How to deploy a server side rendering (SSR) react application (bundled by webpack) on windows server 2016 / IIS

cn flag

Just a heads up: This is the first time I try to deploy a react application on a custom built Windows server, but I’ve managed to do it successfully on Heroku and Linux (PM2), so I know that the application architecture is suppose to work correctly.

The scenario:

I’ve built a Windows Server 2016 / 64 bits hosting server to host multiple websites. I’ve used VPS Contabo to do so. I’ve tested all features that are suppose to work, even with other applications such as ASP.NET, PHP, SSL certificates and everything is working fine.

As for the specific node js project that I’m trying to host on this server, It consists of 2 main parts:

  • Backend in node together with a CMS, also developed in node / javascript.

I created a hosting space on a subdomain for this one and it’s working perfectly, even with the Let’s Encrypt SSL certificate. In case anyone wants to access it, would be on: https://backendnode.fullstackwebdesigner.com/system

  • Frontend in react server side rendering.

This is where the problem is occurring. If anyone would like to access it, here’s the link: https://fullstackwebdesigner.com/

I used basically the same technique for both of them:

  • iisnode;
  • URL rewrite extension;
  • iis node modules;
  • web.config file configuration;

The problem:

I’ve managed to get it to load as a website, just like I did with the backend, but the problem is that it seems not to load the CSS files, images and so on. So the layout doesn’t load. On the console, there’s and error message: Uncaught SyntaxError: Unexpected token '<'

The react application: As I’ve said before, it was done as a server side rendering application and bundled with webpack. So it builds the bundled files to a directory called “/build”. And in this directory, there’s a “/public” directory where all assets are in, like CSS files and images.

While developing, I would run the build on the terminal as: node build/bundle.react.js

And as strange as it may seem, when I run this on the terminal on the windows server, It works perfectly. But can only be accessed through: http://localhost:3001 It loads everything that’s suppose to load.

Here’s a simplified representation of the file structure:

- /build/
--bundle.react.js
--/build/public/
---/files-layout/
---/fonts/
---bundle.react.client.js

Here’s also the web.config file I’m using on the website hosting space for the react build:

<configuration>
    <system.webServer>
        <iisnode nodeProcessCommandLine="C:\Program Files\nodejs\node.exe" />
        
        <handlers>
            <add name="iisnode" path="/build/bundle.react.js" verb="*" modules="iisnode" />
        </handlers>

        <rewrite>
            <rules>
                <!-- Redirect to HTTPS (alternative). -->
                <rule name="Redirect to HTTPS / SSL" stopProcessing="true">
                    <match url="(.*)" />
                    <conditions>
                        <add input="{HTTPS}" pattern="off" ignoreCase="true" />
                    </conditions>
                    <action type="Redirect" redirectType="Found" url="https://{HTTP_HOST}/{R:1}" />
                </rule>

                <!-- Don't interfere with requests for logs. -->
                <rule name="LogFile" patternSyntax="ECMAScript" stopProcessing="true">
                    <match url="^[a-zA-Z0-9_\-]+\.js\.logs\/\d+\.txt$" />
                </rule> 
                <!-- Node. -->
                <rule name="sendToNode">
                    <match url="(.*)" />
                    <conditions>
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="/build/bundle.react.js" />          
                </rule>
            </rules>    
        </rewrite> 
        <defaultDocument>
            <files>
                <clear />
                <add value="/build/bundle.react.js" />
            </files>
        </defaultDocument>
        <security>
            <requestFiltering>
                <hiddenSegments>
                    <add segment="node_modules" />
                </hiddenSegments>
            </requestFiltering>
        </security>
    </system.webServer>
    <system.web>
        <compilation defaultLanguage="js" />
    </system.web>
</configuration>

Anyone has any idea of what could be missing or going on, as there hasn’t been many references on hosting server side rendering on windows server on the web? Maybe an extra configuration on IIS, web.config or on the website hosting space?

Edit:

An interesting test that I just did: on my local development computer, when I run through the terminal:

node bundle.react.js

from inside the /build folder, I get the same result as the problem that’s happening online (no layout, styles, images and so on).

But when I run through the terminal:

node build/bundle.react.js

from outside the /build folder (from the base directory), it loads perfectly.

On more thing. The base directories are organized like so:

…(some folders)
- /build/
--bundle.react.js
--/build/public/
---/files-layout/
---/fonts/
---bundle.react.client.js
-node_modules
…(some root files, like webpack and so on)

I’m guessing there’s some kind of problem referencing the /node_modules folder because of how I wrote the web.config file, but I have no idea on how should reference it.

Score:0
cn flag

Got it to work! The following link helped me make the final adjustments, along with the test I did: https://www.thecodehubs.com/how-to-deploy-ssr-angular-universal-to-iis/

Summing it up, turns out I had to copy the server bundle to the root directory. And change the web.config file to call the root file. Here’s how the web.config file ended up, along with some more configurations that I found interesting in the link I posted (didn’t test yet without the extra configuration):

<configuration>
    <system.webServer>
        <iisnode nodeProcessCommandLine="C:\Program Files\nodejs\node.exe" />
        
        <handlers>
            <add name="iisnode" path="bundle.react.js" verb="*" modules="iisnode" />
        </handlers>

        <rewrite>
            <rules>
                <!-- Redirect to HTTPS. -->
                <rule name="Redirect to HTTPS / SSL" stopProcessing="true">
                    <match url="(.*)" />
                    <conditions>
                        <add input="{HTTPS}" pattern="off" ignoreCase="true" />
                    </conditions>
                    <action type="Redirect" redirectType="Found" url="https://{HTTP_HOST}/{R:1}" />
                </rule>

                <!-- Don't interfere with requests for logs. -->
                <rule name="LogFile" patternSyntax="ECMAScript" stopProcessing="true">
                    <match url="^[a-zA-Z0-9_\-]+\.js\.logs\/\d+\.txt$" />
                </rule>
                
                <!-- Node. -->
                <rule name="sendToNode">
                    <match url="(.*)" />
                    <conditions>
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="bundle.react.js" />
                </rule>
                
                <rule name="StaticContent" stopProcessing="true">
                  <match url="([\S]+[.](jpg|jpeg|gif|css|png|js|ts|cscc|less|ico|html|map|svg))" />
                  <action type="None" />
                </rule>
            </rules>
        </rewrite> 
        
        <staticContent>
            <clientCache cacheControlMode="UseMaxAge" />
            <remove fileExtension=".svg" />
            <remove fileExtension=".eot" />
            <remove fileExtension=".ttf" />
            <remove fileExtension=".woff" />
            <remove fileExtension=".woff2" />
            <remove fileExtension=".otf" />
            <mimeMap fileExtension=".ttf" mimeType="application/octet-stream" />
            <mimeMap fileExtension=".svg" mimeType="image/svg+xml"  />
            <mimeMap fileExtension=".eot" mimeType="application/vnd.ms-fontobject" />
            <mimeMap fileExtension=".woff" mimeType="application/x-woff" />
            <mimeMap fileExtension=".woff2" mimeType="application/x-woff" />
            <mimeMap fileExtension=".otf" mimeType="application/otf" />
        </staticContent>
        
        
        <defaultDocument>
            <files>
                <clear />
                <add value="bundle.react.js" />
            </files>
        </defaultDocument>

        <security>
          <requestFiltering>
            <hiddenSegments>
              <add segment="node_modules" />
              <!--add segment="iisnode" /-->
            </hiddenSegments>
          </requestFiltering>
        </security>
    </system.webServer>
    <system.web>
        <compilation defaultLanguage="js" />
    </system.web>
</configuration>
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.