Serving Multiple Versions of react app with Nginx

Posted on Sunday · November 10 2019 · 12:45 PM | 416 words · 3 min read

The problem and Requirements

  • Need to serve different versions of react application
  • The react application is static content
  • Use nginx to route traffic (instead of running a node server)

Solution

The idea is to put every build into a different directory. The directory name should follow a patter which has its corresponding commit hash in it.

The directory structure will look kind of like this:

 |-src
     |-ver_bbc9dbc
     |  |-index.html
     |  |-main.js
     |-ver_fff890b
        |-index.html
        |-main.js

So, whenever user visits www.mysite.com/ver_bbc9dbc/ he will see the version from commit bbc9dbc.

Since, this is a single page app, we want our users to be redirected to correct route even when they are accessing internal routes. i.e. If a user visits www.mysite.com/ver_bbc9dbc/about/, he should be redirected to /about/ route of that specific SPA.

Nginx directives are very simple and yet so powerful that you can easily mess things up.

This is a simple locatino block, which will serve files from the root(i.e. src) directory.

location / {
    index index.html;
    try_files $uri $uri/ /index.html;
}

Now, we want to intercept requests made to URI which contains our pattern and serve appropriate index.html. For, that we use regex in the location directive and capture groups and leverage that to point to our desired index.html file.

location ~ /(ver_[a-z0-9]+) {
    index index.html;
    try_files $uri $uri/ /$1/index.html;
}

Now, the problem is when we try to visit www.mysite.com/ver_bbc9dbc/ the index.html page loads correctly, but the associated assets aren’t loading correctly. Instead of requesting www.mysite.com/ver_bbc9dbc/main.js it is requesting www.mysite.com/main.js.

An easy fix is to add a base tag in the index.html. But, since we are using CRA(create-react-app) and the static assets are injected dynamically, simply putting base tag won’t work. A quick look into CRA documentation would reveal that, CRA uses the homepage attribute in package.json to determine the base path of the app. Thus we add /ver_bbc9dbc to homepage and it works as intented.

Now, there is a problem. After the app is loaded, all the hyperlinks are messed up!

For example, if we visit www.mysite.com/ver_bbc9dbc/ we would see the app from commit bbc9dbc. But after that, all the hyperlinks are relative to root url and not /ver_bbc9dbc. As a result when you click about page, you’ll be redirected to www.mysite.com/about insteade of www.mysite.com/ver_bbc9dbc/about.

The reason for that is, we didn’t specify our base path in our react app. So, by default it is using the root /.

If you are using react-router, then add basename property.

<BrowserRouter
  basename={optionalString}
>
  <App />
</BrowserRouter>

see doc.

rakeen