Using wildcard subdomains as paths on Next.js

Baraky
3 min readFeb 14, 2023

--

From time to time, here at App Masters, we need to work on a project with a wildcard subdomain with a different theme (or data) for each one of the of the possible subdomains.

Since we’re using Next.js, this looks like something that should be possible out-of-the-box, but it’s not that simple.

On the client side, we can easily know the subdomain using the window.location , but on server side, it’s only possible to know it on the runtime and not on build time, so we had to give up the static rendering and some other optimization features Next.js made available, in favor of always use a getServerSideProps and get the subdomain inside the request.

NOT ANYMORE!

My first approach trying to handle this issue was to map the requests trying to use the Next.js rewrite function watching the header of each request. It makes sense to work, but for now, we can’t map header values as routes, only parameters.

Other solution is to have a custom node server, serving the Next.js frontend, but you will miss some of the optimization features and will have more code to maintain.

The solution: Using middleware!

Introduced on Next 12 and updated on Next 13, we can now easily have a middleware that will handle all our requests to the next node server and we can use this to change the request before it gets to the router.

So first, I created a folder inside my /src/pages folder, called [subdomain] and added all my pages on this folder. You probably already know about this, but adding the [] on the folder/file name inside the page folder, will make it dynamic.

This is how we can get dynamic routing on Next.js

After this, I’ve created the file src/middleware.ts (it need to be on the same level as the pages folder) and started to write some code:

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { getValidSubdomain } from '@/utils/subdomain';

// RegExp for public files
const PUBLIC_FILE = /\.(.*)$/; // Files

export async function middleware(req: NextRequest) {
// Clone the URL
const url = req.nextUrl.clone();

// Skip public files
if (PUBLIC_FILE.test(url.pathname) || url.pathname.includes('_next')) return;

const host = req.headers.get('host');
const subdomain = getValidSubdomain(host);
if (subdomain) {
// Subdomain available, rewriting
console.log(`>>> Rewriting: ${url.pathname} to /${subdomain}${url.pathname}`);
url.pathname = `/${subdomain}${url.pathname}`;
}

return NextResponse.rewrite(url);
}

The exported function middleware will handle all my requests and check if it have a valid subdomain. If it have, I’ll manually rewrite the pathname to the /{subdomain} . So, a request on https://baraky.domain.com/edit will access the page for https://baraky.domain.com/baraky/edit .

Here’s the code for the getValidSubdomain :

export const getValidSubdomain = (host?: string | null) => {
let subdomain: string | null = null;
if (!host && typeof window !== 'undefined') {
// On client side, get the host from window
host = window.location.host;
}
if (host && host.includes('.')) {
const candidate = host.split('.')[0];
if (candidate && !candidate.includes('localhost')) {
// Valid candidate
subdomain = candidate;
}
}
return subdomain;
};

I’ve added some other features to the function, to be able to get the subdomain even if the host string is not available.

Now you will be able to change your pages inside the [subdomain] folder. The subdomain value will be available inside your router, on the query parameters and you can use this how you want:

  • Fetch the data relative for each subdomain
  • Have a different theme for each subdomain
  • Have specific cache for each subdomain, and a static generate page using getStaticProps

But remember: to have access to the wildcard subdomain, you’ll need to host your Next.js app inside a hosting that provides this feature.

Why is this relevant?

Having all your pages separated on a sub-route instead of a subdomain will allow you to use getStaticProps for each one of the subdomains, since you’ll be able to separate then on different caches. Without it, Next.js will not be able to differentiate https://something.domain.com from https://other-thing.domain.com , returning the same static render for each website.

--

--