Mastering Subdomains in Next.js with Middleware

Mastering Subdomains in Next.js with Middleware

Introduction

Multi-tenant applications, different sections within your site, or even personalized user experiences – sometimes you need to dynamically route requests based on subdomains in your Next.js project. With Next.js middleware, this is now easier than ever. In this post, we'll explore how to set up and customize middleware to seamlessly handle subdomains.

Prerequisites

  • Basic familiarity with Next.js

  • An understanding of middleware concepts

  • A Next.js project (I'll be referencing App Router)

What are Subdomains?

Let's start with a quick refresher on subdomains. A subdomain is a part of your main domain. For example:

Why Use Middleware for Subdomains?

Next.js middleware allows you to intercept requests before they reach your page components. This is where we'll check for subdomains and execute routing logic. Advantages of using middleware include:

  • Centralized Logic: Keep your subdomain handling in one place.

  • Flexibility: Modify requests or responses as needed for each subdomain.

  • Performance: Middleware executes at the edge, providing potential speed benefits.

Setting Up the Middleware

  1. Create the middleware file: Inside your Next.js project, create a file named middleware.js (or middleware.ts if using TypeScript) within your appdirectory.

  2. Basic middleware structure: Use this basic template:

     import { NextResponse } from 'next/server';
    
     export function middleware(req) {
         // Your subdomain handling logic will go here
         return NextResponse.next(); 
     }
    

Folder Structure

src/
  app/
    app/
      (routes)/
        ...
      page.tsx
      layout.tsx
    blog/
      (posts)/
        page.tsx
        [id]/
          ...
      page.tsx

Subdomain Routing Logic

Inside your middleware function, let's implement subdomain routing:

const hostname = req.headers.get('host')!;
  const subdomain = hostname.match(/^([^.]+)\./)?.[1];

  switch (true) {
    case subdomain.startsWith('app'):
      return NextResponse.rewrite(new URL(`/app${req.nextUrl.pathname}`, req.url));
    case subdomain.startsWith('blog'):
      return NextResponse.rewrite(new URL(`/blog${req.nextUrl.pathname}`, req.url));
    // Add more cases for other subdomains
    default:
      // Handle the main domain
  }

Explanation

  • Extract Subdomain: We get the hostname and extract the subdomain using a regular expression.

  • switch Statement: We use a switch statement (or if/else blocks) for subdomain-based routing.

  • NextResponse.rewrite: This function rewrites URLs transparently without an actual redirection, so in the browser it would appear as /posts/:id while routing to /blog/posts/:id

Additional Considerations

  • Authentication/Authorization: Secure specific subdomains by adding authentication checks within your middleware.

  • API Requests: Handle API routes (/api) separately if they don't follow your subdomain scheme.

  • Preview Environments: Ensure your middleware can handle routing in a preview environment with a more dynamic URL (app--branch.example.com). In this case, we are checking if the subdomain has the correct prefix (app) and then route accordingly. So it is important for preview URLs to have the correct structure across all environments!

Conclusion

Next.js middleware provides a powerful and intuitive way to manage subdomain-based routing and implement any necessary customization for your multi-tenant applications. By mastering middleware for subdomains, you gain the flexibility to:

  • Organize complex applications: Subdomains neatly segment different functionalities or sections of your site.

  • Enhance SEO: Potentially improve your rankings with targeted content on specific subdomains.

  • Personalize experiences: Deliver tailored content or features based on the subdomain a user accesses.

Beyond the Basics

As you become more comfortable with subdomain handling in middleware, here are some areas to explore further:

  • Complex Authentication: Implement different authentication schemes or authorization rules across your subdomains for fine-grained access control.

  • Hybrid Routing: Combine middleware-based subdomain handling with more granular route-level control within the App Router for maximum flexibility.

  • Data Fetching: Fetch subdomain-specific data in middleware to prepopulate components or personalize content.