vercel/next.js
A request enters at the Node `http.Server` in `start-server.ts`, is immediately sanitised by `filterInternalHeaders()` (which deletes a fixed `INTERNAL_HEADERS` list so clients cannot forge `x-middleware-rewrite`, `x-matched-path`, etc.), then flows through the router-server's `resolveRoutes` pipeline (headers → redirects → middleware → beforeFiles → filesystem → afterFiles → dynamic routes → fallback), and only then crosses into the render server. Inside `BaseServer.handleRequest` the request is wrapped in an OTEL span and per-request metadata is attached via `addRequestMeta`, after which render state is carried in AsyncLocalStorage (`workAsyncStorage`, `workUnitAsyncStorage`) rather than threaded through arguments. Output is a `RenderResult` piped to the socket, with cacheable results passing through `ResponseCache` → `IncrementalCache` → a `CacheHandler`. Build-time data flows the other direction: source files → route discovery → bundler (Turbopack over napi, or webpack) → `.next/*-manifest.json` → read back by `setupFsCheck` at server boot.
- The first thing `requestHandlerImpl` does with a live request is `addRequestMeta(req, 'relativeProjectDir', ...)` and then `filterInternalHeaders(req.headers)` — unless `NEXT_PRIVATE_TEST_HEADERS` is set, which is the documented test-only escape hatch.
- `INTERNAL_HEADERS` is an explicit allow-nothing list of 11 names (`x-middleware-rewrite`, `x-middleware-redirect`, `x-middleware-set-cookie`, `x-middleware-skip`, `x-middleware-override-headers`, `x-middleware-next`, `x-now-route-matches`, `x-matched-path`, `x-nextjs-data`, `x-next-resume-state-length`, `next-resume`); any new internal header must be added here or it is forgeable from outside.
- Routing is a declarative ordered list built once in `resolve-routes.ts`: `middleware_next_data`, `middleware`, `rewrites.beforeFiles`, a second middleware check, `rewrites.afterFiles`, then `rewrites.fallback` — with all rewrite phases skipped entirely when `opts.minimalMode` is set (the deployed/serverless shape).
- Middleware runs out-of-process-shaped: `resolveRoutes` sets `middlewareInvoke: true` request meta, invokes the render server, and then translates the returned `Response` headers — hoisting `x-middleware-override-headers` into real request headers by reading each `x-middleware-request-<key>` and deleting it, and synthesising `x-middleware-refresh` when neither rewrite, next, nor location was set.
- `setupFsCheck()` is the build→runtime data handoff: it reads `routes-manifest.json`, the app/pages routes manifests, and the `public/` + `.next/static/` directory listings into `Set`s (`publicFolderItems`, `nextStaticFolderItems`, `appFiles`, `pageFiles`) plus a sorted `dynamicRoutes` array and a `middlewareMatcher`, then exposes a single LRU-backed `getItem(pathname)`.
- Per-request state is passed via `addRequestMeta`/`getRequestMeta` keys (`invokePath`, `invokeQuery`, `middlewareInvoke`, `locale`, `isRSCRequest`, `relativeProjectDir`) rather than function parameters, which is what lets the router server and render server share a request object across the lazy-init boundary.
- Render-time state lives in AsyncLocalStorage: `app-render.tsx` repeatedly does `workUnitAsyncStorage.run(...)` to produce the RSC payload and the Flight stream, and `workAsyncStorage` carries the work store; there is no explicit context object threaded through the React tree.
- Cache Components prerendering is a two-pass flow: a prospective prerender fills a `PrerenderResumeDataCache` created by `createPrerenderResumeDataCache()`, that cache is attached to `requestStore.resumeDataCache`, and a second 'final'/runtime prerender reuses the filled cache — the comments state caches must be shared between the prospective and final prerender.
- Cacheable responses flow value-as-stream: a `CacheEntry.value` is a `ReadableStream<Uint8Array>` with a documented contract that handlers must return a new stream on every `get` and must not retain the stream after `set` resolves, because a retained stream pins the originating request's async context.
- Tag-based revalidation is a side-channel: `FileSystemCache` mutates a module-scoped `tagsManifest` map with `{ expired: now }` entries and reads it back through `areTagsExpired`, so tag state is per-process in the filesystem handler.
- Build-time config is projected into user bundles as `process.env.__NEXT_*` string literals by `getDefineEnv()` — 40+ keys including `__NEXT_BUNDLER`, `__NEXT_DEV_SERVER`, `__NEXT_CACHE_COMPONENTS`, `__NEXT_USE_CACHE`, `__NEXT_IMAGE_OPTS`, and the client router bloom filters `__NEXT_CLIENT_ROUTER_S_FILTER`/`_D_FILTER`.
- `process.env.__NEXT_USE_NODE_STREAMS` is set to `false` for the edge server and `true` otherwise, which is the fork point for which stream implementation the render path compiles against.
- Outbound network egress from the framework is narrow and bounded: the image optimizer `fetch`es upstream images with `AbortSignal.timeout(7_000)` and `redirect: 'manual'`, following at most 3 redirects manually.
- Build output data leaves the repo through the adapter boundary: `handleBuildComplete()` resolves `adapterPath` with `require.resolve` + `pathToFileURL` and, for `output: 'export'`, enumerates `configOutDir` with `recursiveReadDir` to hand the adapter a static-file list.