---
title: "Web Spec Adapters"
description: "Web Spec Adapters form the bridging layer in Next.js that unifies native Node.js HTTP primitives (IncomingMessage, ServerResponse) and Web Standard Fetch API primitives (Request, Response, Headers,..."
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/web-spec-adapters"
---

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

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

- [packages/next/src/server/web/spec-extension/adapters/next-request.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/next-request.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/src/server/web/adapter.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/adapter.ts)
- [packages/next/src/server/base-http/helpers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/helpers.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/web/exports/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/exports/index.ts)
- [packages/next/src/server/base-http/web.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.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/server/base-http/node.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.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/src/server/web/spec-extension/request.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/request.ts)
- [packages/next/server.js](https://github.com/blade47/next.js/blob/main/packages/next/server.js)
- [packages/next/src/server/web/spec-extension/response.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/response.ts)
- [packages/next/server.d.ts](https://github.com/blade47/next.js/blob/main/packages/next/server.d.ts)
- [packages/next/src/server/web/spec-extension/fetch-event.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/fetch-event.ts)
- [packages/next/src/server/send-response.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/send-response.ts)
- [packages/next/src/server/web/spec-extension/cookies.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/cookies.ts)
- [packages/next/src/server/web/spec-extension/adapters/reflect.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/reflect.ts)
- [packages/next/src/server/after/builtin-request-context.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/after/builtin-request-context.ts)
- [packages/next/src/experimental/testmode/server-edge.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/experimental/testmode/server-edge.ts)
- [packages/next/src/experimental/testmode/context.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/experimental/testmode/context.ts)
- [packages/next/src/experimental/testmode/server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/experimental/testmode/server.ts)
- [packages/next/src/server/web/globals.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/globals.ts)
- [packages/next/src/server/base-http/index.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/index.ts)
- [packages/next/src/server/web/spec-extension/adapters/headers.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts)
- [packages/next/src/next-devtools/server/middleware-response.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/next-devtools/server/middleware-response.ts)
- [packages/next/src/server/web/sandbox/context.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts)
- [packages/next/src/server/web/spec-extension/url-pattern.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/url-pattern.ts)
- [packages/next/src/server/app-render/stream-ops.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/app-render/stream-ops.ts)
- [packages/next/src/api/server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/api/server.ts)
</details>

## Overview

### Overview Sub-Section
Web Spec Adapters form the bridging layer in Next.js that unifies native Node.js HTTP primitives (`IncomingMessage`, `ServerResponse`) and Web Standard Fetch API primitives (`Request`, `Response`, `Headers`, `URL`) across dual execution runtimes: the traditional Node.js server runtime and the lightweight Edge runtime. Because Next.js features such as Middleware, App Route handlers, and Server Actions expose standard Web API interfaces (`NextRequest`, `NextResponse`), the server infrastructure requires a robust translation subsystem to wrap, normalize, and unwrap requests and responses irrespective of where code executes.

Sources: [packages/next/src/server/web/spec-extension/adapters/next-request.ts:1-151](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/next-request.ts#L1-L151)

### Purpose and Mechanics
The architecture bridges the mismatch between Node.js event-emitter based stream mechanics and WHATWG Fetch streaming specifications. It achieves this by introducing runtime HTTP wrappers (`NodeNextRequest`, `NodeNextResponse`, `WebNextRequest`, `WebNextResponse`), request adapters (`NextRequestAdapter`), and spec-extension classes (`NextRequest`, `NextResponse`) equipped with proxy-based casing preservation, cookie interception, and abort signal tracking.

Sources: [packages/next/src/server/base-http/node.ts:1-170](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.ts#L1-L170), [packages/next/src/server/base-http/web.ts:1-141](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.ts#L1-L141)

### Cross-Runtime Integration
By decoupling application handlers from platform-specific IO objects, Web Spec Adapters allow user code to consume standard Web APIs while giving Next.js fine-grained control over header casing, stream consumption tracking, request lifecycle abortion, and telemetry context propagation.

Sources: [packages/next/src/server/web/spec-extension/adapters/next-request.ts:1-151](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/next-request.ts#L1-L151)

## Architecture of Runtime HTTP Abstractions

### Request and Response Wrappers
Next.js defines runtime-specific HTTP representations in `packages/next/src/server/base-http/node.ts` and `packages/next/src/server/base-http/web.ts`. In the Node.js runtime, `NodeNextRequest` wraps `IncomingMessage` and provides streaming conversion via `stream()`, while `NodeNextResponse` wraps `ServerResponse` to manage headers, status codes, and completion listeners. In the Edge runtime, `WebNextRequest` and `WebNextResponse` manage standard `Request`, `TransformStream`, and `Response` objects.

Sources: [packages/next/src/server/base-http/node.ts:19-169](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.ts#L19-L169), [packages/next/src/server/base-http/web.ts:11-140](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.ts#L11-L140)

### Runtime Abstraction Diagram
```mermaid
classDiagram
  class NodeNextRequest {
    +IncomingMessage _req
    +IncomingHttpHeaders headers
    +FetchMetric[] fetchMetrics
    +stream() ReadableStream
    +get originalRequest()
  }

  class NodeNextResponse {
    +ServerResponse _res
    +number statusCode
    +string statusMessage
    +setHeader(name, value)
    +appendHeader(name, value)
    +getHeader(name)
    +send()
    +onClose(callback)
  }

  class WebNextRequest {
    +Request request
    +IncomingHttpHeaders headers
    +FetchMetrics fetchMetrics
    +parseBody()
  }

  class WebNextResponse {
    +TransformStream transformStream
    +number statusCode
    +string statusMessage
    +setHeader(name, value)
    +appendHeader(name, value)
    +toResponse() Promise~Response~
    +onClose(callback)
  }

  class NextRequest {
    +RequestCookies cookies
    +NextURL nextUrl
    +string url
  }

  class NextResponse {
    +ResponseCookies cookies
    +static json()
    +static redirect()
    +static rewrite()
    +static next()
  }

  NodeNextRequest ..> NextRequest : adapted by NextRequestAdapter
  WebNextRequest ..> NextRequest : adapted by NextRequestAdapter
```

Sources: [packages/next/src/server/base-http/node.ts:19-169](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.ts#L19-L169), [packages/next/src/server/base-http/web.ts:11-140](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.ts#L11-L140), [packages/next/src/server/web/spec-extension/request.ts:14-105](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/request.ts#L14-L105), [packages/next/src/server/web/spec-extension/response.ts:36-153](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/response.ts#L36-L153)

### Runtime Bifurcation
The concrete implementations are selected according to environment checks defined in `packages/next/src/server/base-http/helpers.ts`. When `process.env.NEXT_RUNTIME === 'edge'`, type guards `isWebNextRequest` and `isWebNextResponse` return `true`. Otherwise, `isNodeNextRequest` and `isNodeNextResponse` evaluate to `true` for Node.js execution.

Sources: [packages/next/src/server/base-http/helpers.ts:18-49](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/helpers.ts#L18-L49)

## NextRequestAdapter and Request Translation

### Adapter Purpose
The `NextRequestAdapter` class in `packages/next/src/server/web/spec-extension/adapters/next-request.ts` converts runtime request representations into `NextRequest` instances suitable for route handlers, middleware, and server rendering.

Sources: [packages/next/src/server/web/spec-extension/adapters/next-request.ts:56-151](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/next-request.ts#L56-L151)

### Request Translation Flow Diagram
```mermaid
flowchart TD
  A["Incoming Request"] --> B{"process.env.NEXT_RUNTIME === 'edge'?"}
  B -->|Yes| C["NextRequestAdapter.fromWebNextRequest(req)"]
  B -->|No| D["NextRequestAdapter.fromNodeNextRequest(req, signal)"]
  C --> E["Return NextRequest (Web stream body)"]
  D --> F["Attach signalFromNodeResponse & Undici/Node body"]
  F --> E
```

Sources: [packages/next/src/server/web/spec-extension/adapters/next-request.ts:56-151](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/next-request.ts#L56-L151)

### Execution Pipeline Details
1. **Selection:** `NextRequestAdapter.fromBaseNextRequest()` evaluates `process.env.NEXT_RUNTIME === 'edge'` alongside `isWebNextRequest` and `isNodeNextRequest` to branch between Edge and Node conversion logic.
2. **Method & Body Inspection:** GET and HEAD requests explicitly drop body initialization (`let body = null`) per HTTP specifications. For Node requests, non-GET/HEAD bodies are passed directly with `duplex: 'half'`.
3. **URL Resolution:** If `request.url` is absolute, a `URL` object is instantiated directly. If relative, metadata (`initURL` from request meta) or a dummy base (`http://n`) is supplied to construct a valid absolute `URL`.
4. **Abort Signal Binding:** `signalFromNodeResponse()` inspects the underlying writable response. If already errored or destroyed, an aborted signal is immediately returned; otherwise, an `AbortController` listens for the `close` event on the Node `ServerResponse`. If `close` fires before `response.writableFinished`, a `ResponseAborted` error aborts the controller.

> [!WARNING]
> Request bodies cannot be passed if the associated abort signal has already been triggered; doing so raises a `Request body was disturbed` error in the underlying Fetch implementation.

Sources: [packages/next/src/server/web/spec-extension/adapters/next-request.ts:16-54](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/next-request.ts#L16-L54), [packages/next/src/server/web/spec-extension/adapters/next-request.ts:80-150](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/next-request.ts#L80-L150)

## HeadersAdapter and Casing Preservation

### Casing Mismatch Problem
Standard WHATWG `Headers` objects lowercase all header names. However, Node.js and proxy environments frequently require preserving or querying original casing, or interfacing directly with raw `IncomingHttpHeaders` objects without expensive cloning.

Sources: [packages/next/src/server/web/spec-extension/adapters/headers.ts:28-60](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L28-L60)

### HeadersAdapter Proxy Mechanism
`HeadersAdapter` in `packages/next/src/server/web/spec-extension/adapters/headers.ts` solves this via a JavaScript `Proxy`. The proxy intercepts `get`, `set`, `has`, and `deleteProperty` traps, normalizes incoming property lookups to lower case, and searches the underlying Node `headers` keys to find the original casing:

```typescript
const original = Object.keys(headers).find(
  (o) => o.toLowerCase() === lowercased
)
```

Sources: [packages/next/src/server/web/spec-extension/adapters/headers.ts:36-115](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L36-L115)

### Class Hierarchy and Sealing
```mermaid
classDiagram
  class Headers {
    +get(name)
    +set(name, value)
    +has(name)
    +delete(name)
  }

  class HeadersAdapter {
    -IncomingHttpHeaders headers
    +static seal(headers) ReadonlyHeaders
    +static from(headers) Headers
  }

  Headers <|-- HeadersAdapter
```

Sources: [packages/next/src/server/web/spec-extension/adapters/headers.ts:28-160](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L28-L160)

### Enforcing Read-Only Semantics
If found, operations execute against the original casing key. Furthermore, `HeadersAdapter.seal()` wraps a `Headers` instance in a proxy that intercepts mutating methods (`append`, `delete`, `set`) and immediately invokes `ReadonlyHeadersError.callable()` to enforce read-only semantics for server components and cached contexts.

Sources: [packages/next/src/server/web/spec-extension/adapters/headers.ts:8-18](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L8-L18), [packages/next/src/server/web/spec-extension/adapters/headers.ts:116-134](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L116-L134)

## NextResponse and Middleware Field Interception

### NextResponse Extensions
`NextResponse` extends the global Web `Response` class, adding helper static methods (`json`, `redirect`, `rewrite`, `next`) and embedded cookie/middleware interception logic.

Sources: [packages/next/src/server/web/spec-extension/response.ts:36-108](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/response.ts#L36-L108)

### Middleware Field Interception Flow
```mermaid
flowchart TD
  A["NextResponse.redirect / rewrite / next"] --> B["Build Headers with Location / x-middleware-*"]
  B --> C["handleMiddlewareField()"]
  C --> D["Extract init.request.headers"]
  D --> E["Set x-middleware-request-{key} and x-middleware-override-headers"]
  E --> F["Return new NextResponse"]
```

Sources: [packages/next/src/server/web/spec-extension/response.ts:12-29](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/response.ts#L12-L29)

### Request Header Overrides
When custom request headers are modified via middleware response initializers (`MiddlewareResponseInit`), `handleMiddlewareField()` iterates over provided request headers, prefixes them with `x-middleware-request-`, and sets `x-middleware-override-headers` so downstream server layers can reconstruct modified request states.

> [!NOTE]
> When `NextResponse.redirect()` is invoked, the status code is validated against a strict set of redirect codes (`301`, `302`, `303`, `307`, `308`). Passing an unsupported status code throws a `RangeError`.

Sources: [packages/next/src/server/web/spec-extension/response.ts:109-153](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/response.ts#L109-L153)

## Response Sending and Pipe Execution

### Transmission Pipeline
Once a route handler or middleware returns a standard `Response` object, `sendResponse()` in `packages/next/src/server/send-response.ts` transmits the payload across the underlying Node.js HTTP transport.

Sources: [packages/next/src/server/send-response.ts:14-25](https://github.com/blade47/next.js/blob/main/packages/next/src/server/send-response.ts#L14-L25)

### Sequence Diagram of sendResponse
```mermaid
sequenceDiagram
  participant Server as NextServer
  participant Sender as sendResponse()
  participant Res as NodeNextResponse
  participant NodeRes as http.ServerResponse

  Server->>Sender: sendResponse(req, res, response, waitUntil)
  Sender->>Res: Set statusCode & statusText
  loop For each response header
    alt Header is set-cookie
      Sender->>Res: splitCookiesString() & appendHeader()
    else Multiple values allowed or not present
      Sender->>Res: appendHeader()
    end
  end
  alt Request method !== HEAD and has body
    Sender->>NodeRes: pipeToNodeResponse(response.body, originalResponse, waitUntil)
  else HEAD request or no body
    Sender->>NodeRes: originalResponse.end()
  end
```

Sources: [packages/next/src/server/send-response.ts:26-84](https://github.com/blade47/next.js/blob/main/packages/next/src/server/send-response.ts#L26-L84)

### Header Normalization Rules
`sendResponse` handles multi-value header normalization explicitly. While most headers overwrite existing values or check presence, headers with multiple values allowed (`set-cookie`, `www-authenticate`, `proxy-authenticate`, `vary`) are split using `splitCookiesString()` and appended individually to the underlying Node `ServerResponse`.

Sources: [packages/next/src/server/send-response.ts:35-67](https://github.com/blade47/next.js/blob/main/packages/next/src/server/send-response.ts#L35-L67)

## Sandbox Context and Edge Runtime Adaptations

### Edge Sandbox Environment
In the Edge Runtime sandbox (`packages/next/src/server/web/sandbox/context.ts`), global objects such as `fetch`, `Request`, and `Response` are patched to inject Next.js-specific capabilities like inline asset fetching, `next` fetch options configuration, and validation checks.

Sources: [packages/next/src/server/web/sandbox/context.ts:401-420](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L401-L420)

### Code Snippet of Edge Request Patching
```typescript
const __Request = context.Request
context.Request = class extends __Request {
  next?: NextFetchRequestConfig | undefined
  constructor(input: URL | RequestInfo, init?: RequestInit | undefined) {
    const url = typeof input !== 'string' && 'url' in input ? input.url : String(input)
    validateURL(url)
    super(input, init)
    this.next = init?.next
  }
}
```

Sources: [packages/next/src/server/web/sandbox/context.ts:401-420](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L401-L420)

### Unsupported API Stubbing
Furthermore, unsupported Node.js APIs (such as filesystem or cluster modules) are stubbed via `__import_unsupported()`, throwing informative runtime errors when invoked inside Edge functions.

Sources: [packages/next/src/server/web/sandbox/context.ts:57-82](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/sandbox/context.ts#L57-L82)

## Runtime Execution & Adapter Integration Table

### Adapter Mapping Table
The following reference table outlines the correspondence between core Web Spec Adapter classes, their underlying transport objects, and their target execution runtimes.

| Adapter Class | Underlying Transport / Source | Target Runtime | Primary Responsibility |
| :--- | :--- | :--- | :--- |
| `NodeNextRequest` | `http.IncomingMessage` | Node.js Server | Wraps Node request stream with body streaming and meta tracking. |
| `NodeNextResponse` | `http.ServerResponse` | Node.js Server | Wraps Node response socket and manages header mutation. |
| `WebNextRequest` | Fetch `Request` / `NextRequestHint` | Edge Runtime | Wraps Web standard requests for edge handlers. |
| `WebNextResponse` | `TransformStream` | Edge Runtime | Manages web stream transformations and translates to standard `Response`. |
| `HeadersAdapter` | `IncomingHttpHeaders` | Universal | Bridges Node headers with Web `Headers` API preserving casing. |

Sources: [packages/next/src/server/base-http/node.ts:19-169](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/node.ts#L19-L169), [packages/next/src/server/base-http/web.ts:11-140](https://github.com/blade47/next.js/blob/main/packages/next/src/server/base-http/web.ts#L11-L140), [packages/next/src/server/web/spec-extension/adapters/headers.ts:28-231](https://github.com/blade47/next.js/blob/main/packages/next/src/server/web/spec-extension/adapters/headers.ts#L28-L231)

## Related

- [Edge Sandbox Context](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/edge-and-sandbox/edge-sandbox-context)
- [Route Handlers](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/app-router-rendering/route-handlers)


## Sitemap

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