Skip to content
CirruSoul

Several apps, one domain: Next.js basePath and Nginx

We wanted CirruSoul Arena to live at cirrusoul.com/arena instead of a new domain. The basePath, the proxy rules and the redirect loop we hit along the way.

CCirruSoul Technologies Team4 Nov 20263 min read
DevOps

Several apps, one domain: Next.js basePath and Nginx

When we launched CirruSoul Arena, we did not want a second domain. Visitors should find it through the company website and stay on one trusted address. That meant serving a separate Next.js app, with its own API, under cirrusoul.com/arena. It works well, but a few details are easy to get wrong.

The plan

  • The company site runs on one port and the Arena front end on another.
  • Nginx sends everything under /arena to the Arena app and everything else to the company site.
  • The Arena app is built knowing it lives under /arena.

Step 1: tell Next.js about the prefix

Next.js has a basePath setting. With basePath: "/arena", links, assets and routes are all generated under that prefix. We made it configurable through an environment variable so the same code still runs at the root on a developer machine.

Two things the framework does not do for you:

  • Raw fetch calls. A call like fetch("/api/meta") is not prefixed automatically. We added the base path in the one place that wraps our API calls, and in the few raw links, such as a CSV download.
  • WebSockets. The live-contest socket needs its path set explicitly, because the default /socket.io would hit the company site.

Step 2: the Nginx rules

Three locations were enough:

  1. /arena/_next/static/ with a long cache, since file names are hashed.
  2. /arena/socket.io/, with a rewrite that strips /arena before passing the request to the API, plus the upgrade headers WebSockets need.
  3. /arena, passed to the Arena app, which keeps the prefix because the app expects it.

The redirect loop

Our first version added a rule that redirected /arena to /arena/. Next.js, meanwhile, redirects /arena/ back to /arena by default. Each one sent the browser to the other, forever. The fix was to delete our rule and let the application decide, so the proxy is only a proxy. If you see a loop between a 301 and a 308, this is the first place to look.

Step 3: build somewhere with room

The server has 1 GB of memory and little disk, which is too tight to install every dependency and compile there. We build the front end on a laptop using Next.js's standalone output, which produces a small self-contained folder, and copy only that to the server. The API is built the same way and installs just its production dependencies there. This keeps deploys quick and the company site safe from a full disk.

Cookies and shared origin

Because both apps share one origin, cookies are shared too. Use distinct cookie names so a login on one cannot be mistaken for a login on the other. Ours were already different, so there was nothing to change.

When this approach is right

Path-based hosting is a good fit when the products belong to one brand and you want one certificate, one domain and one set of analytics. A separate sub-domain is cleaner when the products are independent or owned by different teams.

Need several products under one roof, or help moving an app behind a shared domain? Talk to us.

  • #Next.js
  • #Nginx
  • #DevOps
  • #Deployment