---
title: "Routing and Normalization"
description: "Routing and Normalization in Next.js serves as the foundational canonicalization engine responsible for parsing, validating, and transforming incoming HTTP request URLs, client transitions, and int..."
last_updated: "2026-09-23T10:52:03.111005+00:00"
canonical_url: "https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/server-runtime/routing-and-normalization"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts)
- [packages/next/src/server/base-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts)
- [packages/next-routing/src/next-data.ts](https://github.com/blade47/next-routing/src/next-data.ts)
- [packages/next/src/server/normalizers/locale-route-normalizer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/locale-route-normalizer.ts)
- [packages/next/src/server/server-utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/server-utils.ts)
- [packages/next/src/shared/lib/i18n/normalize-locale-path.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/i18n/normalize-locale-path.ts)
- [packages/next/src/server/route-modules/route-module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/route-module.ts)
- [packages/next/src/shared/lib/router/router.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/router.ts)
- [packages/next/src/server/web/next-url.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/next-url.ts)
- [packages/next-routing/src/resolve-routes.ts](https://github.com/blade47/next-routing/src/resolve-routes.ts)
- [packages/next/src/server/lib/router-utils/resolve-routes.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts)
- [packages/eslint-plugin-next/src/utils/url.ts](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/utils/url.ts)
- [packages/next/src/shared/lib/router/utils/format-next-pathname-info.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/format-next-pathname-info.ts)
- [packages/next/src/shared/lib/normalized-asset-prefix.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/normalized-asset-prefix.ts)
- [packages/next/src/shared/lib/router/utils/app-paths.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/app-paths.ts)
- [packages/next/src/server/normalizers/request/segment-prefix-rsc.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/request/segment-prefix-rsc.ts)
- [packages/next/src/shared/lib/page-path/normalize-data-path.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/page-path/normalize-data-path.ts)
- [packages/next/src/client/normalize-locale-path.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/normalize-locale-path.ts)
- [packages/next/src/server/route-modules/app-page/normalize-request-url.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-page/normalize-request-url.ts)
- [packages/next-routing/src/i18n.ts](https://github.com/blade47/next-routing/src/i18n.ts)
- [packages/next/src/server/normalizers/request/prefix.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/request/prefix.ts)
- [packages/next/src/server/normalizers/request/next-data.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/request/next-data.ts)
- [packages/next/src/server/route-modules/app-route/helpers/clean-url.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/helpers/clean-url.ts)
- [packages/next/src/server/lib/i18n-provider.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/i18n-provider.ts)
- [packages/next/src/server/normalizers/built/app/app-bundle-path-normalizer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/built/app/app-bundle-path-normalizer.ts)
- [packages/next/src/server/normalizers/normalizer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/normalizer.ts)
- [packages/next/src/server/request/pathname.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/request/pathname.ts)
- [packages/next/src/client/remove-locale.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/client/remove-locale.ts)
- [packages/next/src/server/normalizers/prefixing-normalizer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/prefixing-normalizer.ts)
- [packages/next/src/server/normalizers/request/pathname-normalizer.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/request/pathname-normalizer.ts)
</details>

## Overview

Routing and Normalization in Next.js serves as the foundational canonicalization engine responsible for parsing, validating, and transforming incoming HTTP request URLs, client transitions, and internal build artifacts before they reach route matchers, middleware, or page renderers. Incoming requests often carry environmental artifacts such as configured basePath prefixes, internationalization locale segments, internal Next.js data request structures, React Server Component extensions, and malformed slashes.

Sources: [packages/next/src/server/lib/router-utils/resolve-routes.ts:153-165](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts#L153-L165)

The normalization subsystem addresses these issues by executing structured extraction pipelines across both client and server boundaries, decoupling structural concerns via specialized normalizer classes and utility functions.

Sources: [packages/next/src/server/base-server.ts:1637-1661](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1637-L1661)

```mermaid
flowchart TD
    A["Raw Request URL"] --> B["Normalize Slashes"]
    B --> C["Extract BasePath"]
    C --> D["Extract Next Data URL"]
    D --> E["Analyze Locale"]
    E --> F["Pathname Normalizer Pipeline"]
    F --> G["Route Matcher / Handler"]
```

Sources: [packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts:63-109](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts#L63-L109)

## Pathname Information Extraction (`getNextPathnameInfo`)

The core analytical primitive for parsing request paths is `getNextPathnameInfo`, located in `packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts`. This utility inspects a raw pathname string against configuration parameters and extracts metadata into an interface called `NextPathnameInfo`.

Sources: [packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts:53-61](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts#L53-L61)

The extraction routine checks trailing slashes, strips basePath prefixes, parses Next.js data URLs beginning with `/_next/data/`, and detects locales via `normalizeLocalePath` or `i18nProvider.analyze`.

Sources: [packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts:62-111](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts#L62-L111)

```typescript
export interface NextPathnameInfo {
  basePath?: string
  buildId?: string
  locale?: string
  pathname: string
  trailingSlash?: boolean
}
```

Sources: [packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts:6-30](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts#L6-L30)

> [!NOTE]
> When parsing Next.js data URLs, `getNextPathnameInfo` retains the original data prefix for metadata extraction while providing a normalized inner pathname when `parseData: true` is set.

Sources: [packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts:83-88](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts#L83-L88)

## Pathname Normalizers and Object-Oriented Hierarchy

Next.js implements an extensible object-oriented normalizer pattern defined by the `Normalizer` and `PathnameNormalizer` interfaces.

Sources: [packages/next/src/server/normalizers/normalizer.ts:1-3](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/normalizer.ts#L1-L3)

These classes encapsulate specific URL manipulation behaviors, such as stripping base paths, handling `.json` data suffixes, segment prefetch RSC extensions, and bundle path transformations.

Sources: [packages/next/src/server/normalizers/request/pathname-normalizer.ts:1-6](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/request/pathname-normalizer.ts#L1-L6)

```mermaid
classDiagram
    class Normalizer {
        <<interface>>
        +normalize(pathname: string): string
    }
    class PathnameNormalizer {
        <<interface>>
        +match(pathname: string): boolean
        +normalize(pathname: string, matched?: boolean): string
    }
    class PrefixPathnameNormalizer {
        -prefix: string
        +match(pathname: string): boolean
        +normalize(pathname: string, matched?: boolean): string
    }
    class SuffixPathnameNormalizer {
        -suffix: string
        +match(pathname: string): boolean
        +normalize(pathname: string, matched?: boolean): string
    }
    class NextDataPathnameNormalizer {
        -prefix: PrefixPathnameNormalizer
        -suffix: SuffixPathnameNormalizer
        +match(pathname: string): boolean
        +normalize(pathname: string, matched?: boolean): string
    }
    class LocaleRouteNormalizer {
        -provider: I18NProvider
        +normalize(pathname: string): string
    }

    Normalizer <|-- PathnameNormalizer
    Normalizer <|-- PrefixPathnameNormalizer
    Normalizer <|-- SuffixPathnameNormalizer
    Normalizer <|-- LocaleRouteNormalizer
    PathnameNormalizer <|-- NextDataPathnameNormalizer
```

Sources: [packages/next/src/server/normalizers/request/next-data.ts:7-31](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/request/next-data.ts#L7-L31)

The server instance maintains a prioritized array of normalizers in `BaseServer.normalize` to evaluate requests sequentially.

Sources: [packages/next/src/server/base-server.ts:1637-1661](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1637-L1661)

Each normalizer implements a guard method `match(pathname)` prior to executing `normalize(pathname)`. For instance, `PrefixPathnameNormalizer` explicitly validates constructor parameters.

Sources: [packages/next/src/server/normalizers/request/prefix.ts:3-10](https://github.com/blade47/next.js/blob/main/packages/next/src/server/normalizers/request/prefix.ts#L3-L10)

## Internationalization (`i18n`) Locale Detection and Path Normalization

Internationalization routing requires detecting locales from the request pathname, subdomains, cookies, or `Accept-Language` headers, and normalizing the path by stripping the locale prefix.

Sources: [packages/next-routing/src/i18n.ts:196-269](https://github.com/blade47/next-routing/src/i18n.ts#L196-L269)

This is handled by `normalizeLocalePath`, `I18NProvider`, and routing resolvers.

Sources: [packages/next/src/shared/lib/i18n/normalize-locale-path.ts:22-61](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/i18n/normalize-locale-path.ts#L22-L61)

The `normalizeLocalePath` function splits the pathname by `/` to inspect the second segment, utilizing a `WeakMap` cache for performance.

Sources: [packages/next/src/shared/lib/i18n/normalize-locale-path.ts:8-34](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/i18n/normalize-locale-path.ts#L8-L34)

```typescript
const cache = new WeakMap<readonly string[], readonly string[]>()

export function normalizeLocalePath(
  pathname: string,
  locales?: readonly string[]
): PathLocale {
  if (!locales) return { pathname }

  let lowercasedLocales = cache.get(locales)
  if (!lowercasedLocales) {
    lowercasedLocales = locales.map((locale) => locale.toLowerCase())
    cache.set(locales, lowercasedLocales)
  }

  const segments = pathname.split('/', 2)
  if (!segments[1]) return { pathname }

  const segment = segments[1].toLowerCase()
  const index = lowercasedLocales.indexOf(segment)
  if (index < 0) return { pathname }

  const detectedLocale = locales[index]
  pathname = pathname.slice(detectedLocale.length + 1) || '/'

  return { pathname, detectedLocale }
}
```

Sources: [packages/next/src/shared/lib/i18n/normalize-locale-path.ts:10-61](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/i18n/normalize-locale-path.ts#L10-L61)

> [!WARNING]
> When `i18n` is configured, failing to account for locale prefixes when resolving filesystem items can result in route matching collisions or 404 errors, as app directory routes do not match i18n locale prefixes directly.

Sources: [packages/next/src/server/lib/router-utils/resolve-routes.ts:328-334](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts#L328-L334)

## Next.js Data URL Normalization (`/_next/data/`)

Client-side data fetching for Static Generation and Server-Side Rendering utilizes JSON data files mapped under `/_next/data/{buildId}/{path}.json`.

Sources: [packages/next-routing/src/next-data.ts:5-34](https://github.com/blade47/next-routing/src/next-data.ts#L5-L34)

The normalization engine converts these data request URLs back into standard page pathnames for routing and execution.

Sources: [packages/next/src/shared/lib/page-path/normalize-data-path.ts:6-18](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/page-path/normalize-data-path.ts#L6-L18)

Both client-side utilities and server-side router utils perform this canonicalization via `normalizeDataPath`:

Sources: [packages/next-routing/src/next-data.ts:40-67](https://github.com/blade47/next-routing/src/next-data.ts#L40-L67)

```typescript
export function normalizeDataPath(pathname: string) {
  if (!pathHasPrefix(pathname || '/', '/_next/data')) {
    return pathname
  }
  pathname = pathname
    .replace(/\/_next\/data\/[^/]{1,}/, '')
    .replace(/\.json$/, '')

  if (pathname === '/index') {
    return '/'
  }
  return pathname
}
```

Sources: [packages/next/src/shared/lib/page-path/normalize-data-path.ts:6-18](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/page-path/normalize-data-path.ts#L6-L18)

Conversely, denormalization reconstructs the data URL format by injecting the build ID and `.json` extension.

Sources: [packages/next-routing/src/next-data.ts:37-67](https://github.com/blade47/next-routing/src/next-data.ts#L37-L67)

## App Directory Route Normalization (`normalizeAppPath`)

In the App Router (`app/` directory), file paths on disk include structural syntax such as route groups, parallel route slots, and leaf filenames.

Sources: [packages/eslint-plugin-next/src/utils/url.ts:37-61](https://github.com/blade47/next.js/blob/main/packages/eslint-plugin-next/src/utils/url.ts#L37-L61)

The `normalizeAppPath` function strips these markers to derive the public request pathname.

Sources: [packages/next/src/shared/lib/router/utils/app-paths.ts:23-52](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/app-paths.ts#L23-L52)

```typescript
export function normalizeAppPath(route: string) {
  return ensureLeadingSlash(
    route.split('/').reduce((pathname, segment, index, segments) => {
      if (!segment) {
        return pathname
      }
      if (isGroupSegment(segment)) {
        return pathname
      }
      if (segment[0] === '@') {
        return pathname
      }
      if (
        (segment === 'page' || segment === 'route') &&
        index === segments.length - 1
      ) {
        return pathname
      }

      return `${pathname}/${segment}`
    }, '')
  )
}
```

Sources: [packages/next/src/shared/lib/router/utils/app-paths.ts:23-52](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/app-paths.ts#L23-L52)

To correctly prioritize parallel slot paths during route matching and manifest loading, Next.js employs `compareAppPaths`.

Sources: [packages/next/src/shared/lib/router/utils/app-paths.ts:64-70](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/app-paths.ts#L64-L70)

> [!IMPORTANT]
> `compareAppPaths` ensures that parallel slot paths containing `/@` sort before children page paths. Without this, route group prefixes like `(group)` would sort before `@`, leading to manifest mismatches in development mode.

Sources: [packages/next/src/shared/lib/router/utils/app-paths.ts:54-70](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/app-paths.ts#L54-L70)

## Request URL Canonicalization Pipeline

When an incoming HTTP request is received by the Next.js server, it undergoes a rigorous validation and normalization pipeline before middleware or route handlers execute.

Sources: [packages/next/src/server/lib/router-utils/resolve-routes.ts:118-142](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts#L118-L142)

The pipeline checks for repeated slashes and backslashes, constructs absolute initialization URLs, peels locale and basePath prefixes, and tags data requests.

Sources: [packages/next/src/server/lib/router-utils/resolve-routes.ts:153-207](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts#L153-L207)

```mermaid
sequenceDiagram
    participant Client
    participant Server as BaseServer / resolve-routes
    participant Normalizer as PathnameNormalizer
    participant Router as Route Matcher

    Client->>Server: HTTP Request
    Server->>Server: Check repeated slashes / backslashes
    alt Malformed Slashes Detected
        Server-->>Client: 308 Redirect
    else Clean URL
        Server->>Normalizer: Match & Normalize BasePath / Data / Locale
        Normalizer-->>Server: Canonical Pathname
        Server->>Router: Resolve Route Match / Middleware
    end
```

Sources: [packages/next/src/server/lib/router-utils/resolve-routes.ts:153-165](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts#L153-L165)

## Configuration Options and Reference Table

The routing and normalization subsystem relies on configuration properties defined in `next.config.js` and internal request headers.

Sources: [packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts:32-51](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts#L32-L51)

| Configuration Property | Type | Default | Purpose / Behavior |
| :--- | :--- | :--- | :--- |
| `basePath` | `string` | `''` | Prefixes all application routes. |
| `i18n` | `object` | `null` | Enables internationalization routing. |
| `trailingSlash` | `boolean` | `false` | Enforces or strips trailing slashes. |
| `assetPrefix` | `string` | `''` | CDN or asset prefix URL for chunks. |
| `skipProxyUrlNormalize` | `boolean` | `false` | Bypasses automatic trailing slash normalization. |

Sources: [packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts:40-44](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts#L40-L44), [packages/next/src/server/lib/router-utils/resolve-routes.ts:193-202](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts#L193-L202)

## Design Trade-offs

The routing and normalization architecture balances performance, flexibility, and compliance with HTTP standards through deliberate design choices.

Sources: [packages/next/src/server/base-server.ts:1637-1661](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-server.ts#L1637-L1661)

| Design Choice | Benefit | Cost / Trade-off |
| :--- | :--- | :--- |
| **WeakMap Locale Caching** | Prevents redundant lowercasing of locale arrays while allowing GC. | Small memory overhead per unique locale array reference. |
| **Pipelined Pathname Normalizers** | Decouples concerns into single-responsibility classes. | Multiple regex checks and string slicing operations per request. |
| **Strict App Path Normalization** | Strips layout, group, and slot segments for clean URLs. | Requires manifest lookups to map public paths back to disk. |

Sources: [packages/next/src/shared/lib/i18n/normalize-locale-path.ts:8-11](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/i18n/normalize-locale-path.ts#L8-L11), [packages/next/src/shared/lib/router/utils/app-paths.ts:23-52](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/app-paths.ts#L23-L52)

## Related

- [Server Request Lifecycle](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/server-runtime/server-request-lifecycle)
- [Route Matching](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/server-runtime/route-matching)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/llms.txt) for all pages in this wiki.
