Hosting a react app on a route not working, but subdomain does?

So I'm trying to setup traefik so I can have multiple apps as routes from one domain.
IE. domain.com/app1, /app2, etc

With this config

version: "3.7"

services:
  myapp:
    container_name: "myapp-frontend"
    image: linux-node-build
    deploy:
      labels:
        - traefik.enable=true
        - traefik.http.services.myapp-service.loadbalancer.server.port=8080 
        - traefik.http.routers.myapp.entrypoints=web
        - traefik.http.routers.myapp.rule=PathPrefix(`/app1`)
        - traefik.http.routers.myapp.middlewares=redirect@file
        # http to https redirect working fine
        - traefik.http.routers.myapp-tls.rule=PathPrefix(`/app1`)
        - traefik.http.routers.myapp-tls.entrypoints=websecure
        - traefik.http.routers.myapp-tls.tls=true
    networks:
      - traefik-public
      
networks:
  traefik-public:
    external: true

And express app

const express = require('express');
const cors = require('cors');
const path = require('path');
const bodyParser = require('body-parser');

const PORT = process.env.PORT || 8080;
const HOST = '0.0.0.0';

const app = express();
app.use(cors());
app.options('*', cors());
app.use(bodyParser.json()); // handle json data
app.use(bodyParser.urlencoded({ extended: true })); // handle URL-encoded data

app.get('/health', (req, res) => { res.status(200).send('Healthy!') });

app.use("/static", express.static(path.join(__dirname, 'build/static'), { maxAge: '30m' }));
app.use("/", express.static(path.join(__dirname, 'build'), { maxAge: '30m' }));

app.get('/*', (req, res) => {
    res.set('Cache-Control', 'public, max-age=1800'); // 30m
    res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);

My app is running and if I hit the PathPrefix of /app1 I get a page that tries to load resources like css/js
The problem is that the resources are just loaded as html so I get a white page.

I've tried stripping the path prefix and adding a homepage in package.json but neither worked.

Oddly this works perfectly fine if instead of doing a PathPrefix I use Host(app.localhost) and remove the homepage in package.json. My react app renders with all the css/js/img assets.

Hi @bcowell, it is not related to Traefik, but to the fact that your application should be adapted for being served behind a URL path/context: Traefik is able to route the main request and forward the answer (your webbrowser developer console can show you that the initial request is a HTTP/200).

2 ways to solve the issue:

  • Adapt your application so the relative links for CSS/images/JS scripts/forms/etc. does not start with a /:

For instance:

<script src="js/script.js" />
<!-- instead of <script src="/js/script.js" /> -->

so the web browser will emit request from its current context, e.g. http://domain.com/app1 + js/scripts.js => http://domain.com/app1/js/scripts.js, instead of http://domain.com/js/scripts.js because the initial / means "root of the URL domain".

  • Adapt your application to make it "aware" of the full external URL so the generated links are absolutes instead of relatives (generally by passing an environment variable to the web app).
1 Like

Hey @dduportal thanks!
Realized I was missing the path in my express routes serving the react app.
So although I was using homepage and the asset links were correct, the express routes were wrong.

app.get('/app1/health', (req, res) => { res.status(200).send('Healthy!') });

app.use("/app1/static", express.static(path.join(__dirname, 'build/static'), { maxAge: '30m' }));
app.use("/app1/", express.static(path.join(__dirname, 'build'), { maxAge: '30m' }));

app.get('/app1/*', (req, res) => {
    res.set('Cache-Control', 'public, max-age=1800'); // 30m
    res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

So this coupled with "homepage": "/app1" in my package.json works!

1 Like