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/ |
content/ | /posts/post-1/ |
content/, in a numbered folder | /days/day-one/ |
content/Café.md | /cafe/ |
content/ | /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/.
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.mdbecomespost-1. - Statac drops the accents from Latin letters:
Café.mdbecomescafe. 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/day-one/ |
content/ | /days/day-two/ |
content/ | /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.
Choose a URL with permalink
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/ |
|---|---|---|
{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/, dated 4 October 2026, at
/2026/10/post-1/.
A pattern with a declared field cascades the same way:
pages.permalink: / 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.mdmoves with its ownpermalink. Incontent/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. Apages.permalinkpattern from a folder above moves the pages in a folder, never the folder’sindex.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,  | 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/. 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:/ | 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/ 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/ 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:/. |
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?
- Lists, tags and series: numbered folders, tag pages, and where a list’s pagination goes
- Writing: links, and
@urlfor links inside the site - Images: resized copies, and leaving an image unchanged
- Building and publishing: building the site, and a preview at another URL
Something unclear or wrong? Open an issue.