React + Express + Serve Static Trailing Slash

This is hobby project so I am trying to save cost and run all of one server. On dev machine I start Express.js on port 3000 and start CRA on port 3001 with "proxy": "http://localhost:3000", in CRA’s package.json and life is good.

Frontend is basic CRA project with it’s own router

<BrowserRouter>
  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/whatever" element={<Whatever />} />
    <Route path="*" element={<NotFound />} />
  </Routes>
</BrowserRouter>

Backend is Express.js with express router, idea being that / serves built CRA app, then API routes are checked and finally if that fails to match anything, the backend will return CRA app and let CRA’s router figure out what is going on.

import express from 'express';
import path from 'path';
import serveStatic from 'serve-static';

const router = express.Router(),
      root = path.join(__dirname, '..', '..');

const frontendPublicPath = path.join(root, '..', 'frontend', 'build');

router.use('/', serveStatic(frontendPublicPath));
router.use('/api/user', (req, res) => res.json(req.socket.remoteAddress));
router.use('/api/admin/esi/fetch', ensureAuthenticated, admin.fetchEndpoints);
router.use('/api/admin/esi/update', ensureAuthenticated, admin.updateEndpoints);

router.use('*', serveStatic(frontendPublicPath));
app.use('/', router);

This setup works like a charm, with one problem. On production server (heroku) it builds static frontend and serves it to the client, however, there are trailing slash issues

If you request directly https://example.org, you get https://example.org
If you request directly httts://example.org/page, you get https://example.org/page/ and 301 redirect

If you request directly https://example.org, you get https://example.org and if you then use link on page to go to httts://example.org/page, you get httts://example.org/page because routing in this case is done by CRA alone and there is no backend request.

On localhost, this redirect never happens

I am trying to figure out how to get rid of the 301 redirect in this setup.

Things tried:

Changing express.Router() to express.Router({ strict: true }) doesn’t seem to do anything here, because as expected, the redirect comes from serveStatic

Changing

router.use('/', serveStatic(frontendPublicPath, { redirect: false }));
...
router.use('*', serveStatic(frontendPublicPath, { redirect: false }));

It works as expected on localhost. However, on production, I can only access https://example.org and then navigate within CRA as expected, but any deep link fails. There is no longer 301 redirect, but asking for httts://example.org/page will say Cannot GET /page, which is unexpected since I would assume it would fail all the routes until it hits * route and just serves CRA app? If I do request httts://example.org/page/ then it works again, but I would prefer to get rid of / all together.

Is there something obvious I am missing here?

>Solution :

When routing to a specific route using serveStatic it will try and find a folder within the selected folder so in this case it is trying to find a folder named page in your build folder, when routing to a folder it searches for an index.html file this is the reason the basic route / works and others not. React-router handles the routing for you so you just need to serve the index.html for it. I found this example somewhere which seems to do what you are looking for

router.get('*', (req, res) => { 
    res.sendFile(pathToIndexHtml); 
});

Leave a Reply