NextJS + Ingress subpath prefixing

Prefixing your NextJS app through Ingress

What do we want to achieve?

By default, NextJS doesn’t support serving an application with it’s assets from a custom app prefix (in both dev and prod), for example:

1
https://www.myapp.com/ 	  <-- SomeOtherApp
2
https://www.myapp.com/portal <--- My NextJS app
3
https://www.myapp.com/static/portal <--- My NextJS static (cached by CDN)

NextJS configuration

To be able to do this we need to add a custom server.(js|ts), use the setAssetPrefix and pass handling the assets into the NextJS request handler.

1
const assetPrefix = '/static/portal';
2
3
const app = next({ dev: process.env.NODE_ENV !== 'production' });
4
const handle = app.getRequestHandler();
5
6
// Handle the asset and rewrite the pathname
7
const handleAppAssets = (assetRegexp, handle) => (req, res) => {
8
  const parsedUrl = parse(req.url, true);
9
  const pathname = parsedUrl.pathname as string;
10
  const assetMatch = assetRegexp.exec(pathname);
11
  const [, asset] = assetMatch;
12
  req.url = format({
13
    ...parsedUrl,
14
    pathname: asset,
15
  });
16
17
  return handle(req, res);
18
};
19
20
app.prepare().then(() => {
21
  // Set the asset prefix
22
  app.setAssetPrefix(assetPrefix);
23
  
24
  const server = express();
25
  
26
  // Handle the app assets and route them
27
  server.get(
28
    `${assetPrefix}/*`,
29
    handleAppAssets(new RegExp(`^${assetPrefix}(/.*$)`), handle),
30
  );
31
32
  server.all('*', (req, res) => handle(req, res));
33
  server.listen(3000, error => {
34
    if (error) throw error;
35
    console.log('Server started.');
36
  });
37
});

Ingress configuration

By default, ingress will rewrite+proxy to our pod(s), but it’ll keep the subpath. By using the rewrite-target annotation, it’ll rewrite to the root of our container.

1
metadata:
2
  name: frontend-portal
3
  labels:
4
    owner: myorg
5
  annotations:
6
    # Route all traffic to pod, but don't keep subpath (!)
7
    nginx.ingress.kubernetes.io/rewrite-target: /
8
9
spec:
10
  rules:
11
    - host: {{ .Values.clusterDomain }}
12
      http:
13
        paths:
14
			- path: /portal(/.*|$)
15
              backend:
16
                serviceName: frontend-portal
17
                servicePort: 80
18
			- path: /static/portal(/.*|$)
19
              backend:
20
                serviceName: frontend-portal
21
                servicePort: 80

Result

Now your NextJS app will live in the subpath, with it’s assets served separately, and your dev and production environment will be in sync.