Skip to content

Commit 1371f4a

Browse files
committed
routing doc
1 parent 36a877b commit 1371f4a

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed

request-chain.md

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Adapter request chain
2+
3+
Adapters receive information about output pathnames, but the Adapters API/specification does not
4+
fully document how to stitch those outputs together to implement routing that matches Next.js
5+
behavior.
6+
7+
This document describes the Next.js request chain logic for adapter implementers. It gathers
8+
behavior observed in the Next.js runtime and Vercel adapters and documents required routing logic
9+
that currently needs to be reverse-engineered. The document describes what logic must be applied,
10+
but does not prescribe implementation details — it is up to implementers to decide where and how to
11+
apply this logic in their specific adapter architecture.
12+
13+
**Note:** This document intentionally does not cover ISR/prerender grouping and revalidation or PPR
14+
(Partial Prerendering) handling, as those belong to the details of prerender output handling and not
15+
to routing or output matching logic.
16+
17+
Legend:
18+
19+
- `[onBuildComplete routes]`: information supplied to the adapter via its `onBuildComplete`
20+
callback. This information is site/application-specific.
21+
- `[generic rules]`: Generic, non-site-specific routing behaviors that are not provided via the
22+
adapter's `onBuildComplete` callback and are not currently documented in the specification. Many
23+
of these rules are conditional — for example, they may only apply when the Pages router or App
24+
router is used, when middleware is enabled, when i18n is configured.
25+
26+
Routing rules:
27+
28+
1. Incoming request phase
29+
30+
All incoming requests go through this phase first, before any output matching is attempted.
31+
32+
Steps:
33+
1. `[onBuildComplete routes]` Priority redirects (trailing slash): if the request pathname's
34+
trailing slash doesn't match configuration, terminate with a redirect that adds or strips the
35+
trailing slash.
36+
2. `[generic rules]` If middleware/proxy is enabled and using the Pages router:
37+
1. If the request path matches `<basepath>/_next/data/<build_id>/<path>.json`, add the
38+
`x-next-data: 1` request header and continue.
39+
2. If the request path matches `<basepath>/_next/data/<build_id>/<path>.json`, rewrite the
40+
request path to `<basepath>/<path>` (index special case) and continue.
41+
3. `[generic rules]` If i18n is enabled:
42+
1. If locale domains are configured:
43+
1. Rewrite the pathname to ensure the correct locale for the matched domain is prefixed to
44+
the pathname (index special case) and continue.
45+
2. If locale detection is enabled and the request targets the index route (including
46+
locale-prefixed ones), detect the locale based on the `NEXT_LOCALE` cookie or
47+
`Accept-Language` header and redirect to the locale-specific domain. TODO: avoid
48+
redirect loops — only redirect when the current pathname is not already the expected
49+
pathname.
50+
2. If locale detection is enabled and the request targets the index route, detect locale based
51+
on the `NEXT_LOCALE` cookie or `Accept-Language` header and redirect to the locale-prefixed
52+
path (or root for default locale). TODO: avoid redirect loops.
53+
3. If the pathname has no locale prefix, add the default locale prefix to the pathname (index
54+
special case) and continue.
55+
4. `[onBuildComplete routes]` Collect headers (from `next.config`) that match current request
56+
conditions and apply them to the final response.
57+
5. `[onBuildComplete routes]` Non-priority redirects (from `next.config`). If matched, terminate.
58+
6. `[onBuildComplete routes]` If middleware matched: run middleware. Note: middleware responses
59+
may be non-terminal (they can rewrite the request or mutate headers). Implementers must handle
60+
`x-middleware-rewrite`, `x-middleware-next`, `x-middleware-override-headers`,
61+
`x-middleware-set-cookie`, and similar control headers.
62+
7. `[onBuildComplete routes]` Run `beforeFiles` rewrites (from `next.config`); on a match,
63+
continue.
64+
8. `[generic rules]` Ensure that `/404` or `/<locale>/404` (if i18n enabled) routes return a 404
65+
status for non-revalidate requests, then continue.
66+
9. `[generic rules]` Ensure that `/500` or `/<locale>/500` (if i18n enabled) routes return a 500
67+
status for non-revalidate requests, then continue.
68+
10. `[generic rules]` If middleware/proxy is enabled and using the Pages router:
69+
1. If the request has the `x-next-data` header, rewrite the request path to
70+
`<basepath>/_next/data/<build_id>/<path>.json` (index special case) and continue — this
71+
undoes the `_next/data` path normalization done earlier.
72+
11. `[generic rules]` If App router behavior applies:
73+
1. Prefetch/segment handling is required here — it depends on multiple request headers and
74+
needs to be fleshed out (placeholder).
75+
2. If the request has an `rsc: 1` header, rewrite the request path to `<basepath>/<path>.rsc`
76+
(index special case) and continue.
77+
12. Proceed to the Output matching phase.
78+
79+
2. Output matching phase
80+
81+
This phase executes potentially multiple times during the request chain. It's often invoked after
82+
applying a rewrite to check whether the rewrite resulted in a match on outputs.
83+
84+
Steps:
85+
1. `[onBuildComplete routes]` Try to match outputs (other than middleware) or the `_next/image`
86+
image-optimization endpoint. Prioritize prerenders over functions if both exist for the same
87+
path. Terminate on a match. TODO: decide and document priority rules if static files,
88+
prerenders, and functions overlap — define priority or declare that any overlap is unexpected.
89+
2. `[generic rules]` Rewrite `<basepath>/_next/image` to `_next/image`. On a match, re-run output
90+
matching and terminate.
91+
3. `[generic rules]` If middleware/proxy + Pages router is enabled (normalizing again for
92+
rewrites in future steps):
93+
1. If the request path matches `<basepath>/_next/data/<build_id>/<path>.json`, rewrite the
94+
request path to `<basepath>/<path>` (index special case) and continue.
95+
4. `[generic rules]` If no middleware is present and the request path matches
96+
`<basepath>/_next/data/<build_id>/<path>.json`, try matching outputs again and terminate even
97+
if not matched (return a 404).
98+
5. `[generic rules]` If App router: rewrite `<basepath>/index.(rsc|action)` to `/` to normalize
99+
`/index` for rewrite matching. TODO: clarify prefetch/segment interactions.
100+
6. `[onBuildComplete routes]` Run `afterFiles` rewrites (from `next.config`); on a match, re-run
101+
output matching and terminate.
102+
7. `[generic rules]` If App router (fixing "bad rewrites"):
103+
1. Rewrite `<basepath>/.prefetch.rsc` to `<basepath>/__index.prefetch.rsc` and re-run the
104+
filesystem phase on a match.
105+
2. Rewrite `<basepath>/<path>/.prefetch.rsc` to `<basepath>/<path>.prefetch.rsc` and re-run
106+
the filesystem phase on a match.
107+
3. Rewrite `<basepath>/.rsc` to `<basepath>/index.rsc` and re-run the filesystem phase on a
108+
match.
109+
4. Rewrite `<basepath>/<path>/.rsc` to `<basepath>/<path>.rsc` and re-run the filesystem phase
110+
on a match.
111+
8. `[generic rules]` Rewrite `<basepath>/_next/static/<path>` to
112+
`<basepath>/_next/static/not-found.txt` and assign a 404 status code. On a match, re-run
113+
output matching and terminate.
114+
9. `[generic rules]` If i18n is enabled:
115+
1. Strip the locale prefix from the path and try matching on outputs again; terminate on a
116+
match.
117+
10. `[generic rules]` If middleware/proxy + Pages router is enabled: perform normalization steps
118+
as needed.
119+
11. If the request has the `x-next-data` header, rewrite the request path to
120+
`<basepath>/_next/data/<build_id>/<path>.json` (index special case) and continue — this
121+
undoes the `_next/data` path normalization done earlier.
122+
12. `[onBuildComplete routes]` Try to match on dynamic route rewrites (from `next.config`). On a
123+
match, check outputs again and terminate.
124+
13. `[onBuildComplete routes]` Apply fallback rewrites (from `next.config`). On a match, check
125+
outputs again and terminate.
126+
14. `[generic rules]` No outputs matched — return a 404 status.
127+
128+
3. Termination
129+
130+
After the request chain terminates (an output matched or a final status determined), apply
131+
additional transformations to the final response before returning it to the client.
132+
133+
Steps:
134+
1. `[generic rules]` If a matched output returns a non-error response:
135+
1. If serving from `<basepath>/_next/static` (static files), apply
136+
`Cache-Control: public, max-age=31536000, immutable` response header.
137+
2. Apply an `x-matched-path` response header with the matched pathname.
138+
2. `[generic rules]` If no output matched or a 404 response was selected:
139+
1. Serve a custom 404 page if defined in outputs:
140+
1. If i18n + Pages router: `/<basepath>/<locale>/404` (based on the pathname locale) or
141+
`/<basepath>/<default-locale>/404` (if no locale in pathname).
142+
2. If no i18n + Pages router: `/<basepath>/404`.
143+
3. If App router: `/<basepath>/_not-found`.
144+
3. `[generic rules]` If a matched output produced a 5xx response or output execution failed:
145+
1. Serve a custom 500 page if defined in outputs:
146+
1. If i18n + Pages router: `/<basepath>/<locale>/500` (based on the pathname locale) or
147+
`/<basepath>/<default-locale>/500` (if no locale in pathname).
148+
2. If no i18n + Pages router: `/<basepath>/500`.
149+
3. If App router: `/<basepath>/_error`.

0 commit comments

Comments
 (0)