Statac

URLs

Where each page and file is published, and how to choose or change a URL.

Statac publishes every page at a URL based on where its file is in content/, unless you choose a different one.

How a file’s URL is made

File URL
content/index.md /
content/post-1.md /post-1/
content/posts/index.md /posts/
content/posts/post-1.md /posts/post-1/
content/posts/2026-10-04-post-1.md /posts/post-1/
content/days/03-day-one.md, in a numbered folder /days/day-one/
content/Café.md /cafe/
content/posts/file-1.pdf /posts/file-1.pdf

Statac writes each page as an index.html in a folder named by its URL, unless the page’s permalink ends with a file name, such as /about.html. For example, it writes /posts/post-1/ as posts/post-1/index.html.

A folder’s own page

content/post-1/index.md
content/post-1/file-1.pdf

Statac publishes a folder’s index.md at the folder’s URL, unless its permalink gives another. If it is the only page in the folder, Statac publishes the folder’s other files beside it, even when its permalink moves it. Here Statac publishes the PDF at /post-1/file-1.pdf, and the page links to it as [the file](file-1.pdf).

The slug

A page’s slug is the last part of its URL. Statac makes it from the file’s name, in lower case, with its words joined by hyphens. Also:

  • Statac removes a date at the start of the name: 2026-10-04-post-1.md becomes post-1.
  • Statac drops the accents from Latin letters: Café.md becomes cafe. It keeps letters of other scripts unchanged.
  • In a numbered folder, Statac removes the number at the start of the name.

A numbered folder

File URL
content/days/01-day-one.md /days/day-one/
content/days/02-day-two.md /days/day-two/
content/days/03-day-three.md /days/day-three/

A folder is numbered if the name of every page in it starts with a number and a hyphen. The numbers set the folder’s order, and Statac leaves them out of the URLs, so renumbering a page never changes its URL.

If the numbers are meant to be part of the names, write numbered: false in the folder’s index.md. The slugs then keep the numbers.

permalink: /about/

With permalink, Statac publishes the page at the URL you give, counted from the top of the site. The URL can also be a pattern, with placeholders that Statac fills in for each page:

Placeholder What Statac puts in its place For content/posts/2026-10-04-post-1.md
{year}, {month}, {day} The page’s date 2026, 10, 04
{slug} The page’s slug, made as above post-1
{name} The file’s name exactly as written, without its extension 2026-10-04-post-1
{folder} The name of the folder the page is in posts
A field declared in statac.yaml, such as {isbn} for fields.isbn: text The page’s value for the field, made into a slug the same way 978-0-14-044913-6, for isbn: 978-0-14-044913-6

A pattern is most useful when it cascades to many pages. In content/posts/index.md:

---
title: Posts
pages.permalink: /{year}/{month}/{slug}/
---

This publishes content/posts/post-1.md, dated 4 October 2026, at /2026/10/post-1/.

A pattern with a declared field cascades the same way: pages.permalink: /books/{isbn}/ in content/books/index.md publishes each book at its ISBN. A pattern names a field nested under another by its path, such as {book.author} for author: under book:, declared as fields.book.author: text. Each page the pattern gives a URL must have the field, or the build stops:

error  Unusable URL: `content/books/three.md` has no `isbn` for `{isbn}`
  content/books/index.md, line 3
   3 │ pages.permalink: /books/{isbn}/
     │                         ^^^^^^
  Add `isbn:` to the page, or take `{isbn}` out of the pattern.
  More: https://statac.dev/e/unusable-url/

See declaring custom fields and unusable URL.

  • A folder’s index.md moves with its own permalink. In content/posts/index.md, permalink: /blog/ publishes the folder’s list page at /blog/, with its pagination beneath it if it has any, and its feed beside it if it asks for one. The folder’s other pages and files keep their own URLs. A pages.permalink pattern from a folder above moves the pages in a folder, never the folder’s index.md.
  • On a site in several languages, Statac takes the pattern from the folder for the page’s language: see URLs in each language.

A site in a folder of its domain

In statac.yaml:

url: https://example.org/blog/

If the site’s url has a path, every page’s URL starts with that path. For example, page.url for content/post-1.md is /blog/post-1/. Where a URL starting with / then leads depends on where you write it:

Where you write it A leading / means So /post-1/ leads to
permalink, redirect_from or redirect_to the top of the site /blog/post-1/
@url(/post-1/) in a page’s content, {{ url("/post-1/") }} in a template the top of the site /blog/post-1/
A Markdown image whose file Statac finds, ![Photo 1](/photo-1.jpg) the top of the site the image, under /blog/
A plain link, in a page’s content or a template, [Post 1](/post-1/) the top of the domain /post-1/, outside the site

