How to rewrite subdomain to subpath without using a reverse proxy?

How to rewrite subdomain to subpath without using a reverse proxy?

Hey friends 👋,

Overview

I was re-coding a website service for nfthost.app and I wanted users to have their websites linked to a subdomain of my application.

My project is using Next.js and it was deployed with Vercel.

This blog will show you step by step how I managed to rewrite a dynamic subdomain to a dynamic subpath.

Setting Up Vercel

Navigate to Vercel’s Dashboard and choose the project that you are working on. Go to Settings → Domains and add a wildcard subdomain by adding .example.com.

Vercel Domain Setup

Setting Up Next.js

Now that we finished setting up Vercel, you can now setup your next.js project. Add a middleware.js file at the root of your project.

Enter the following code:

import { NextResponse } from 'next/server'

export const config = {
  matcher: ['/', '/_sites/:path'],
}

export default async function middleware(req) {
    const url = req.nextUrl;
    const hostname = req.headers.get('host');

    const subpath = process.env.NODE_ENV === 'production' ? hostname.slice(0, hostname.indexOf('.')) : 'www';

    if (subpath !== 'www') {   
        url.pathname = `/_sites/${subpath}${url.pathname}`;
    }

    return NextResponse.rewrite(url);
}

(Relative source code: github.com/stephenasuncionDEV/nfthost/blob/..)

The code above sees if one of the matcher elements matches with the URL entered. It gets the subdomain from the host of the request and rewrites the page to https://nfthost.app/_sites/<subdomain>.

Now that you’ve setup your middleware, go to your dynamic page and add the following code:

export const getStaticPaths = async () => {
    const mappedSubdomains = await axios.get(`${config.serverUrl}/api/website/getMappedSubdomains`, {
        headers: {
            Authorization: `bearer ${process.env.CREATE_WEBSITE_TOKEN}`
        }
    });
        // mapped subdomains returns an array of the possible subdomains
    // so you would need to find all the website data that has the route key (in my case)
    // and create an array of { params: { siteRoute: websiteData.route } }
        //
        // example:
        // exports.getMappedSubdomains = async (req, res, next) => {
        //     try {
        //         ...
        //         const websites = await Website.find({ route: {
        //             $exists: true,
        //             $ne: ''
        //         } });

        //         const mappedSubdomains = websites.map((websiteData) => {
        //             return { params: { siteRoute: websiteData.route } }
        //         })

        //         res.status(200).json(mappedSubdomains);

        //     } catch (err) {
        //         next(err);
        //     }
        // }

    return {
        paths: mappedSubdomains.data,
        fallback: true,
    }
}

export const getStaticProps = async ({ params: { siteRoute } }) => {
        // Gets the route from https://nfthost.app/_sites/<siteRoute>
        // and pass it to the component's props
    const site = await axios.get(`${config.serverUrl}/api/website/getWebsiteByRoute`, {
        params: {
            route: siteRoute
        },
        headers: {
            Authorization: `bearer ${process.env.CREATE_WEBSITE_TOKEN}`
        }
    });
        // site will return the website data

    return {
        props: site.data,
        revalidate: 3600
    }
}

(Relative source code: github.com/stephenasuncionDEV/nfthost/blob/..)

Demo

Here is my website: https://test.nfthost.app/ which rewrites to https://www.nfthost.app/_sites/test

Reference

https://vercel.com/blog/wildcard-domains

https://github.com/vercel/examples/tree/main/edge-functions/hostname-rewrites