Home

Site Redirects

Stencila sites support two redirect mechanisms. Config redirects defined in stencila.toml support pattern matching with placeholders and splats. Per-directory _redirect.json files handle simple single-route cases.

Config redirects

Redirect routes are defined under [site.routes] in stencila.toml. Placeholders use {name} syntax (consistent with spread routes) and splats use *, e.g.

[site.routes]
# Exact redirect
"/old-page" = { redirect = "/new-page", status = 301 }

# Splat — * matches all remaining path segments
"/blog/*" = { redirect = "https://blog.example.com/{splat}", status = 301 }

# Placeholders — {name} matches a single path segment
"/docs/{lang}/{page}" = { redirect = "/documentation/{lang}/{page}" }

# Combined placeholder and splat
"/products/{category}/*" = { redirect = "/shop/{category}/{splat}", status = 301 }

# External redirect (defaults to 307)
"/legacy/" = { redirect = "https://legacy.example.org" }

Fields

FieldTypeRequiredDescription
redirectstringyesTarget URL — use {name} to substitute placeholders and {splat} to substitute the splat
statusintegernoHTTP status code: 301, 302, 303, 307, or 308. Defaults to 307

Patterns

Exact match

"/old-page" = { redirect = "/new-page", status = 301 }

For static sites hosted on Stencila Sites, exact redirects are intended for a single route path. To avoid ambiguity, prefer writing routes with your site's canonical trailing-slash style.

Splats (*)

A * at the end of the route path greedily matches all remaining path segments. Use {splat} in the target to substitute the matched value.

"/blog/*" = { redirect = "https://blog.example.com/{splat}", status = 301 }
Request{splat} valueRedirects to
/blog/(empty)https://blog.example.com/
/blog/hellohellohttps://blog.example.com/hello
/blog/2024/my-post2024/my-posthttps://blog.example.com/2024/my-post

Only one * is allowed per rule, and it must be the last segment.

Placeholders ({name})

A {name} segment matches exactly one path segment. Use {name} in the target to substitute the captured value.

"/docs/{lang}/{page}" = { redirect = "/documentation/{lang}/{page}" }
RequestCapturesRedirects to
/docs/en/introlang=en, page=intro/documentation/en/intro
/docs/fr/guidelang=fr, page=guide/documentation/fr/guide
/docs/en(no match — too few segments)
/docs/en/intro/extra(no match — too many segments)

Placeholder names must be alphanumeric (plus underscores) and unique within a rule.

Combining splats and placeholders

"/products/{category}/*" = { redirect = "/shop/{category}/{splat}", status = 301 }
RequestRedirects to
/products/shoes//shop/shoes/
/products/shoes/nike/air-max/shop/shoes/nike/air-max

Rule ordering

Rules are matched in the order they are declared in [site.routes], and the first match wins. Place more specific rules before catch-all rules:

[site.routes]
# Specific rule first
"/blog/archive" = { redirect = "/blog/all", status = 301 }
# Catch-all after
"/blog/*" = { redirect = "https://blog.example.com/{splat}", status = 301 }

A request to /blog/archive matches the first rule and redirects to /blog/all.

Destination URLs

Redirect targets can be:

FormatExampleBehavior
Absolute URLhttps://example.com/pageRedirect to external site
Absolute path/new-pathRedirect within same site
Relative path../otherResolved against request URL

Only http: and https: protocols are allowed. Query strings can be included in the target:

"/search" = { redirect = "/find?source=redirect" }

Choosing a status code

For static sites on Stencila Sites, use these status codes as a guide:

StatusUse whenNotes
301The old URL has moved permanently and clients can update bookmarks and search indexesCommon for page renames and site restructures
302You need a temporary redirect with broad legacy compatibilitySome clients may change the request method
303You explicitly want the follow-up request to use GETMost useful for form or action endpoints rather than static pages
307You need a temporary redirect and want to preserve the original request methodDefault for Stencila Sites because it is the safest temporary redirect
308The move is permanent and you want to preserve the original request methodPermanent counterpart to 307

For most static-site page moves:

  • use 301 for permanent page moves

  • use 307 for temporary moves or maintenance redirects

  • use 302 only when you specifically want classic temporary redirect behavior

  • use 303 mainly for non-page workflows

  • use 308 when permanent method-preserving behavior matters

Per-directory redirects

Place a _redirect.json file in any directory to redirect requests for that exact route.

{
  "location": "/docs/",
  "status": 301
}
FieldTypeRequiredDescription
locationstringyesTarget URL (relative or absolute)
statusintegernoHTTP status code: 301, 302, 303, 307, or 308. Defaults to 307

Per-directory redirects only trigger for route requests (paths ending with / that have no index.html). They match the exact directory — there is no inheritance to subdirectories.

If a _redirect.json file exists for a route that also has a config redirect in stencila.toml, the per-directory file takes precedence.

Example

A _redirect.json in the old-docs/ directory:

{
  "location": "/docs/",
  "status": 301
}
RequestResult
/old-docs/301 → /docs/
/old-docs/guide/404 (no _redirect.json in old-docs/guide/)

Resolution order

For static sites hosted on Stencila Sites, redirects are resolved in this order:

  1. Static file — serve the file if it exists

  2. Per-directory _redirect.json — checked for route requests when no index.html exists

  3. Root-level _redirects.json — config redirects checked for any unmatched path

  4. 404 — if nothing matches

Static files always take priority — redirects never override an existing file.

Constraints

ConstraintDetail
Splat positionMust be the last segment (/a/* ✓, /a/*/b ✗)
Splats per ruleOne * maximum
Placeholder matchingSingle path segment only (no / within match)
Status codes301, 302, 303, 307, 308 (others default to 302)
Protocol safetyOnly http/https destinations allowed
File priorityRedirects never override existing files
PrecedencePer-directory _redirect.json > config _redirects.json
© 2026 Stencila