Warning

Statac never changes where a link you write leads. So on a site under a path, a plain link starting with / leads outside the site. Write @url(/post-1/) instead, as links inside the site describes.

To build the site once at another URL, see a preview at another URL.

Old URLs and redirects

Keep an old URL working

redirect_from: [/2025/post-1/, /old/post-1/]

At each URL listed in redirect_from, Statac publishes a redirect to this page: a small page built with templates/redirect.html. If the site has no such template, Statac publishes a refresh to this page and a link to it, and nothing else. For a page at /posts/post-1/, on a site with no url:

<meta http-equiv="refresh" content="0; url=/posts/post-1/">
<a href="/posts/post-1/">/posts/post-1/</a>

Redirect a page to another URL

---
title: Post 1
redirect_to: /posts/post-1/
---

A page with redirect_to publishes only a redirect, at its own URL.

redirect_to is What Statac does
A URL on the site, counted from the top, which may end with # and an id on the page Redirects there.
A full URL elsewhere, such as https://example.org/post-1/ Redirects there.
Another redirect Follows it, and redirects straight to where it ends.

If a page with redirect_to has a dated file name, the date stays in its slug: Statac publishes content/2019-05-01-post-1.md at /2019-05-01-post-1/, not /post-1/, unless its permalink gives another URL.

If a folder’s index.md, or a page at a tag’s URL, has redirect_to, Statac publishes the redirect at the list’s URL, and makes no list page, pagination or feed there.

What a redirect page reads

A page with redirect_to is not a post. It doesn’t appear in any list or feed, or in site.pages. Statac reads these fields from its front matter: title, date, permalink, draft, needs, redirect_from, order and numbered, and custom fields. Statac never publishes the content below its front matter.

templates/redirect.html builds the redirect for both redirect_from and redirect_to. Without the template, each redirect is only a refresh and a link, as above. The template is handed:

Name What it contains
to The URL the redirect leads to. If that URL is on the site and the site has a url, to is the full URL, such as https://example.org/posts/post-1/.
title The title of the page that makes the redirect. If it has none, what headline returns for the page published at to. If there is no such page, to itself.
page The page that makes the redirect, with the redirect_from or redirect_to.

A redirect template can be this short:

<!doctype html>
<meta charset="utf-8">
<title>{{ title }}</title>
<meta name="robots" content="noindex">
<meta http-equiv="refresh" content="0; url={{ to }}">
<link rel="canonical" href="{{ to }}">
<p>This page has moved to <a href="{{ to }}">{{ to }}</a>.</p>

noindex asks search engines to leave the redirect out of their results, and the canonical link tells them which page to show instead.

Keep track of published URLs

Each time Statac builds the site, and while statac serve runs, it adds every URL it publishes to published.txt, at the top of the site’s folder. Commit the file with the site, so that a build on any machine knows the same URLs.

If Statac no longer publishes a URL in the file, for example because you renamed or moved a page or gave it another permalink, Statac warns and gives the redirect_from line to add to the page that took its place. If the page is gone for good, take its line out of published.txt. See URL gone.

Statac never adds some URLs, because they come and go with what is listed: a list page that Statac makes itself, such as a tag’s, pagination, and a tag’s or a series’ feed. Nor does it add the files in assets/. It adds drafts and scheduled posts once it publishes them. If a URL appears and goes again while one statac serve runs, such as a page you renamed while writing it, Statac takes it out of the file without a warning.

How each file is published

In content/ What Statac publishes
A page HTML, at its URL.
A file that isn’t a page or an image, such as posts/file-1.pdf The file unchanged, at the same place: /posts/file-1.pdf.
A JPEG, or a PNG or WebP that isn’t animated, that a page shows Resized copies in several sizes, next to where the original would be. Statac publishes the original too if a page links to it.
A JPEG, PNG or WebP that a page only links to, or that no page shows or links to The original, at the same place.
A JPEG, PNG or WebP with optimise: false The original, with no copies: see leaving an image unchanged.
An animated PNG or WebP The original, with no copies.
Any other image, such as a GIF, an SVG or an AVIF The file unchanged.
An image’s .yaml file, such as photo-1.jpg.yaml Nothing.
A hidden file (one whose name starts with a dot), or a file or folder that exclude names, either by its name wherever it is, such as drafts, or by its path from content/, such as posts/post-1.md Nothing.

The resized copies hold nothing about where or when the image was taken, or the device that took it. The original, if Statac publishes it, still does: see the original image.

To publish a file unchanged, put it in static/.

Where next?

Something unclear or wrong? Open an issue.