Every published page and post gets exactly one canonical URL. This page explains how that URL is built, what happens when a slug changes, and how to preview a draft before it goes live.
Pages
A page with slug home serves at /. Every other page slug serves at /<slug>. The pattern is fixed and not configurable.
Posts
A post's URL follows the site's permalinkPattern setting, one of exactly four fixed patterns:
| Pattern | Example |
|---|---|
blog/{slug} (the default) | /blog/hello-world |
{slug} | /hello-world |
{yyyy}/{mm}/{slug} | /2026/08/hello-world |
blog/{yyyy}/{mm}/{slug} | /blog/2026/08/hello-world |
There is no template language beyond these four strings. Set permalinkPattern through manage_site.
The {yyyy} and {mm} tokens come only from the post's frontmatter.publishedAt, in UTC. A post cannot publish under a date pattern without a valid publishedAt; Noon never substitutes the creation date, the update date, or the current time for it, because any of those could silently change the URL on a later save.
User-defined content types
A routable custom content type reserves its URL the same way a page does: /<slug>, in the same namespace as pages. A custom entity and a page can collide on the same slug; whichever one publishes first keeps the path, and the second publish attempt fails.
As of this writing, a custom entity's URL is reserved but not rendered: visiting it returns 404 even though the path is taken. Do not rely on a custom entity being visible at its URL yet.
Slug and pattern changes
When a post's slug changes, or the site's permalinkPattern changes, the old path becomes an alias. Visiting the old path returns an HTTP 301 redirect straight to the new path. This is always exactly one hop: an alias never points to another alias, even after several renames, so a visitor is never redirected more than once no matter how many times a slug changed since.
An alias only redirects while the entity is published. An alias of an unpublished entity does not redirect.
Unpublishing
Unpublishing a page or post does not free its URL, and it does not redirect anywhere. The path simply stops resolving. An unpublished path and a path that was never used at all return the exact same 404 response, with no distinguishing detail. This is deliberate: nobody can tell the two apart by probing URLs, so an unpublished slug cannot be discovered that way.
Sitemap and feed
GET /sitemap.xml and GET /feed.xml are served only when the seo plugin is enabled; otherwise both return 404.
/sitemap.xml lists every currently published page and post at its current URL, including / when the home page is published. Aliases, drafts, and unpublished entities never appear.
/feed.xml is an RSS 2.0 feed. It lists posts only, never pages. Each item's pubDate appears only when the post has a publishedAt; its description appears only when the post has a nonblank frontmatter.description. Neither is invented when missing.
Both responses are generated fresh on every request and are never cached, so an unpublish is reflected immediately.
Previewing a draft
When you create or update a page or post, or roll one back, the response includes a previewUrl. This is a signed link to the draft, rendered through the same templates the live site uses:
/preview/<entityType>/<id>?sig=<signature>&exp=<expiry>
The signature covers the entity type, the id, and the expiry together, so none of the three can be swapped or extended independently. By default the link expires one hour after it is issued. A request at exactly the expiry time is still valid; anything after is not.
The preview link itself is the authorization: no bearer token is required to open it. Anyone holding the link can view that one draft until it expires, and nothing else. If the draft is missing, or the signature is invalid, expired, or tampered with, the preview route returns the same 403 for every case, so it cannot be used to discover whether a given id exists.