How to validate locale parameters in URLs in Next.js (Pages Router) v16

Handle unsupported locale codes gracefully

Problem

When locale codes become part of URL structure, they transform into user input that must be validated. A visitor can type /xx/about or /gibberish/contact just as easily as a valid locale like /en/about. Without validation, the application may crash, display broken content, or show confusing error messages. Users who encounter invalid locale URLs need a clear path forward—either through redirection to a valid locale or a proper "Not Found" response that helps them understand what went wrong.

Solution

Validate incoming locale parameters against a list of supported locales before the request reaches page components. Use Next.js middleware to intercept requests, check whether the locale in the URL matches a supported value, and respond appropriately. For invalid locales, redirect the user to the default locale or rewrite the request to display a 404 page. This ensures that only valid locale codes proceed to render content, while invalid codes are handled gracefully without breaking the user experience.

Steps

1. Define supported locales in Next.js configuration

Configure the i18n settings in next.config.js to declare which locales your application supports and which locale serves as the default.

module.exports = {
  i18n: {
    locales: ["en", "fr", "de"],
    defaultLocale: "en",
    localeDetection: false,
  },
};

Setting localeDetection to false prevents automatic redirects based on browser preferences, giving you full control over locale handling.

2. Create middleware to validate locale parameters

Create a middleware.ts file in the root of your project or inside the src folder if you are using one.

import { NextRequest, NextResponse } from "next/server";

const SUPPORTED_LOCALES = ["en", "fr", "de"];
const DEFAULT_LOCALE = "en";

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  const pathnameHasLocale = SUPPORTED_LOCALES.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`,
  );

  if (!pathnameHasLocale) {
    return;
  }

  const localeInPath = pathname.split("/")[1];

  if (!SUPPORTED_LOCALES.includes(localeInPath)) {
    const url = request.nextUrl.clone();
    url.pathname = pathname.replace(`/${localeInPath}`, `/${DEFAULT_LOCALE}`);
    return NextResponse.redirect(url);
  }
}

export const config = {
  matcher: ["/((?!_next|api|favicon.ico|.*\\..*).*)"],
};

The middleware extracts the locale from the URL path, checks it against the supported locales array, and redirects invalid locales to the default locale while preserving the rest of the path.

3. Handle invalid locales with a 404 response

If you prefer to show a 404 page instead of redirecting, rewrite the request to the custom 404 page.

import { NextRequest, NextResponse } from "next/server";

const SUPPORTED_LOCALES = ["en", "fr", "de"];

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  const pathnameHasLocale = SUPPORTED_LOCALES.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`,
  );

  if (!pathnameHasLocale) {
    return;
  }

  const localeInPath = pathname.split("/")[1];

  if (!SUPPORTED_LOCALES.includes(localeInPath)) {
    const url = request.nextUrl.clone();
    url.pathname = "/404";
    return NextResponse.rewrite(url);
  }
}

export const config = {
  matcher: ["/((?!_next|api|favicon.ico|.*\\..*).*)"],
};

Create a custom 404 page at pages/404.js which is statically generated at build time.

4. Create a custom 404 page

export default function Custom404() {
  return (
    <div>
      <h1>404 - Page Not Found</h1>
      <p>The page you are looking for does not exist.</p>
    </div>
  );
}

This page displays when the middleware rewrites invalid locale requests, providing users with a clear message that the requested URL is not available.