Skip to main content
doc0blade47/next.jsdocumentation home
Sign in

Request Lifecycle

Last updated Oct 12, 2026100% grounded
Relevant source files

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

Overview

The request lifecycle orchestrates how HTTP requests are received, parsed, routed, and fulfilled across both Node.js and Edge Runtime environments. It manages request metadata propagation via Async Storage, matches incoming paths against custom routes, headers, and redirects, and normalizes bundle paths for App and Pages directories. The pipeline controls the execution of Server Components, Route Handlers, and legacy Pages APIs while interfacing with the Incremental Cache to dispatch optimized payloads and responses.

Sources: packages/next/src/server/base-server.ts:1071-1080, packages/next/src/server/web/adapter.ts:127-145, packages/next/src/server/request-meta.ts:51-175, packages/next-routing/src/resolve-routes.ts:557-600, packages/next/src/server/normalizers/built/app/app-bundle-path-normalizer.ts:7-15, packages/next/src/server/normalizers/built/pages/pages-bundle-path-normalizer.ts:7-21, packages/next/src/build/templates/app-page-runtime.ts:245-280, packages/next/src/build/templates/pages-api.ts:47-80, packages/next/src/server/send-payload.ts:35-50

Edge and Server Request Metadata

Request Metadata Operations

Per-request metadata tracking in Next.js relies on a shared internal symbol NEXT_REQUEST_META (Symbol.for('NextInternalRequestMeta')) attached directly to incoming message objects (NextIncomingMessage), bridging Node.js HTTP requests, base HTTP wrappers, and Web API request types.

Sources: packages/next/src/server/request-meta.ts:19-27

During request execution, the invocation path passes through specific middleware handling routines. In handleCatchallMiddlewareRequest(), the server checks whether the request has an active middleware invocation flag. When middleware is absent or routing matchers evaluate to false, handleFinished() is invoked. Inside handleFinished(), the call chain invokes addRequestMeta(req, 'middlewareInvoke', true) to record the completed state, which in turn calls getRequestMeta() to read the existing metadata dictionary before setting the key. The full call sequence is handleCatchallMiddlewareRequest → handleFinished → addRequestMeta → getRequestMeta.

Sources: packages/next/src/server/next-server.ts:1943-2053, packages/next/src/server/request-meta.ts:421-429, packages/next/src/server/request-meta.ts:393-399

Sources: packages/next/src/server/next-server.ts:1943-2053, packages/next/src/server/request-meta.ts:421-429, packages/next/src/server/request-meta.ts:393-399

Metadata Fields and Structure

The RequestMeta interface maintains state regarding routing decisions, incremental caching layers, locale negotiation, and development diagnostics.

