0

I'm trying to find a way how to host multiple (currently 2) React applications, each created with CRA. Both apps should run behind a single instance of NGINX and be accessible in a separate directory:

app1 -> http://localhost/app1

app2 -> http://localhost/app2

The 2 React applications where created like this:

npx create-react-app app1
npx create-react-app app2

To host both React applications using NGINX, the production build of each CRA app (npm run build) is copied to NGINX static directory /var/www/app[1|2]

# App1
npm run build
cp build/* /var/www/app1

# App2
npm run build
cp build/* /var/www/app2

This is how NGINX can be setup to host a single CRA app:

# /etc/nginx/conf.d/default.conf
server {
    listen 80;
    listen [::]:80;

    server_name localhost;

    location / {
        root /var/www/app1;
        index index.html;
    }
}

Now I'm trying to extend this example so both CRA apps are hosted by one NGINX.

Which modifications are necessary, both to NGINX site.conf and each React application itself?

I pushed a code showing my (incomplete) example above to Github for reference: https://github.com/wolkenarchitekt/multiple-cra-apps-behind-nginx. The code is using docker-compose for simplicity, but in the end the whole stack should run without Docker, so running separate Docker services for each React app is not an option.

Wolkenarchitekt
  • 18,653
  • 29
  • 105
  • 169

1 Answers1

1

In order to run both React apps on the same domain but different subfolders, you should just need two location blocks:

root /var/www;
location /app1/ {
        index index.html;
    }
location /app2/ {
        index index.html;
    }

The necessary changes in the React apps usually include:

  • changing the homepage field in the package.json as described here
  • (only applies if react-router is used, older versions should work similarly) change the basename for BrowserRouterto your subdirectory: <BrowserRouter basename='/app1'>

EDIT:

After checking out your repo (before you pushed the new branch) I just got it working with the following Dockerfile (way worse for development since every code change requires a rebuild, I just like multistage builds and this would be more suited for a production environment):

FROM node:16 as builder1
WORKDIR /var/app
COPY app1/package.json .
COPY app1/package-lock.json .
RUN npm i
COPY app1/public/ ./public
COPY app1/src/ ./src
RUN npm run build

FROM node:16 as builder2
WORKDIR /var/app
COPY app2/package.json .
COPY app2/package-lock.json .
RUN npm i
COPY app2/public/ ./public
COPY app2/src/ ./src
RUN npm run build


FROM nginx:mainline-alpine
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
COPY ./nginx/site.conf /etc/nginx/conf.d/default.conf
COPY --from=builder1 /var/app/build/ /var/www/app1
COPY --from=builder2 /var/app/build/ /var/www/app2

The following site.conf worked for me:

server {
    listen 80;
    listen [::]:80;

    server_name localhost;
    root /var/www;

    location /app1 {
        index index.html;
    }
    location /app2 {
        index index.html;
    }
}

I just had to change homepage to /app1 and /app2 respectively, change app2/App.js to actually output App2, and then

docker build . -t testreactmultiple:latest
docker run -it -p 3000:80 docker.io/library/testreactmultiple:latest  

I hope this helps you reproduce the working container that errors when the index is requested but works fine when /app1 or /app2 is requested.

Taxel
  • 2,985
  • 15
  • 31
  • Thanks! However when configuring nginx the way above, it will search for each apps files inside `/var/www/app1/app1/...`: `2022/02/15 14:33:18 [error] 30#30: *2 "/var/www/app1/app1/index.html" is not found (2: No such file or directory), client: 172.21.0.1, server: localhost, request: "GET /app1/ HTTP/1.1", host: "localhost:8009" ` – Wolkenarchitekt Feb 15 '22 at 14:36
  • Try `root /var/www`, since nginx apparently appends the location path (didn't know that) – Taxel Feb 15 '22 at 14:38
  • Using `/var/www` as root won't work either. This time, the react app will try to load static resources outside its `homepage` using the root dir: `/static/...`: `2022/02/15 16:00:33 [error] 30#30: *1 open() "/etc/nginx/html/static/css/main.073c9b0a.css" failed`. Seems that `homepage` doesn't work as expected. I also tried various approaches of passing `PUBLIC_URL`. None of these worked. – Wolkenarchitekt Feb 15 '22 at 16:05
  • I pushed the suggested changes to a branch my playground-repo: https://github.com/wolkenarchitekt/multiple-cra-apps-behind-nginx/tree/taxel as I said this doesn't solve the problem yet – Wolkenarchitekt Feb 15 '22 at 16:14
  • Is the react app making requests to `localhost:port/static/file.ext` instead of `localhost:port/app1/static/file.ext` or is `nginx` resolving it wrong? (can be figured out in devtools, network tab). This will at least narrow down the problem to be either a wrong `nginx` config (more likely, since I'm less familiar with that) or something with react not working. – Taxel Feb 15 '22 at 16:32
  • @Wolkenarchitekt I got it working with Docker and updated my answer. – Taxel Feb 15 '22 at 17:02
  • This. is. awesome! Works like a charm. Thank you so much, also for the nice multi-stage-dockerbuild (pretty useful for future react/nginx-debugging). – Wolkenarchitekt Feb 15 '22 at 18:40