@shinyaz

Next.js App Router needs a catch-all route for locale-aware 404 pages

1 min read

Hitting /ja/nonexistent-page was redirecting to /en instead of showing a Japanese 404 page. Turns out the root not-found.tsx (which had redirect("/en") via defaultLocale) was being called instead of [locale]/not-found.tsx.

The fix is a catch-all route inside [locale]:

src/app/[locale]/[...slug]/page.tsx
import { notFound } from "next/navigation";
 
export default function CatchAllPage() {
  notFound();
}

This alone isn't enough — the root not-found.tsx must also render a 404 UI instead of redirecting, because Next.js App Router can still invoke it in some cases. Both not-found files detect the locale from request headers:

src/app/[locale]/not-found.tsx
const headersList = await headers();
const pathname = headersList.get("x-pathname") ?? "";
const referer = headersList.get("referer") ?? "";
const candidate = getLocaleFromUrl(pathname) || getLocaleFromUrl(referer) || defaultLocale;
const locale = isValidLocale(candidate) ? candidate : defaultLocale;

It prioritises x-pathname (set by middleware), falls back to referer, then to the default locale. Vercel's official guide "How to internationalise error pages in Next.js App Router" documents this exact pattern.

Share this post

Shinya Tahara

Shinya Tahara

Solutions Architect @ AWS

I'm a Solutions Architect at AWS, providing technical guidance primarily to financial industry customers. I share learnings about cloud architecture and AI/ML on this site.The views and opinions expressed on this site are my own and do not represent the official positions of my employer.