Documentation
Routing and content negotiation
How a Markdown-mode site turns a request path into content, and how the same page can answer with either HTML or its Markdown source.
Special endpoints
These are answered before any file lookup:
| URL | Response |
|---|---|
/sitemap.xml | XML sitemap of all pages |
/index.xml | RSS 2.0 feed |
/search-index.json | Search index as JSON |
/search | Search page |
/tags/, /tags/<name> | All tags, and the pages carrying one |
/<taxonomy>/, /<taxonomy>/<value> | The same for a custom taxonomy |
Resolving a page
For any other path:
GET /<slug>
1. <slug>.md exists? → render as Markdown
2. <slug>/index.md exists? → render as Markdown
3. <slug>/index.html exists? → serve as-is
4. <slug>/ has .md files? → auto-generated directory listing
5. otherwise → 404
A path with a non-Markdown extension — /style.css, /images/photo.jpg — is
looked up directly and served as-is, with its media type guessed from the
extension.
Legacy .html URLs
Static generators with "ugly URLs" publish pages as /<slug>.html. When such a
request matches no file but the Markdown source exists, Everlock answers 302
to the extensionless route, so inbound links survive a migration without
per-page redirects:
GET /<slug>.html (no such file)
<slug>.md exists? → 302 /<slug>
GET /<dir>/index.html (no such file)
<dir>/index.md or <dir>/_index.md exists? → 302 /<dir>
A literal .html file in the store always wins; the fallback runs only when the
requested file is absent.
Content negotiation
Every rendered page has two representations at one URL: the HTML its layout
produces, and the page's own Markdown source. The client picks with Accept:
GET /bike
Accept: text/markdown, text/html, */*
200 OK
Content-Type: text/markdown; charset=utf-8
Vary: Accept
This follows the convention described at acceptmarkdown.com: a page that has a Markdown source should be able to hand it over when asked, rather than making a reader — or an agent — parse prose back out of markup.
What the Markdown representation is. The source after front matter removal
and shortcode expansion — the same text the HTML renderer consumes. Shortcodes
that expand to HTML remain as inline HTML. When the front matter carries a
title and the body does not open with a level-one heading, the title is emitted
as a leading # heading.
How the choice is made. Selection follows RFC 9110 quality values: Markdown
is served when text/markdown is named explicitly, with a q at least equal to
the one text/html resolves to. Wildcards (*/*, text/*) never select
Markdown, so browsers and a plain curl keep getting HTML. Both representations
carry Vary: Accept, so shared caches keep them apart.
Where it applies. Page routes only — /<slug>, /<slug>.md, and /<dir>/
resolving to index.md. Files served as-is, directory listings, feeds, the
search index and output-format variants ignore Accept. HTML-mode sites have one
representation per file and ignore it too.
Draft and future-dated rules apply to both representations: a page that is not published yet is not published in either form.
Paths that are never served
These return 404 whatever the store holds, because they are project configuration rather than content:
| Path | Reason |
|---|---|
site.toml | Site config |
layouts/* | Templates |
shortcodes/* | Shortcode templates |
Hidden directories and anything named in ignore_dirs are excluded the same
way — see Structure.
404 responses
When no route matches, a Markdown-mode site renders layouts/404.html if the
repository provides one — an ordinary Tera template that may extend
base.html — served with status 404 and the usual site, page, data and
all_pages context, where page.title is Page not found and page.path is
what was requested. Without that template, and always in HTML mode, the response
is plain text.