Skip to content

Commit a59f97b

Browse files
committed
routing doc
1 parent 36a877b commit a59f97b

File tree

1 file changed

+142
-0
lines changed

1 file changed

+142
-0
lines changed

request-chain.md

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

0 commit comments

Comments
 (0)