vercel/next.js
Server runtime
The HTTP request handling layer of Next.js. Three classes in a hierarchy plus a router server that runs in front of them, all in packages/next/src/server/.
The class hierarchy
classDiagram
class BaseServer {
<<abstract>>
+handleRequest(req, res)
+renderToHTML(req, res, pathname, query)
+serveStatic(req, res, path)
#findPageComponents(pathname)
#generateRoutes()
}
class NextNodeServer {
+runMiddleware(...)
+handleApiRequest(...)
}
class NextDevServer {
+ensurePage(...)
+invalidate(pageKey)
}
class NextWebServer {
+runEdgeFunction(...)
}
BaseServer <|-- NextNodeServer
NextNodeServer <|-- NextDevServer
BaseServer <|-- NextWebServer
note for BaseServer "packages/next/src/server/base-server.ts (3,070 lines)"
note for NextNodeServer "packages/next/src/server/next-server.ts (2,178 lines)"
note for NextDevServer "packages/next/src/server/dev/next-dev-server.ts"
note for NextWebServer "packages/next/src/server/web-server.ts"Active contributors
Heavy churn in the last 90 days; activity centered on packages/next/src/server/lib/router-server.ts, packages/next/src/server/base-server.ts, and packages/next/src/server/next-server.ts.
Request entry: router-server
Before any of the BaseServer subclasses sees a request, the router server runs first. Source: packages/next/src/server/lib/router-server.ts (~30 KB).
sequenceDiagram
participant Client
participant Router as router-server.ts
participant Filter as filterInternalHeaders
participant Match as Route matcher
participant Server as BaseServer subclass
Client->>Router: HTTP request
Router->>Filter: strip x-next* internal headers
Filter-->>Router: sanitized req
Router->>Match: classify (app | pages | api | static | middleware | image)
Match-->>Router: match result
Router->>Server: forward to renderer
Server-->>Router: response
Router-->>Client: HTTP responseThe internal-header filter at packages/next/src/server/lib/server-ipc/utils.ts strips request headers in the INTERNAL_HEADERS array. Routes that read non-standard headers must be audited against this list — a header outside the list is forgeable by an external attacker.
BaseServer
packages/next/src/server/base-server.ts is the abstract base. It owns the rendering decision tree and the static-asset serving logic. It does not bind to a TCP port directly; instead, it accepts a req/res shape and processes it.
Key methods:
| Method | Role |
|---|---|
handleRequest(req, res) |
The main entry; dispatches to the renderers |
renderToHTML(req, res, pathname, query) |
App Router or Pages Router render |
findPageComponents(pathname) |
Look up the route module for a path |
generateRoutes() |
Build the route table from manifests |
serveStatic(req, res, path) |
Serve _next/static/ files |
The class is large (3,070 lines) because every shared concern between dev and prod, Node and Edge, App and Pages routers eventually lives here.
NextNodeServer
packages/next/src/server/next-server.ts (2,178 lines). The Node-flavored production server. Subclasses BaseServer and:
- Runs middleware as Node code (the production case where middleware is bundled but executed in the same process).
- Handles
pages/api/*route handlers via Node'shttprequest/response. - Serves
pages/api/*requests through the Node-flavored route module path. - Does the actual
http.createServer().listen(port)when invoked vianext start.
NextDevServer
packages/next/src/server/dev/next-dev-server.ts (~36 KB). Subclasses NextNodeServer and adds:
- On-demand entry compilation via
packages/next/src/server/dev/on-demand-entry-handler.ts. - Hot reload integration via the bundler-specific reloader (
hot-reloader-turbopack.ts,hot-reloader-webpack.ts,hot-reloader-rspack.ts). - Development overlay (
packages/next/src/next-devtools/). - Error overlay rendering via
react-refresh.
The dev server is what next dev starts.
NextWebServer
packages/next/src/server/web-server.ts. The Edge-runtime variant of the server. Used inside the @edge-runtime/vm sandbox (or on Vercel's edge network). Subclasses BaseServer directly because the Node http-specific bits don't apply.
Route matching
Once BaseServer decides where a request goes, it consults a route matcher:
graph TD
Match[base-server matches] --> Manager[Route matcher manager<br/>route-matcher-managers/]
Manager --> AppMatch[app routes provider]
Manager --> PagesMatch[pages routes provider]
Manager --> ApiMatch[api routes provider]
Manager --> AppRoute[app route handlers provider]
AppMatch --> Match
PagesMatch --> Match
ApiMatch --> Match
AppRoute --> MatchProviders under packages/next/src/server/route-matcher-providers/ read the manifests written at build time and turn URL patterns into compiled regexes. Managers under packages/next/src/server/route-matcher-managers/ decide which provider answers for a given request.
The route definitions and route-match types are in packages/next/src/server/route-definitions/ and packages/next/src/server/route-matches/.
Route modules
Once a route is matched, it's handed to a route module — a self-contained bundle of "everything this route needs to render itself". Live under packages/next/src/server/route-modules/:
| Kind | Module |
|---|---|
| App Router page | route-modules/app-page/ |
| App Router route handler | route-modules/app-route/ |
| Pages Router page | route-modules/pages/ |
| Pages Router API route | route-modules/pages-api/ |
Each module exports a handler function that takes the renderable inputs and produces a response.
Async storage
Server runtime relies heavily on Node's AsyncLocalStorage to thread per-request context through deep call stacks. The storages are:
| Storage | File |
|---|---|
| Work | packages/next/src/server/app-render/work-async-storage.external.ts |
| Work-unit | packages/next/src/server/app-render/work-unit-async-storage.external.ts |
| Action | packages/next/src/server/app-render/action-async-storage.external.ts |
After-task (unstable_after) |
packages/next/src/server/app-render/after-task-async-storage.external.ts |
| Console replay | packages/next/src/server/app-render/console-async-storage.external.ts |
| Dynamic-access tracking | packages/next/src/server/app-render/dynamic-access-async-storage.external.ts |
The .external.ts suffix marks these files as singleton modules that must not be duplicated across React's server / client layers. The pattern is: xxx-async-storage-instance.ts creates the storage, xxx-async-storage.external.ts re-exports it as the public surface.
Integration points
- The build pipeline writes manifests under
.next/that the server reads to populate route tables (see build). - The App Router renderer consumes route modules for app-dir pages.
- The Pages Router renderer consumes route modules for pages-dir pages.
- Middleware runs first when configured, before route matching.
- Image optimization is a special route (
/_next/image) routed by the same matcher.
Entry points for modification
- To change how a request is classified before matching:
packages/next/src/server/lib/router-server.ts. - To add a new internal header:
packages/next/src/server/lib/server-ipc/utils.ts(INTERNAL_HEADERSarray). - To change shared dispatch behavior:
packages/next/src/server/base-server.ts. - To change the Node prod server:
packages/next/src/server/next-server.ts. - To change dev-server behavior:
packages/next/src/server/dev/next-dev-server.ts. - For the edge server:
packages/next/src/server/web-server.tsandpackages/next/src/server/web/.
Key source files
| File | Purpose |
|---|---|
packages/next/src/server/lib/router-server.ts |
First request entry, header filter, classification |
packages/next/src/server/lib/server-ipc/utils.ts |
filterInternalHeaders() |
packages/next/src/server/base-server.ts |
Abstract base server |
packages/next/src/server/next-server.ts |
Node production server |
packages/next/src/server/dev/next-dev-server.ts |
Dev server |
packages/next/src/server/web-server.ts |
Edge server |
packages/next/src/server/route-matcher-managers/ |
Route matcher managers |
packages/next/src/server/route-matcher-providers/ |
Route matcher providers |
packages/next/src/server/route-modules/ |
Per-kind route module implementations |
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.