Skip to content

Commit 2c36895

Browse files
committed
routing doc
1 parent 36a877b commit 2c36895

File tree

1 file changed

+144
-0
lines changed

1 file changed

+144
-0
lines changed

request-chain.md

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

0 commit comments

Comments
 (0)