Search code examples
dockerhttp-redirectnext.jsproxymiddleware

How to redirect from domain to another domain in Next.js v13 app on server side behind proxy


I do have many domains and I want to redirect them to one main domain.

List of domains:

  • example.com
  • example1.com
  • example2.com
  • example3.com

The main domain I want to redirect from other domains is example.com

There is a great answer to redirect Next.js on the server side to a different path.

Next.js >= 12 Now you can do redirects using middleware, create a _middleware.js file inside the pages folder (or any sub folder inside pages)

import { NextResponse, NextRequest } from 'next/server'
export async function middleware(req, ev) {
    const { pathname } = req.nextUrl
    if (pathname == '/') {
        return NextResponse.redirect('/hello-nextjs')
    }
    return NextResponse.next() } 

Source: https://stackoverflow.com/a/58182678/10826693

Note: For Next.js v13, you must create the middleware.js file in the root directory of Next.js instead of pages/_middleware.js as mentioned in that answer.

If I try to redirect to another domain, the TypeScript code in middleware.ts in the root looks like this:

/* eslint-disable @next/next/no-server-import-in-page */
import { NextResponse, NextRequest } from 'next/server'

export async function middleware(req: NextRequest) {
    const url = req.nextUrl.clone()
    console.log(url.host) //logs localhost:3000

    if (!url.host.match(/example.com/)) {
        url.host = 'example.com'
        return NextResponse.redirect(url) //never executes because the URL is always localhost in the Docker container
    }
    return NextResponse.next()
}

However, a Next.js v13 application running in a Docker container behind a proxy server always has the localhost URL in the host. And url.host in a Docker container always equals the URL localhost with a defined port inside the container (e.g. localhost:3000).

How to redirect the domains example1.com, example2.com and example3.com to example.com including the same path, query parameters and hash when I only have localhost on the server side?


Solution

  • If you want to redirect all domains in the Docker container to the master domain, you need to get the redirected URL from the X-Forwarded-Host header.

    In addition to the host, you must also set the correct port (80/443 - header called X-Forwarded-Port) and protocol (http/https - header called X-Forwarded-Proto).

    Next.js v13 Create the middleware.ts file in your app's src folder and redirect all domains that do not match the example.com domain to it in production.

    /* eslint-disable @next/next/no-server-import-in-page */
    import { NextRequest, NextResponse } from 'next/server';
    
    export function middleware(request: NextRequest) {
      const url = request.nextUrl.clone();
    
      const isProduction = process.env.NODE_ENV === 'production' // redirect only in production
      const requestedHost = request.headers.get('X-Forwarded-Host');
    
      if (isProduction && requestedHost && !requestedHost.match(/example.com/)) {
        const host = `example.com`; // set your main domain
    
        const requestedPort = request.headers.get('X-Forwarded-Port');
        const requestedProto = request.headers.get('X-Forwarded-Proto');
    
        url.host = host;
        url.protocol = requestedProto || url.protocol;
        url.port = requestedPort || url.port;
    
        return NextResponse.redirect(url);
      }
    
      return NextResponse.next();
    }
    
    export const config = {
      matcher: '/',
    };
    

    Now all domains example1.com, example2.com and example3.com are redirected to example.com including the same path, query parameters and hash.