FieldTypePurpose
initQueryParsedUrlQueryThe initial query string parameters used to make the request.
initURLstringThe full request URL string captured at entry.
initProtocolstringThe protocol scheme (http or https) associated with the request.
clonableBodyCloneableBodyMulti-readable request body stream wrapper.
isLocaleDomainbooleanTrue when the request matches a configured locale domain.
didStripLocalebooleanTrue when locale information was stripped from the URL pathname.
rewrittenPathnamestringThe target pathname if the request URL underwent a rewrite.
middlewareCookiestring[]Cookies added by middleware to be appended to the response.
matchRouteMatchThe resolved route match object for the current pathname.
incrementalCacheIncrementalCacheThe incremental cache provider instance assigned to the request.
serverComponentsHmrCacheServerComponentsHmrCacheDevelopment-only HMR cache for server components.
hmrRefreshHashstringHash of the latest server component change used in "use cache" keys.
segmentPrefetchRSCRequeststringTarget segment path for prefetch RSC requests.
isPrefetchRSCRequesttrueTrue when the request fetches prefetch flight data.
isRSCRequesttrueTrue when the request fetches React Server Component flight data.
cacheBustingSearchParamstringSearch param hash verifying custom headers to prevent cache poisoning on CDNs.
isNextDataReqtrueTrue when requesting /_next/data via the Pages Router.
postponedstringPostponed PPR state string used for resumption rendering.
actionBodyBufferAction payload extracted from server action requests.
onCacheEntryOnCacheEntryHandlerDeprecated callback invoked on response cache hits or generations.
onCacheEntryV2OnCacheEntryHandlerCallback invoked when a response cache entry is generated or retrieved.
notFoundRevalidatenumber | falseRevalidation interval prior to rendering a 404 page for notFound: true.
developmentNotFoundSourcePagestringOriginal source page returning a 404 during development.
invokePathstringInternal routing invocation path.
invokeOutputstringSpecific page output target for matching.
invokeStatusnumberStatus code passed during internal routing invocation.
invokeErrorErrorRouting error object associated with an invocation.
invokeQueryRecord<string, ...>Parsed query parameters for internal routing invocations.
middlewareInvokebooleanFlag indicating whether the request is a middleware invocation.
renderFallbackShellbooleanTrue if the server should render the fallback shell.
resolvedRouteParamKeysSet<string>Route parameter keys explicitly resolved from partial query parameters.
customErrorRendertrueTrue when rendering the custom application error page.
bubbleNoFallbacktrueBubble NoFallbackError to the caller when a 404 occurs.
localeInferredFromDefaulttrueTrue when the locale was inferred from the default configuration.
localestringThe active locale set or inferred for the request.
defaultLocalestringThe default locale configured for the application.
relativeProjectDirstringRelative path of the project directory from the root.
distDirstringBuild output distribution directory.
hostnamestringHostname used by route handlers for absolute URL construction.
revalidateRevalidateFnInternal revalidation function bypassing network calls.
render404FunctionCallback rendering the Pages Router 404 page.
queryParsedUrlQueryResolved query parameters after route matching.
paramsParsedUrlQueryResolved dynamic route parameters after route matching.
PagesErrorDebugPagesDevOverlayBridgeTypeError overlay component bridge for the Pages Router in development.
minimalModebooleanTrue when running the server in minimal resource mode.
stagedFallbackParamsOpaqueFallbackRouteParamsRoute params deferred until after the static stage in PPR.
fallbackRouteParamsOpaqueFallbackRouteParamsRoute params substituted with opaque placeholders during shell debugging.
devParamMatchingRejectedbooleanDevelopment flag indicating parameter matching rejection.
devNotFoundParamsreadonly string[]Closed parameters including allowed values in development.
devRequestTimingStartbigintRequest start timestamp in nanoseconds via process.hrtime.bigint().
devRequestTimingMiddlewareStartbigintMiddleware execution start timestamp in development.
devRequestTimingMiddlewareEndbigintMiddleware execution completion timestamp in development.
devRequestTimingInternalsEndbigintInternal processing completion timestamp in development.
devGenerateStaticParamsDurationbigintDuration of getStaticPaths or generateStaticParams execution.
devServerActionLogobjectServer action execution logs captured in development.

Sources: packages/next/src/server/request-meta.ts:51-375

Note

The NEXT_REQUEST_META symbol allows mutable request attributes to persist across framework boundaries, including Node.js IncomingMessage, custom BaseNextRequest wrappers, and Web NextRequest abstractions, preventing metadata loss during internal request forwarding and middleware runs. Sources: packages/next/src/server/request-meta.ts:18-27

Route Resolution and Header Rewriting

Path Matching and Internationalization

Incoming HTTP requests undergo initial route resolution through resolveRoutes, which evaluates request URLs, normalizes _next/data requests when shouldNormalizeNextData is enabled, and applies internationalization (i18n) locale detection. When i18n is configured and the request does not target _next or api paths, the system inspects cookies, accept-language headers, pathnames, and domain configuration.

