Angular and SSR behind Nginx with pm2

I’m having a bit of an issue with an Angular application I have built (v. 17). It works perfectly fine, but I’m utilising a lot of dynamic routes, and these need to be rendered on the server. For example, I have a route /game/:id/:slug where id and slug are dynamic.

I have built the application and copied over the files to a location nginx can access them, and have the following config in Nginx:

server {
   listen 80;
   server_name domain.com;
   root /var/www/html/frontend/server;
   gzip on;
   gzip_types    text/plain application/javascript application/x-javascript text/javascript text/xml text/css;

   location /robots.txt {
        alias  /var/www/frontend/robots.txt;
   }

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

    location @backend {
        proxy_pass http://localhost:4000;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header X-NginX-Proxy true;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_cache_bypass $http_upgrade;
        proxy_redirect off;
        proxy_set_header X-Forwarded-Proto $scheme;
  }
}

Which is based off some config that I saw in another thread on here (I have also tried /var/www/html/frontend/browser, which gives the same results) . I have then ran the Angular app using pm2 via pm2 start server.mjs --name "app".

Everything runs perfectly, but I have noticed that when going to a dynamic route, such as /game/1/slug, the index.html file is loaded first, and then the correct component is loaded and all the data after the API request has been made on the client/browser.

This is causing a huge issue for me in terms of Google. They did index one page, but they indexed the index.html page for the link /game/1/slug, I assume because that is what they saw first before the component loaded. Since then, however, Google is refusing to index any of the pages citing a ‘soft 404’ error.

I’m not sure if I’m doing something obviously wrong here, or why none of my API requests are being made on the server? I can see all of the API calls (GET) being made in the browser console instead of on the server.

I’m using the default server.ts file:

export function app(): express.Express {
  const server = express();
  const serverDistFolder = dirname(fileURLToPath(import.meta.url));
  const browserDistFolder = resolve(serverDistFolder, '../browser');
  const indexHtml = join(serverDistFolder, 'index.server.html');

  const commonEngine = new CommonEngine();

  server.set('view engine', 'html');
  server.set('views', browserDistFolder);

  // Serve static files from /browser
  server.get(
    '*.*',
    express.static(browserDistFolder, {
      maxAge: '1y',
    }),
  );

  // All regular routes use the Angular engine
  server.get('*', (req, res, next) => {
    const { protocol, originalUrl, baseUrl, headers } = req;

    commonEngine
      .render({
        bootstrap,
        documentFilePath: indexHtml,
        url: `${protocol}://${headers.host}${originalUrl}`,
        publicPath: browserDistFolder,
        providers: [
          { provide: APP_BASE_HREF, useValue: req.baseUrl },
          { provide: 'REQUEST', useValue: req },
          { provide: 'RESPONSE', useValue: res },
        ],
      })
      .then((html) => res.send(html))
      .catch((err) => next(err));
  });

  return server;
}

function run(): void {
  const port = process.env['PORT'] || 4000;

  // Start up the Node server
  const server = app();
  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:${port}`);
  });
}

run();

Does anyone have any ideas/pointers in the right direction of what I’m potentially doing wrong here? I assume its something very obvious that I can’t seem to figure out.