DEV Community

Gaurav Burande
Gaurav Burande

Posted on • Edited on

1

How to implement custom domains for pages in NextJS.

I'm a full-stack developer.

when I was looking at some support tickets I saw that most users were asking for a custom domain for their landing pages.

I started researching on Google and asked chatgpt, but the resources were not specific to my problem statement, they were just okay.

My first open-source contribution was to the EddieHub organization.

I casually revisited one of their projects - BioDrip, they had custom domains for user profiles, where normally the user profiles were just a landing page.

I was so lucky.
The tech stack for makelanding.ai and BioDrip were the same - NextJS.

I started reading the pull request for the custom domain feature.

Technically, having a custom domain for pages is impossible - a discord user replying to my help post.

The custom domains actually point at the IPv4 address of makelanding.ai - 76.76.21.21.

If the users wanted a subdomain for their landing page then they had to add a CNAME record with value cname.vercel-dns.com.

NextJs middleware which runs on the edge runtime can be used for many purposes:

  • A/B testing landing pages
  • Authentication
  • Geo-specific content delivery
  • Authentication
  • Personalization, which is the same as Geo-specific content delivery
  • Rewriting the response, which I used to serve the pages on custom domains.

When a user adds a custom domain to their landing page, It's added to the domains of the Vercel project, and I'm just verifying if the domain has configured the DNS records correctly using this vercel API: https://api.vercel.com/v10/domains/${domain}/config?teamId=${process.env.VERCEL_TEAM_ID}

To create the middleware in a NextJs project you just have to create a middleware.js or middleware.ts file in the root folder.

By default, the middleware will run on all the routes, API, and pages, but you can specify the routes, where you want to run the middleware by a config variable export:

export const config = {
    matcher: [
        "/",

        // page management
        "/page/:path*",
        "/api/page/domain",
    ],
};
Enter fullscreen mode Exit fullscreen mode

You don't want to run middleware on API routes where you're using dependencies, because middleware runs on edge runtime and to be fast it is like a minified version of nodejs, its edge runtime only supports few web APIs and the max size for a route's executed code running on edge is between 1MB - 4MB.

It'd be better to just visit the NextJs middleware doc rather than understand my explanation.

How this works is when the custom domain has configured the DNS records correctly and it's pointing to the vercel deployed project, the middleware checks the host which is making the request to the project's IPv4 address.

const hostname = req.headers.get("host");
    const reqPathName = req.nextUrl.pathname;
    const hostedDomain = process.env.NEXT_PUBLIC_BASE_URL.replace(
        /http:\/\/|https:\/\//,
        "",
    );
    const hostedDomains = [hostedDomain, `www.${hostedDomain}`, `preview.${hostedDomain}`];

    const regex = /^(.*\.)?vercel\.app$/;

    const vercelSubdomain = regex.test(hostname);

    // if custom domain + on root path
    if (!hostedDomains.includes(hostname) && !vercelSubdomain && reqPathName === "/") { 
    console.log("custom domain for landing page")
}
Enter fullscreen mode Exit fullscreen mode

If the host is not the hostedDomain for the vercel project or the subdomain provided by vercel for the projects, then we proceed.

Then check if the custom domain exists in the database, get the corresponding landing page to that specific custom domain, and rewrite the response! Easy!

if (
            page?.domain &&
            page?.domain === hostname
        ) {
            console.log(
                `custom domain "${hostname}", matched for page "${page.name}" (protocol: "${protocol}")`,
            );
            //If a match is found, rewrite to the custom domain and display the page
            return NextResponse.rewrite(
                new URL(
                    `/page/${page.id}`,
                    `${protocol}://${page.domain}`,
                ),
            );
        } else {
            console.error("something went wrong with serving the page on custom domain: " + page.domain)
        }
Enter fullscreen mode Exit fullscreen mode

Tiugo image

Modular, Fast, and Built for Developers

CKEditor 5 gives you full control over your editing experience. A modular architecture means you get high performance, fewer re-renders and a setup that scales with your needs.

Start now

Top comments (0)

Neon image

Next.js applications: Set up a Neon project in seconds

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Get started →

👋 Kindness is contagious

Dive into this thoughtful article, cherished within the supportive DEV Community. Coders of every background are encouraged to share and grow our collective expertise.

A genuine "thank you" can brighten someone’s day—drop your appreciation in the comments below!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found value here? A quick thank you to the author makes a big difference.

Okay