// Handle i18n locale detection and redirects
if (i18n && !isDataUrl) {
  const pathname = currentUrl.pathname.startsWith(basePath)
    ? currentUrl.pathname.slice(basePath.length) || '/'
    : currentUrl.pathname
 
  // Skip locale handling for _next and api routes
  if (!pathname.startsWith('/_next/') && !pathname.startsWith('/api/')) {
    const hostname = currentUrl.hostname
    const cookieHeader = currentRequestHeaders.get('cookie') || undefined
    const acceptLanguageHeader =
      currentRequestHeaders.get('accept-language') || undefined
 
    // Detect locale from path first
    const pathLocaleResult = normalizeLocalePath(pathname, i18n.locales)
    const localeInPath = !!pathLocaleResult.detectedLocale
 
    // Detect domain locale
    const domainLocale = detectDomainLocale(i18n.domains, hostname)
    const defaultLocale = domainLocale?.defaultLocale || i18n.defaultLocale
 
    // Determine target locale if locale detection is enabled
    let targetLocale = pathLocaleResult.detectedLocale || defaultLocale
 
    // Match Next.js behavior: preferred-locale auto-detection redirects only
    // on index requests, not on arbitrary non-locale pathnames.
    const shouldDetectPreferredLocale =
      i18n.localeDetection !== false &&
      !localeInPath &&
      pathLocaleResult.pathname === '/'
 
    if (shouldDetectPreferredLocale) {
      const detectedResult = detectLocale({
        pathname,
        hostname,
        cookieHeader,
        acceptLanguageHeader,
        i18n,
      })
 
      targetLocale = detectedResult.locale

Sources: packages/next-routing/src/resolve-routes.ts:602-643

If preferred-locale auto-detection triggers on root index requests without an explicit path locale, the request redirects to the detected locale or domain. Otherwise, the locale is prefixed internally for route resolution without triggering an HTTP redirect.

Tip

Preferred-locale auto-detection is restricted strictly to root index requests (pathLocaleResult.pathname === '/') when no explicit locale exists in the path, preventing unexpected redirects on nested application routes. Sources: packages/next-routing/src/resolve-routes.ts:628-632

Middleware Execution and Header Overrides

After processing beforeMiddleware rules, the routing engine evaluates whether the request matches any middleware matchers. If matched, middleware is invoked, and any resulting headers, redirects, or rewrites are applied to the request and response objects.

const middlewareHeaders = toNodeOutgoingHttpHeaders(
  middlewareRes.headers
) as Record<string, string | string[] | undefined>
 
debug('middleware res', middlewareRes.status, middlewareHeaders)
 
if (middlewareHeaders['x-middleware-override-headers']) {
  const overriddenHeaders: Set<string> = new Set()
  let overrideHeaders: string | string[] =
    middlewareHeaders['x-middleware-override-headers']
 
  if (typeof overrideHeaders === 'string') {
    overrideHeaders = overrideHeaders.split(',')
  }
 
  for (const key of overrideHeaders) {
    overriddenHeaders.add(key.trim())
  }
  delete middlewareHeaders['x-middleware-override-headers']
 
  // Delete headers.
  for (const key of Object.keys(req.headers)) {
    if (!overriddenHeaders.has(key)) {
      delete req.headers[key]
    }
  }
 
  // Update or add headers.
  for (const key of overriddenHeaders.keys()) {
    const valueKey = 'x-middleware-request-' + key
    const newValue = middlewareHeaders[valueKey]
    const oldValue = req.headers[key]
 
    if (oldValue !== newValue) {
      req.headers[key] = newValue === null ? undefined : newValue
    }
    delete middlewareHeaders[valueKey]
  }
}

Sources: packages/next/src/server/lib/router-utils/resolve-routes.ts:662-700

When middleware returns x-middleware-override-headers, the server parses the specified header names, strips unlisted incoming request headers, and applies new values prefixed with x-middleware-request-. Headers explicitly blocked by ipcForbiddenHeaders or control keys such as content-length, x-middleware-rewrite, x-middleware-redirect, and x-middleware-refresh are filtered out or handled specially.

Caution

The x-middleware-set-cookie header is assigned directly to request headers (req.headers['x-middleware-set-cookie'] = value) rather than copied to response headers, ensuring cookies passed from middleware persist for downstream request processing. Sources: packages/next/src/server/lib/router-utils/resolve-routes.ts:725-730

Dynamic Routes and Condition Evaluation

Once file system checks and beforeFiles rewrites complete, dynamic routes are scanned against pathnames and query conditions using has and missing parameters.

for (const route of routes.dynamicRoutes) {
  const match = matchDynamicRoute(currentUrl.pathname, route, caseSensitive)
 
  if (!match.matched) {
    continue
  }
 
  const hasResult = checkHasConditions(
    route.has,
    currentUrl,
    currentRequestHeaders
  )
  const missingMatched = checkMissingConditions(
    route.missing,
    currentUrl,
    currentRequestHeaders
  )
 
  if (!hasResult.matched || !missingMatched) {
    continue
  }

Sources: packages/next-routing/src/resolve-routes.ts:881-901

If a dynamic route matches both pattern criteria and validation conditions, its destination is resolved via query merging and matched headers from routes.onMatch are appended to the response context.

Path Normalization and Manifest Mapping

App Router Normalizers

The App Router utilizes specialized normalizers to transform route strings and filenames into canonical bundle paths. The AppBundlePathNormalizer extends PrefixingNormalizer with an 'app' prefix, applying normalizePagePath to convert pages into their normalized pathname representations. Sources: packages/next/src/server/normalizers/built/app/app-bundle-path-normalizer.ts:7-15

In development mode, DevAppBundlePathNormalizer orchestrates a composite normalizer chain via Normalizers. It combines the incoming page normalizer and an AppBundlePathNormalizer, conditionally prepending an UnderscoreNormalizer when Turbopack is active to handle %5F to _ replacements. Sources: packages/next/src/server/normalizers/built/app/app-bundle-path-normalizer.ts:17-31

Note

The %5F to _ underscore replacement in bundle paths is restricted strictly to Turbopack compilation runs within DevAppBundlePathNormalizer. Sources: packages/next/src/server/normalizers/built/app/app-bundle-path-normalizer.ts:26-29

Pages Router Normalizers

For the legacy Pages Router, PagesBundlePathNormalizer wraps normalizePagePath to strip trailing /index segments from bundle paths before passing them through a PrefixingNormalizer initialized with the 'pages' prefix. Sources: packages/next/src/server/normalizers/built/pages/pages-bundle-path-normalizer.ts:7-16

The corresponding development variant, DevPagesBundlePathNormalizer, composes the pages-specific normalizer with a runtime page normalizer to translate filenames into target route paths during on-demand compilation. Sources: packages/next/src/server/normalizers/built/pages/pages-bundle-path-normalizer.ts:23-31

Normalizer ClassBase Class / WrapperPrefix / TargetCore Operation
AppBundlePathNormalizerPrefixingNormalizer'app'Normalizes page paths via normalizePagePath and prefixes with 'app'
DevAppBundlePathNormalizerNormalizers'app' (+ optional UnderscoreNormalizer)Combines page normalizer, app normalizer, and conditional Turbopack underscore normalization
PagesBundlePathNormalizerNormalizers'pages'Strips trailing /index via normalizePagePath and prefixes with 'pages'
DevPagesBundlePathNormalizerNormalizers'pages'Chains pages file normalizer with PagesBundlePathNormalizer

Sources: packages/next/src/server/normalizers/built/app/app-bundle-path-normalizer.ts:7-31, packages/next/src/server/normalizers/built/pages/pages-bundle-path-normalizer.ts:7-31

App Page Runtime Execution Pipeline

Runtime Template Invocation and Handler Initialization

The App Page runtime execution pipeline begins when incoming requests are processed by generated runtime templates such as packages/next/src/build/templates/app-page-runtime.ts and Edge SSR templates like packages/next/src/build/templates/edge-ssr-app.ts. The main handler function extracts request metadata, normalizes source paths via normalizeAppPath, and parses dynamic route segment parameter names from the route pattern. Sources: packages/next/src/build/templates/app-page-runtime.ts:206-222

async function handler(
  req: IncomingMessage,
  res: ServerResponse,
  ctx: {
    waitUntil?: (prom: Promise<void>) => void
    requestMeta?: RequestMeta
  }
) {
  if (ctx.requestMeta) {
    setRequestMeta(req, ctx.requestMeta)
  }

Sources: packages/next/src/build/templates/app-page-runtime.ts:215-225

Requests are prepared via routeModule.prepare, evaluating multi-zone draft mode configuration and returning a comprehensive context object containing build manifests, router contexts, query parameters, and configuration properties. Sources: packages/next/src/build/templates/app-page-runtime.ts:245-280

Warning

Failing to capture req.url into httpTarget before routeModule.prepare runs will cause subsequent CDN URL rewrites to corrupt trace span attributes. Sources: packages/next/src/build/templates/app-page-runtime.ts:236-241

PPR Resumption and Server Actions Handling

When Partial Prerendering (PPR) is enabled via nextConfig.cacheComponents, incoming requests accompanied by postponed resume state headers undergo body size and limit validations. If NEXT_RESUME_STATE_LENGTH_HEADER is present alongside a server action request, the runtime reads the request body with a combined size budget comprising the state length and action body limit. Sources: packages/next/src/build/templates/app-page-runtime.ts:349-390

const resumeStateLengthHeader = req.headers[NEXT_RESUME_STATE_LENGTH_HEADER]
if (
  !getRequestMeta(req, 'postponed') &&
  isMinimalMode &&
  couldSupportPPR &&
  isPossibleServerAction &&
  resumeStateLengthHeader &&
  typeof resumeStateLengthHeader === 'string'
) {
  const stateLength = parseInt(resumeStateLengthHeader, 10)
  const { maxPostponedStateSize, maxPostponedStateSizeBytes } =
    getMaxPostponedStateSize(nextConfig.experimental.maxPostponedStateSize)
 
  if (!isNaN(stateLength) && stateLength > 0) {
    if (stateLength > maxPostponedStateSizeBytes) {
      res.statusCode = 413
      res.end(getPostponedStateExceededErrorMessage(maxPostponedStateSize))
      ctx.waitUntil?.(Promise.resolve())
      return null
    }

Sources: packages/next/src/build/templates/app-page-runtime.ts:353-372

The raw request buffer is subdivided into the postponed state string—stored in request metadata under 'postponed'—and the remaining action body payload destined for the server action handler. Sources: packages/next/src/build/templates/app-page-runtime.ts:403-411

Route Invocation and Edge SSR Adapters

Route modules execute rendering operations by wrapping Node request and response objects into NodeNextRequest and NodeNextResponse adapters, dispatching either routeModule.prerender or routeModule.render depending on the active render operation. Sources: packages/next/src/build/templates/app-page-runtime.ts:777-789

const invokeRouteModule = async (
  span: Span | undefined,
  context: AppPageRouteHandlerContext,
  renderOperation: AppPageRenderOperation
) => {
  const nextReq = new NodeNextRequest(req)
  const nextRes = new NodeNextResponse(res)
 
  const result =
    renderOperation === 'prerender'
      ? routeModule.prerender(nextReq, nextRes, context)
      : routeModule.render(nextReq, nextRes, context)
 
  return result.finally(() => {

Sources: packages/next/src/build/templates/app-page-runtime.ts:777-790

In Edge SSR runtime templates, renderResultToResponse converts render outcomes into standard Web Response instances, setting appropriate x-edge-runtime headers, mapping fetch metrics, and piping dynamic readable streams via TransformStream. Sources: packages/next/src/build/templates/edge-ssr-app.ts:250-328

Operation ContextTarget Parameter / HeaderDefault / Fallback ValuePurpose
Action Body LimitnextConfig.experimental.serverActions?.bodySizeLimit'1 MB' (1024 * 1024 bytes)Bounded payload size check for server action requests
Postponed State SizenextConfig.experimental.maxPostponedStateSizePlatform default limitsValidates incoming PPR postponed state length against configuration
Response Content-Typeresult.contentType'text/html; charset=utf-8'Establishes mime type for Edge SSR responses
Edge Runtime Header'x-edge-runtime''1'Identifies responses originating from the V8 edge sandbox

Sources: packages/next/src/build/templates/app-page-runtime.ts:378-387, packages/next/src/build/templates/edge-ssr-app.ts:272-274

Pages API Template Request Processing

Request Preparation and Route Module Initialization

The legacy Pages API handler initializes by accepting incoming IncomingMessage and ServerResponse objects alongside a context object containing optional waitUntil and requestMeta properties. If requestMeta is provided, it is attached to the request, and development timing metrics are recorded if routeModule.isDev is active. For Turbopack environments, dynamic route page paths ending in /index are normalized to ensure correct path matching. Sources: packages/next/src/build/templates/pages-api.ts:47-68

const prepareResult = await routeModule.prepare(req, res, { srcPage })
 
if (!prepareResult) {
  res.statusCode = 400
  res.end('Bad Request')
  ctx.waitUntil?.(Promise.resolve())
  return
}

Sources: packages/next/src/build/templates/pages-api.ts:70-77

Warning

If routeModule.prepare returns a falsy result, the response status code is immediately set to 400 with a "Bad Request" body, short-circuiting execution before any userland route module rendering occurs. Sources: packages/next/src/build/templates/pages-api.ts:72-77

Tracing Context and Route Execution

When preparation succeeds, the request method and active OpenTelemetry tracer are retrieved to establish distributed tracing spans. Depending on whether the server wraps the request, the handler either uses an existing active span or initiates a new BaseServerSpan.handleRequest trace with server span kinds and route attributes. Sources: packages/next/src/build/templates/pages-api.ts:79-92, packages/next/src/build/templates/pages-api.ts:172-196

const invokeRouteModule = async (span?: Span) =>
  routeModule
    .render(req, res, {
      query: {
        ...query,
        ...params,
      },
      params,
      allowedRevalidateHeaderKeys: process.env
        .__NEXT_ALLOWED_REVALIDATE_HEADERS as any as string[],
      multiZoneDraftMode: Boolean(process.env.__NEXT_MULTI_ZONE_DRAFT_MODE),
      trustHostHeader: process.env
        .__NEXT_TRUST_HOST_HEADER as any as boolean,
      // TODO: get this from from runtime env so manifest
      // doesn't need to load
      previewProps,
      propagateError: false,
      dev: routeModule.isDev,
      page: 'VAR_DEFINITION_PAGE',
 
      internalRevalidate: routerServerContext?.revalidate,
 
      onError: (...args: Parameters<InstrumentationOnRequestError>) =>
        onRequestError(req, ...args),
    })

Sources: packages/next/src/build/templates/pages-api.ts:93-117

Spans are finalized via .finally() hooks that inspect res.statusCode. Status codes of 500 or greater trigger SpanStatusCode.ERROR along with error.type attributes. Root span attributes are verified to match BaseServerSpan.handleRequest before updating trace span names with HTTP methods and matched route paths. Sources: packages/next/src/build/templates/pages-api.ts:118-170

Error Boundary Traps and Response Delivery

Uncaught exceptions thrown during route module invocation are intercepted by a catch block. In development mode, errors are re-thrown directly to trigger the error overlay. In production environments, an internal server error is dispatched using sendError, serving a 500 status response. Sources: packages/next/src/build/templates/pages-api.ts:197-205

} finally {
  // We don't allow any waitUntil work in pages API routes currently
  // so if callback is present return with resolved promise since no
  // pending work
  ctx.waitUntil?.(Promise.resolve())
}

Sources: packages/next/src/build/templates/pages-api.ts:205-210

Note

Pages API routes do not currently permit asynchronous background work via waitUntil, so the finally block immediately resolves any pending waitUntil promise callbacks. Sources: packages/next/src/build/templates/pages-api.ts:206-210

Response Payload Dispatch and Caching

ETag Validation and Caching Headers

The payload dispatch pipeline verifies response state and sets caching and metadata headers before outputting content. If a response has already been sent, execution returns immediately. When poweredByHeader is enabled and the response content type matches the HTML content type header, the X-Powered-By: Next.js header is assigned. Sources: packages/next/src/server/send-payload.ts:35-56

// If cache control is already set on the response we don't
// override it to allow users to customize it via next.config
if (cacheControl && !res.getHeader('Cache-Control')) {
  res.setHeader('Cache-Control', getCacheControlHeader(cacheControl))
}
 
const payload = result.isDynamic ? null : result.toUnchunkedString()
 
if (generateEtags && payload !== null) {
  const etag = generateETag(payload)
  if (sendEtagResponse(req, res, etag)) {
    return
  }
}

Sources: packages/next/src/server/send-payload.ts:58-71

Cache control headers are populated using getCacheControlHeader if not already present, preserving user customizations set via next.config. For unchunked static payloads, ETags are generated and evaluated via sendEtagResponse. If the request headers match the generated ETag using fresh, status 304 is assigned, the connection is ended, and dispatch terminates early. Sources: packages/next/src/server/send-payload.ts:11-33, packages/next/src/server/send-payload.ts:58-71

Warning

When generating a 304 Not Modified response, the server sets required validation headers such as ETag per RFC 7232 before ending the response without a body. Sources: packages/next/src/server/send-payload.ts:16-24

Payload Streaming and Node Response Piping

if (!res.getHeader('Content-Type') && result.contentType) {
  res.setHeader('Content-Type', result.contentType)
}
 
if (payload) {
  res.setHeader('Content-Length', Buffer.byteLength(payload))
}
 
if (req.method === 'HEAD') {
  res.end(null)
  return
}
 
if (payload !== null) {
  res.end(payload)
  return
}
 
// Pipe the render result to the response after we get a writer for it.
await result.pipeToNodeResponse(res)

Sources: packages/next/src/server/send-payload.ts:73-92

Content type and content length headers are registered for non-empty payloads. If the incoming method is HEAD, the body is omitted and the response is terminated. Unchunked string payloads are dispatched directly via res.end(payload), whereas dynamic streaming results are piped to the underlying Node response using result.pipeToNodeResponse(res). Sources: packages/next/src/server/send-payload.ts:73-92

Was this page helpful?