Redirects, rewrites, headers and route rules
The build validates these rules, and the handler validates them again when it is created.
The build validates these rules, and the handler validates them again when it is created. The validation and the rules are the
routing() feature (@shadow-js/dusk/features/routing): the generated server entry lists it when this file declares any rule, and a
hand-written createHandler with a routing option lists it too, or is refused at creation.
Patterns use the router's grammar: :name matches one segment, *name (or *) matches one or more trailing segments
and captures them, and ** is the glob that matches zero or more trailing segments. A * inside a segment matches a
run of characters in that segment (cover-*.png). ** and *name are only allowed as the last segment. A trailing
slash is ignored.
A rule covers every spelling of the path that the router serves. The literal parts of a pattern compare without ASCII
case, as the router does (so /admin/** also covers /ADMIN/users), unless router.caseSensitive is on, in which case
the rules are case-sensitive too (Case sensitivity). A path is decoded
once per segment, so /%61dmin is /admin. A *name rest never matches a path whose rest holds an empty, . or ..
segment (an encoded ..%2F_private in a rewrite of /docs/*rest to /content/*rest would otherwise climb out of
/content), so such a request is not rewritten or redirected and the router answers it. The patterns are not regular
expressions: matching is linear in the length of the path, whatever the rules are.
Evaluation order:
- Redirects run first, in declaration order: the first match answers.
permanent: truegives 308, the default is 307, andstatusCode(301, 302, 303, 307 or 308) overrides both. The target is checked with the same rule as every redirect in the framework: a path of this app, or an absolute URL whose origin is insecurity.trustedOrigins. Anything else (//host, a backslash, ajavascript:URL) is refused when the file is read. - Route-rule redirects (
routeRules[...].redirect) run after the redirects, for the most specific matching rule. A redirect to the path the request already has is ignored. beforeFilesrewrites change the path the request is served with before the route is matched. The URL the browser sees stays the same. The query string is kept, and the rule's own query parameters replace the same names. A rewrite destination is a path of this app and never starts with/_dusk/.afterFilesandfallbackrewrites run only when no page matches the path. Within each phase the first match wins, and a request is rewritten at most once per phase.- Headers apply every matching rule in declaration order, so a later rule overwrites an earlier one. A header the route or the handler already set wins over a rule, and the framework's secure defaults come after the rules.
- Route rules merge: every matching rule applies, least specific first, so the most specific rule wins each
property (a literal path beats a parameter, which beats a
**glob).headersmerge key by key.
Route rule settings:
revalidate: seconds | falseis the fresh window of the page cache for pages whose route exports norevalidateof its own.cache: { staleSeconds, cookies, searchParams, cdn }shapes the cache key, andcache: falseturns caching off.cdn: truealso sendsCDN-Cache-Controlwith the origin's directives;cdn: { surrogate: true }also sendsSurrogate-Control. The headers and theVarybehavior are in CDN, caching and what the platform owns.prerender: truerenders the path at build time. It needs a literal path (no parameters or wildcards); list dynamic paths inprerender.entries.ssr: falseanswers the document shell, and the browser renders the page (the same asdefineRoute({ ssr: false })). Onlyfalseis accepted here.csr: falseships no client code for the page: no entry script and no script preloads.cacheControl: "public, max-age=60"(orfalse) is thecache-controlof the path's responses that set none, in place ofsecurity.defaultCacheControl.falseleaves those responses without the header. Acache-controlthe route or aheadersrule sets always wins. A value with a line break is refused when the file is read.
Conditions: has requires each listed condition to hold, and missing requires each one to be absent. Both take
header, cookie, query and host conditions with the same meaning: key and an optional exact value (a host
condition takes value only). A host value without a port matches that host on any port; a value with a port matches
that port only. Regular expressions in source are not supported (deferred: the pattern grammar above is the only one).
Query strings: a redirect keeps the request's query string, so /old?x=1 redirects to /new?x=1. A destination that has
a query string of its own keeps that one, and the request's query is then dropped (/new?from=rule). Rewrites keep the
query as described in step 3.
Fragments: a # in a destination is refused when the file is read. A fragment is never sent to the server: the handler
sees an empty hash, and route matching ignores it.