---
title: "Middleware Execution"
description: "Middleware execution in Next.js governs how incoming HTTP requests are intercepted, evaluated, and transformed before reaching core page handlers or router logic. By running custom code at the edge..."
last_updated: "2026-09-23T10:52:03.128635+00:00"
canonical_url: "https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/edge-and-sandbox/middleware-execution"
---

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

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

- [packages/next/src/server/web/sandbox/sandbox.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts)
- [packages/next/src/server/next-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.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/next/src/server/dev/next-dev-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.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/src/server/dev/hot-reloader-webpack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-webpack.ts)
- [packages/next/src/server/web/adapter.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts)
- [packages/next/src/server/lib/router-utils/filesystem.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/filesystem.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/edge-route-module-wrapper.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts)
- [packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts)
- [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts)
- [packages/next/src/server/lib/router-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-server.ts)
- [packages/next/src/server/route-modules/app-route/module.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-modules/app-route/module.ts)
- [packages/next-routing/src/middleware.ts](https://github.com/blade47/next-routing/src/middleware.ts)
- [packages/next-codemod/transforms/__testfixtures__/middleware-to-proxy/runtime-multiple-exports.input.ts](https://github.com/blade47/next-codemod/transforms/__testfixtures__/middleware-to-proxy/runtime-multiple-exports.input.ts)
- [packages/next/src/experimental/testing/server/middleware-testing-utils.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/experimental/testing/server/middleware-testing-utils.ts)
- [packages/next/src/server/dev/middleware-webpack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts)
- [packages/next/src/server/api-utils/get-cookie-parser.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/api-utils/get-cookie-parser.ts)
- [packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts)
- [packages/next/src/shared/lib/router/utils/prepare-destination.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/prepare-destination.ts)
</details>

## Overview

Middleware execution in Next.js governs how incoming HTTP requests are intercepted, evaluated, and transformed before reaching core page handlers or router logic. By running custom code at the edge prior to request completion, it enables dynamic routing decisions, header manipulation, authentication checks, and URL rewrites or redirects based on runtime conditions.

Sources: [packages/next/src/server/next-server.ts:1611-1617](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1611-L1617), [packages/next/src/server/web/adapter.ts:377-380](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts#L377-L380)

## Route Matching and Has Conditions

### Overview

Incoming request evaluation relies on matching paths against compiled regular expressions and verifying optional declarative conditions such as headers, cookies, query parameters, and host names. The routing subsystem processes these checks sequentially to determine whether middleware should execute for a given request.

Sources: [packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts:14-40](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts#L14-L40), [packages/next/src/shared/lib/router/utils/prepare-destination.ts:48-125](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/prepare-destination.ts#L48-L125)

### Route Matcher Execution Flow

The route matching engine executes a deterministic call chain when testing an incoming request against configured route matchers.

1. `unstable_doesMiddlewareMatch()` initializes the validation context, parsing the target URL and constructing request abstractions with headers and cookies.
   Sources: [packages/next/src/experimental/testing/server/middleware-testing-utils.ts:19-40](https://github.com/blade47/next.js/blob/main/packages/next/src/experimental/testing/server/middleware-testing-utils.ts#L19-L40)
2. `getMiddlewareRouteMatcher()` iterates over the array of compiled proxy matchers, invoking `RegExp.exec()` against the request pathname.
   Sources: [packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts:22-26](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts#L22-L26)
3. If the pathname matches, `matchHas()` evaluates any associated `has` or `missing` conditions against the request headers, cookies, query, or host.
   Sources: [packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts:28-33](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts#L28-L33), [packages/next/src/shared/lib/router/utils/prepare-destination.ts:48-125](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/prepare-destination.ts#L48-L125)
4. If all conditions succeed, the matcher returns `true`; otherwise, iteration continues across remaining matchers or returns `false`.
   Sources: [packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts:30-38](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts#L30-L38)

> [!NOTE]
> If a matcher configuration omits the `matcher` property entirely, `unstable_doesMiddlewareMatch()` immediately returns `true` without evaluating path regexes or conditional rules.
> Sources: [packages/next/src/experimental/testing/server/middleware-testing-utils.ts:32-34](https://github.com/blade47/next.js/blob/main/packages/next/src/experimental/testing/server/middleware-testing-utils.ts#L32-L34)

### Condition Evaluation Types

The `matchHas` function inspects request properties based on explicit condition types defined in route configurations.

| Condition Type | Source Property Evaluated | Value Extraction & Normalization |
| --- | --- | --- |
| `header` | `req.headers[key]` | Key is lowercased; header value retrieved as string |
| `cookie` | `req.cookies` or `req.headers` | Cookies parsed via `getCookieParser()` if `cookies` property is absent on request |
| `query` | `query[key]` | Direct lookup against search parameter entries |
| `host` | `req.headers.host` | Hostname extracted by splitting at port `:` and lowercasing |

Sources: [packages/next/src/shared/lib/router/utils/prepare-destination.ts:56-86](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/prepare-destination.ts#L56-L86)

> [!WARNING]
> When evaluating `cookie` conditions against raw Node `IncomingMessage` objects, `matchHas` dynamically invokes `getCookieParser()` on request headers rather than relying on pre-parsed cookie collections.
> Sources: [packages/next/src/shared/lib/router/utils/prepare-destination.ts:66-72](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/prepare-destination.ts#L66-L72)

### Design Trade-Offs in Route Matching

| Design Choice | Benefit | Cost |
| --- | --- | --- |
| Sequential array iteration over compiled RegExp matchers | Simple implementation and predictable evaluation order | Linear O(n) performance scaling with the number of configured matchers |
| Dynamic cookie parser invocation on raw requests | Supports both Web API request objects and Node HTTP `IncomingMessage` instances | Repeated header parsing overhead when multiple cookie conditions are evaluated |
| Strict `has` and `missing` boolean conjunction (`every` and `!some`) | Expressive declarative conditions for advanced routing logic | Short-circuiting stops parameter collection early if any secondary constraint fails |

Sources: [packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts:22-36](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/middleware-route-matcher.ts#L22-L36), [packages/next/src/shared/lib/router/utils/prepare-destination.ts:66-75](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/prepare-destination.ts#L66-L75), [packages/next/src/shared/lib/router/utils/prepare-destination.ts:117-119](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/utils/prepare-destination.ts#L117-L119)

## Routing Pipeline and Invocation Determination

### Overview

When an incoming HTTP request traverses the Next.js server routing pipeline, the system determines whether to execute middleware by matching request paths against compiled route criteria and inspecting internal routing metadata. The process evaluates explicit path matchers, handles data route normalization, and orchestrates target resolution before invoking the server request handler.
Sources: [packages/next-routing/src/resolve-routes.ts:492-555](https://github.com/blade47/next-routing/src/resolve-routes.ts#L492-L555), [packages/next/src/server/lib/router-utils/resolve-routes.ts:423-490](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/resolve-routes.ts#L423-L490)

### Middleware Invocation Determination Walkthrough

The decision to trigger middleware for a given request flows through a series of specific checks:

1. `shouldInvokeMiddlewareForRequest()` or `resolveRoutes()` inspects whether `middlewareMatchers` are defined; if `undefined`, legacy behavior defaults to returning `true`.
   Sources: [packages/next-routing/src/resolve-routes.ts:492-534](https://github.com/blade47/next-routing/src/resolve-routes.ts#L492-L534)
2. If `middlewareMatchers` is an empty array, it immediately short-circuits and returns `false`.
   Sources: [packages/next-routing/src/resolve-routes.ts:535-537](https://github.com/blade47/next-routing/src/resolve-routes.ts#L535-L537)
3. The raw `url.pathname` is tested against each matcher's compiled regular expression and conditional `has` or `missing` rules.
   Sources: [packages/next-routing/src/resolve-routes.ts:498-527](https://github.com/blade47/next-routing/src/resolve-routes.ts#L498-L527)
4. If the raw pathname fails to match, the system attempts to decode the pathname using `decodeURIComponent()`. If decoding throws an error, it returns `false`.
   Sources: [packages/next-routing/src/resolve-routes.ts:543-549](https://github.com/blade47/next-routing/src/resolve-routes.ts#L543-L549)
5. If the decoded pathname differs from the raw pathname, matchers are re-evaluated against the decoded variant; otherwise, it returns `false`.
   Sources: [packages/next-routing/src/resolve-routes.ts:550-554](https://github.com/blade47/next-routing/src/resolve-routes.ts#L550-L554)

> [!WARNING]
> Pathname decoding errors during middleware invocation checks are treated as non-fatal and immediately abort matching for that candidate, falling back to `false` rather than throwing a request-level exception.
> Sources: [packages/next-routing/src/resolve-routes.ts:546-548](https://github.com/blade47/next-routing/src/resolve-routes.ts#L546-L548)

### Routing Pipeline Metadata and Request Handlers

During client transitions and server routing resolution, request metadata flags control how paths and data requirements are interpreted. The `getMiddlewareData` function inspects response headers to manage internal rewrites and redirection states.

| Response Header / Meta Key | Type / Source | Purpose |
| --- | --- | --- |
| `x-nextjs-rewrite` | Response Header | Specifies an internal rewrite target path from middleware execution |
| `x-nextjs-matched-path` | Response Header | Fallback header used to detect `next.config.js` rewrites when explicit rewrite targets are absent |
| `x-nextjs-data` | Request Header | Indicates a Next.js data fetch request (`/_next/data/...`) requiring special routing checks |
| `middlewareInvoke` | Request Meta | Boolean flag indicating whether the active request context originated from middleware processing |
| `invokePath` | Request Meta | Stores the resolved execution path assigned during `invokeRender()` |

Sources: [packages/next/src/shared/lib/router/router.ts:183-199](https://github.com/blade47/next.js/blob/main/packages/next/src/shared/lib/router/router.ts#L183-L199), [packages/next/src/server/lib/router-server.ts:318-336](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-server.ts#L318-L336)

> [!NOTE]
> When `x-nextjs-data` is present, if the normalized `invokePath` matches `/404` while middleware matchers are configured, the server short-circuits execution, sets response status `404`, and returns an empty JSON payload `{}` without invoking the full rendering pipeline.
> Sources: [packages/next/src/server/lib/router-server.ts:318-326](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-server.ts#L318-L326)

### Design Trade-Offs in Path Resolution and Invocation

| Design Choice | Benefit | Cost |
| --- | --- | --- |
| Dual raw and decoded pathname matching checks | Gracefully handles percent-encoded paths (e.g., Nginx-decoded proxy requests) | Double evaluation overhead against matcher regexes when raw matching fails |
| Undefined matcher fallback to `true` | Preserves backwards compatibility for callers without explicit matchers | Implicitly opts all requests into middleware execution if configuration is omitted |
| Request meta augmentation via `addRequestMeta()` | Decouples internal routing state from core Node HTTP request/response objects | Relies on mutable request object property mutation across pipeline boundaries |

Sources: [packages/next-routing/src/resolve-routes.ts:531-534](https://github.com/blade47/next-routing/src/resolve-routes.ts#L531-L534), [packages/next-routing/src/resolve-routes.ts:543-554](https://github.com/blade47/next-routing/src/resolve-routes.ts#L543-L554), [packages/next/src/server/lib/router-server.ts:333-343](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-server.ts#L333-L343)

## Server Orchestration and Manifest Loading

### Overview

Server-side orchestration prior to core request handling relies on manifest loading mechanisms and runtime inspection to locate, verify, and execute middleware before ordinary route handlers run. The `NodeManifestLoader` class uses the build distribution directory and the server directory constant to load compiled JSON manifests from disk. Concurrently, `NextServer` orchestrates the middleware execution pipeline through `runMiddleware`, validating requests, handling on-demand revalidation bypasses, normalizing absolute URLs, and evaluating edge versus Node.js middleware runtimes.

Sources: [packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts:5-20](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts#L5-L20), [packages/next/src/server/next-server.ts:1617-1760](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1617-L1760)

### Manifest Loading and Execution Call Chain

When the server initializes and resolves routing metadata or evaluates route matchers, manifest loaders retrieve structural configuration files from the build output directory. For middleware and routing entries, the call sequence proceeds through manifest lookup and verification functions before request handling commences.

The execution call chain for loading manifests and dispatching middleware proceeds through the following sequence:

1. `NodeManifestLoader.load()` — Joins the distribution directory (`distDir`), the server directory constant (`SERVER_DIRECTORY`), and the requested manifest name to locate the file on disk.
   Sources: [packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts:16-20](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts#L16-L20)
2. `NodeManifestLoader.require()` — Safely executes a Node.js `require()` on the constructed file path, returning `null` if the manifest file is absent or fails to load.
   Sources: [packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts:8-14](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts#L8-L14)
3. `runMiddleware()` — Orchestrates runtime request evaluation in `NextServer`, verifying on-demand revalidation status, normalizing request protocols, and querying `this.getMiddleware()` and `this.hasMiddleware()`.
   Sources: [packages/next/src/server/next-server.ts:1617-1675](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1617-L1675)
4. `this.getEdgeFunctionInfo()` or `this.loadNodeMiddleware()` — Resolves the execution target based on whether edge function metadata exists in the middleware manifest or falls back to Node.js middleware modules.
   Sources: [packages/next/src/server/next-server.ts:1678-1714](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1678-L1714)
5. `adapterFn()` or `sandbox.run()` — Executes the resolved middleware module via the web adapter or the isolated sandbox runtime, returning headers, cookies, and `waitUntil` promises.
   Sources: [packages/next/src/server/next-server.ts:1719-1759](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1719-L1759)

> [!WARNING]
> If `process.env.NEXT_MINIMAL` is set, calling `runMiddleware()` throws an immediate invariant error because minimal mode expects server orchestration to bypass standard middleware runner execution.
> Sources: [packages/next/src/server/next-server.ts:1624-1628](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1624-L1628)

### Orchestration Constants and Manifest Parameters

The orchestration layer relies on constant paths, environment configuration flags, and request metadata to determine how middleware is loaded and invoked.

| Constant / Parameter | Target Value / Type | Purpose / Behavior |
| --- | --- | --- |
| `SERVER_DIRECTORY` | `packages/next/src/shared/lib/constants.ts` | Subdirectory name (`server`) appended to `distDir` when loading manifests via `NodeManifestLoader` |
| `PRERENDER_MANIFEST` | `packages/next/src/server/next-server.ts` | Filename (`prerender-manifest.json`) loaded by `getPrerenderManifest()` for preview and prerender routing data |
| `NEXT_MINIMAL` | `process.env.NEXT_MINIMAL` | Environment check that throws an invariant error if `runMiddleware()` is invoked in minimal runtime mode |
| `middlewareInvoke` | Request Meta (`base-server.ts` / `next-server.ts`) | Boolean flag checked by `handleCatchallMiddlewareRequest` to verify if a request originates from middleware processing |

Sources: [packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts:1-18](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts#L1-L18), [packages/next/src/server/next-server.ts:1624-1628](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1624-L1628), [packages/next/src/server/next-server.ts:1801-1805](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1801-L1805), [packages/next/src/server/next-server.ts:1914-1916](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1914-L1916)

### Design Trade-Offs in Server Orchestration

| Design Choice | Benefit | Cost |
| --- | --- | --- |
| Safe `require()` wrapper returning `null` on failure | Prevents server crashes when optional manifest files are missing from the build output | Masks filesystem corruption or missing build artifacts as missing route configurations |
| Conditional fallback from edge manifest to Node.js middleware | Supports legacy or custom Node-based middleware handlers not recorded in `middleware-manifest.json` | Introduces branching complexity and divergent execution paths between edge and Node runtimes |
| On-demand revalidation header short-circuiting | Bypasses unnecessary middleware execution for revalidation requests, optimizing build-cache purges | Requires duplicate header parsing logic (`checkIsOnDemandRevalidate`) inside the middleware runner |

Sources: [packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts:8-14](https://github.com/blade47/next.js/blob/main/packages/next/src/server/route-matcher-providers/helpers/manifest-loaders/node-manifest-loader.ts#L8-L14), [packages/next/src/server/next-server.ts:1630-1640](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1630-L1640), [packages/next/src/server/next-server.ts:1712-1721](https://github.com/blade47/next.js/blob/main/packages/next/src/server/next-server.ts#L1712-L1721)

## Web Adapter and Response Handling

### Overview

The web adapter layer converts standard Node HTTP request structures and route module handlers into Fetch API-compatible abstractions (`NextRequest`, `NextResponse`, `NextFetchEvent`), while parsing and validating the response objects returned by middleware execution. 
Sources: [packages/next/src/server/web/adapter.ts:1-10](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts#L1-L10), [packages/next/src/server/web/edge-route-module-wrapper.ts:71-82](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L71-L82)

### Call-Chain Execution Walkthrough

The execution of a middleware request through the adapter pipeline flows through specific phases from wrapping to response parsing:

1. `EdgeRouteModuleWrapper.wrap()` initializes the module wrapper and binds the wrapper's private `handler()` method as an `EdgeHandler`.
   Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:61-82](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L61-L82)
2. `adapter()` receives request options, sets up the outer `waitUntil` promise context via `getBuiltinRequestContext()`, and instantiates `NextFetchEvent`.
   Sources: [packages/next/src/server/web/adapter.ts:241-250](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts#L241-L250)
3. The request is processed by `propagator()`, which checks if `params.page` corresponds to middleware (`/middleware`, `/src/middleware`, `/proxy`, or `/src/proxy`). If true, it initiates a tracer span (`MiddlewareSpan.execute`), creates request and work stores via `createRequestStoreForAPI()` and `createWorkStore()`, and executes `workAsyncStorage.run()` wrapping `params.handler`.
   Sources: [packages/next/src/server/web/adapter.ts:254-346](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts#L254-L346)
4. `EdgeRouteModuleWrapper.handler()` normalizes dynamic route parameters via `utils.normalizeDynamicRouteParams()`, instantiates a `CloseController`, calls `this.routeModule.handle(request, context)`, and attaches stream tracking via `trackStreamConsumed()`.
   Sources: [packages/next/src/server/web/edge-route-module-wrapper.ts:84-176](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L84-L176)
5. `responseToMiddlewareResult()` converts the returned middleware `Response` object into a mutable `MiddlewareResult`, processing header overrides (`x-middleware-override-headers`), rewrites (`x-middleware-rewrite`), redirects (`location`), and refresh signals (`x-middleware-refresh`).
   Sources: [packages/next-routing/src/middleware.ts:13-204](https://github.com/blade47/next-routing/src/middleware.ts#L13-L204)

> [!CAUTION]
> If the value returned from `params.handler` is truthy but fails to satisfy `response instanceof Response`, the adapter immediately throws a `TypeError('Expected an instance of Response to be returned')`, halting request propagation.
> Sources: [packages/next/src/server/web/adapter.ts:362-365](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts#L362-L365)

### Response Parsing and Header Transformation Tables

The response handling logic inspects specific custom headers to determine how request headers and downstream routing destinations are mutated.

| Middleware Header / Property | Action / Transformation | Target Header / Result Field |
| --- | --- | --- |
| `x-middleware-override-headers` | Splits comma-separated keys, deletes request headers not present in the override set, and applies values prefixed with `x-middleware-request-` | `requestHeaders` mutations |
| `x-middleware-rewrite` | Parses destination URL, normalizes against base URL, and sets internal routing headers | `x-middleware-rewrite`, `x-nextjs-rewrite`, `result.rewrite` |
| `location` | Validates status against allowed redirect status codes (`301`, `302`, `303`, `307`, `308`), converts URL format, and records redirect status | `location`, `result.redirect` |
| `x-middleware-refresh` | Sets explicit body sent flag when no rewrite, next, or location header is present | `result.bodySent = true` |
| `x-middleware-set-cookie` | Appends or sets cookie values specifically onto request headers | `requestHeaders` |

Sources: [packages/next-routing/src/middleware.ts:36-196](https://github.com/blade47/next-routing/src/middleware.ts#L36-L196)

### Adapter Design Trade-Offs

| Design Choice | Benefit | Cost |
| --- | --- | --- |
| Explicit `Response` instance validation | Catches invalid middleware return types early with a descriptive `TypeError` before header parsing runs | Fails the request execution abruptly if middleware developers return plain objects or strings instead of `NextResponse` |
| Deferred body-close dispatch via `setTimeout` / `CloseController` | Allows asynchronous task registration via `waitUntil` to complete before request storage and stream resources are torn down | Delays resource cleanup until the next event loop turn, complicating deterministic testing of stream lifetimes |
| Automatic relative URL conversion via `getRelativeURL()` | Normalizes internal redirects and rewrites to relative paths when origins match, avoiding cross-domain routing errors | Requires extra `URL` parsing overhead for every outgoing rewrite or redirect header |

Sources: [packages/next/src/server/web/adapter.ts:347-365](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts#L347-L365), [packages/next/src/server/web/edge-route-module-wrapper.ts:159-174](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/edge-route-module-wrapper.ts#L159-L174), [packages/next-routing/src/middleware.ts:141-189](https://github.com/blade47/next-routing/src/middleware.ts#L141-L189)

## Sandbox Runtime and Context Evaluation

### Overview

Executing edge middleware within isolated runtime environments requires constructing dedicated module contexts, managing asynchronous execution scopes, and stripping restricted transport headers. The sandbox architecture coordinates runtime initialization, request body stream cloning, and error stack-frame mapping for development mode.

Sources: [packages/next/src/server/web/sandbox/sandbox.ts:1-163](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L1-L163)

### Execution Walkthrough and Context Initialization

The sandbox entry point orchestrates runtime setup and handler invocation through a sequence of discrete operations.

1. `getRuntimeContext(params)` calls `getModuleContext()` to acquire the compiled edge runtime and evaluation helper, attaches incremental caching (`__incrementalCache`, `__incrementalCacheShared`), router server context symbols, HMR caches (`__serverComponentsHmrCache`), and client asset tokens (`NEXT_CLIENT_ASSET_SUFFIX`), then evaluates all provided module paths.
   Sources: [packages/next/src/server/web/sandbox/sandbox.ts:72-109](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L72-L109)
2. `run(params)` wraps the execution flow using `withTaggedErrors`, invokes `getRuntimeContext(params)` to retrieve the runtime, and extracts the default export function from `runtime.context._ENTRIES['middleware_' + params.name]`.
   Sources: [packages/next/src/server/web/sandbox/sandbox.ts:49-70](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L49-L70), [packages/next/src/server/web/sandbox/sandbox.ts:111-116](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L111-L116)
3. Request body streams are inspected via `['HEAD', 'GET'].includes(params.request.method)`. For methods carrying payloads, `params.request.body?.cloneBodyStream()` duplicates the stream.
   Sources: [packages/next/src/server/web/sandbox/sandbox.ts:118-120](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L118-L120)
4. `edgeSandboxNextRequestContext.run()` and `requestStore.run()` establish the async local storage contexts before executing the `edgeFunction` with a normalized request object that wraps cloned streams via `requestToBodyStream()`.
   Sources: [packages/next/src/server/web/sandbox/sandbox.ts:134-155](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L134-L155)
5. `FORBIDDEN_HEADERS` are systematically deleted from the resulting response object, and the request body stream is finalized in a `finally` block.
   Sources: [packages/next/src/server/web/sandbox/sandbox.ts:23-27](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L23-L27), [packages/next/src/server/web/sandbox/sandbox.ts:152-162](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L152-L162)

### Sandbox Configuration and Forbidden Headers

| Constant / Parameter | Target Value / Type | Purpose / Behavior |
| --- | --- | --- |
| `ErrorSource` | `Symbol('SandboxError')` | Identifies errors originating specifically within the sandbox execution environment |
| `FORBIDDEN_HEADERS` | `['content-length', 'content-encoding', 'transfer-encoding']` | Restricted transport headers stripped from edge function responses before propagation |
| `NEXT_CLIENT_ASSET_SUFFIX` | String (e.g., `?dpl=<token>`) | Global asset suffix injected into `globalThis` when a client asset token is present |
| `__incrementalCache` | Cache instance | Shared incremental cache reference attached to the edge runtime global scope |

Sources: [packages/next/src/server/web/sandbox/sandbox.ts:21-27](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L21-L27), [packages/next/src/server/web/sandbox/sandbox.ts:84-87](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L84-L87), [packages/next/src/server/web/sandbox/sandbox.ts:100-103](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L100-L103)

### Sandbox Runtime Design Trade-Offs

| Design Choice | Benefit | Cost |
| --- | --- | --- |
| Conditional error tagging via `withTaggedErrors` in development | Maps stack frames to original source locations using `getServerError(error, 'edge-server')` during development | Adds runtime conditional checks and node stack frame module loading overhead in non-production environments |
| Explicit `FORBIDDEN_HEADERS` stripping post-execution | Prevents edge functions from malforming underlying transport encoding or content length declarations | Requires traversing and deleting specific header keys on every successful response return |
| Request body stream cloning based on HTTP method | Avoids consumption errors on idempotent GET and HEAD requests while preserving payloads for mutating requests | Requires explicit stream finalization logic in a `finally` block to prevent resource leaks |

Sources: [packages/next/src/server/web/sandbox/sandbox.ts:49-70](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L49-L70), [packages/next/src/server/web/sandbox/sandbox.ts:118-120](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L118-L120), [packages/next/src/server/web/sandbox/sandbox.ts:152-162](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L152-L162)

> [!WARNING]
> If an edge function execution fails to return a response object (resulting in an undefined `result`), `run()` immediately throws an explicit `Error('Edge function did not return a response')` rather than defaulting to an empty response.
> Sources: [packages/next/src/server/web/sandbox/sandbox.ts:158-158](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L158-L158)

### Full Worked Example: Sandbox Execution Runner

The following example demonstrates how `run` initializes runtime context, handles request body stream cloning for non-GET/HEAD methods, and delegates execution through nested async storage contexts.

```typescript
import { run } from './sandbox'
import type { NodejsRequestData, FetchEventResult } from '../types'

async function executeMiddlewareSandbox(
  requestData: NodejsRequestData,
  modulePaths: string[]
): Promise<FetchEventResult> {
  const result = await run({
    name: 'middleware',
    paths: modulePaths,
    request: requestData,
    useCache: true,
    edgeFunctionEntry: {
      assets: [],
      wasm: [],
      env: {},
    },
    distDir: '.next',
    clientAssetToken: 'secure-token-123',
    onError: (err) => console.error('Sandbox error:', err),
    onWarning: (warn) => console.warn('Sandbox warning:', warn),
  })

  return result
}
```

Sources: [packages/next/src/server/web/sandbox/sandbox.ts:29-43](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L29-L43), [packages/next/src/server/web/sandbox/sandbox.ts:111-163](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/sandbox.ts#L111-L163)

## Development Bundling and Source Maps

### Overview

The development bundler and hot reloader coordinate on-demand compilation, file watching, and source frame resolution for middleware and application routes. File watchers monitor project directories with an aggregate timeout of 5 milliseconds to detect modifications to configuration, environment files, or route entrypoints. When changes occur, the bundler aggregates file states, validates convention files, and maps source locations for error overlays.

Sources: [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:402-422](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L402-L422)

### File Watching and Aggregation Call-Chain

File change detection and entrypoint evaluation execute through a structured asynchronous pipeline when Watchpack triggers an aggregation event:

1. `wp.on('aggregated')` — Fires when file modifications settle past the 5ms aggregate timeout.
2. `wp.getTimeInfoEntries()` — Retrieves timestamps and metadata for all known files in the project workspace.
3. `absolutePathToPage()` — Normalizes scanned file paths into internal route identifiers based on page extensions and directory structure.
4. `isMiddlewareFile()` — Evaluates whether a modified file matches the middleware convention (`middleware.ts` or `proxy.ts`).
5. `getStaticInfoIncludingLayouts()` — Inspects static configuration and extracts middleware matchers from the file payload.

Sources: [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:422-458](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L422-L458), [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:548-567](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L548-L567)

> [!WARNING]
> If both `middleware.ts` and `proxy.ts` are detected at the root convention level, the dev bundler immediately throws an error requiring the use of `proxy.ts` exclusively.
> Sources: [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:479-486](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L479-L486)

### Source Map and Stack Frame Resolution

Source maps and original stack frames are resolved through middleware handlers that query client, server, and edge server webpack compilations. The resolution sequence inspects native node source maps or webpack bundle modules to map compiled line and column positions back to original source files.

| Middleware Endpoint | Method | Purpose |
| --- | --- | --- |
| `/__nextjs_original-stack-frames` | POST | Receives serialized stack frames and resolves them to original source positions and code frames |
| `/__nextjs_source-map` | GET | Looks up compilation artifacts for a specified filename and returns its raw source map JSON payload |
| `/__nextjs_launch-editor` | GET | Opens source files in the user's configured text editor at a specific line and column coordinate |

Sources: [packages/next/src/server/dev/middleware-webpack.ts:592-677](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts#L592-L677), [packages/next/src/server/dev/middleware-webpack.ts:697-744](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts#L697-L744)

> [!TIP]
> When resolving original stack frames, sources containing `node_modules`, `next/dist`, or starting with `node:` are automatically marked as ignored unless explicitly requested otherwise.
> Sources: [packages/next/src/server/dev/middleware-webpack.ts:36-43](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts#L36-L43), [packages/next/src/server/dev/middleware-webpack.ts:237-243](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts#L237-L243)

### Development Bundling Design Trade-Offs

| Design Choice | Benefit | Cost |
| --- | --- | --- |
| Short 5ms Watchpack `aggregateTimeout` | Minimizes bootup dead time and speeds up initial reaction to file changes | Increases frequency of aggregation passes during rapid batch file modifications |
| Multi-compilation stats fallback order (Client → Server → Edge) | Correctly targets the right bundle source map whether an error originates from client components, SSR, or edge runtime | Requires querying multiple webpack compilation graphs sequentially until a matching module ID or source map is found |
| Eager ignore-list checking via `shouldIgnoreSource` | Keeps error overlays clean by automatically filtering out third-party framework internals and library frames | Adds path-string checking overhead during stack frame transformation |

Sources: [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts:402-403](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts#L402-L403), [packages/next/src/server/dev/middleware-webpack.ts:36-43](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts#L36-L43), [packages/next/src/server/dev/middleware-webpack.ts:485-515](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/middleware-webpack.ts#L485-L515)

## Related

- [Edge Sandbox Context](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/edge-and-sandbox/edge-sandbox-context)
- [Routing and Normalization](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/server-runtime/routing-and-normalization)


## Sitemap

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