Docs
Systhema Design (opens in new tab)
Unreleased

Redirects

Redirects and wildcard redirects.

On this page

Enable the Redirects collection by passing redirect: true to withSysthema():

const config = withSysthema(buildConfig({ /* ... */ }), {
  redirect: true,
})

This registers @payloadcms/plugin-redirects's redirects collection. Editors author redirects in the admin panel under Redirects.

Stored fields and accessLink to this section

Each row has from, a custom URL or document-reference destination, the redirect status and preserveQueryString. Creating, reading, updating and deleting redirect rules uses the corresponding redirects.* capabilities. Change and delete hooks revalidate the site so cached redirect behavior is refreshed. Reference destinations require a resolvable document path.

Wildcard redirectsLink to this section

When a from path contains *, Systhema treats it as a wildcard redirect and matches it automatically — no toggle needed.

from syntaxLink to this section

Each * captures part of the path. A * at the end of the pattern captures everything that follows, including slashes. A * with more pattern after it captures a single segment (no slashes).

from patternMatches
/blog/*Everything under /blog/ — /blog/hello and /blog/2024/recap (a trailing * captures the rest, including slashes)
/shop/*/reviewsA single segment — /shop/widget/reviews, but not /shop/a/b/reviews (a * with more pattern after it matches one segment)
/*/news/*Two captures — first * (has /news/… after it) = one segment; second * (trailing) = the rest; reorder via $1, $2

to syntax (custom URL target)Link to this section

Target valueBehaviour
/posts/*Bare * — substituted with the first capture ($1)
/posts/$1Explicit numbered capture — same as bare *
/news/$2-$1Reorders two captures

If the target is a reference (a Payload document relationship) rather than a custom URL, all matching paths redirect to that single document — no capture substitution.

Worked exampleLink to this section

FromToIncoming pathRedirects to
/blog/*/posts/*/blog/2024/recap/posts/2024/recap
/*/news/*/news/$2-$1/world/news/recap/news/recap-world

Same-origin safetyLink to this section

A custom-URL target stays on your own site: if a captured value would turn the resolved target into an absolute (http://…) or protocol-relative (//…) URL, the redirect is rejected and the next rule is tried — a capture can't smuggle in an off-site destination. Intentional off-site redirects still work; declare the host in the target itself (e.g. https://example.com/*), so the off-origin part comes from your literal template rather than from a capture.

Resolution orderLink to this section

Within the site catch-all, resolution runs:

  1. Exact redirects (from the redirects collection, no wildcards) — matched first.
  2. Wildcard redirects — matched next, most-specific rule wins (the rule with the most literal characters in from takes precedence).
  3. Page lookup — only reached if no redirect matched.
  4. Post permalink lookup — with the Posts module enabled, only when no page matched at the path.

So an authored wildcard redirect wins over still-existing content at the same path. With the Posts module enabled, a redirect's reference target can also point at a post — it resolves through the permalink engine.

Status codesLink to this section

308 permanent is the default. Editors can change it to 307 temporary per redirect on the standard Payload Redirects form.

Preserve query stringLink to this section

Every redirect — exact and wildcard alike — has an Advanced Settings collapsible containing a Preserve query string checkbox (default on). When on, the incoming ?query=string is carried over to the target URL — target params win on conflict. Uncheck it to drop the query string entirely.

Redirects and localesLink to this section

With locales enabled, from and a custom to.url are localized: each locale has its own pair, so /about → /contact in English and /rolunk → /kapcsolat in Hungarian live on one row. An incoming request is matched against the from of its own locale only — a Hungarian path never matches an English from. Leave a locale's from empty and that row simply doesn't apply there.