Publish
Webmentions
A webmention is another site telling yours that it linked to one of your pages, as a reply, a like, a repost, a bookmark or a plain mention.
Turn them on
In statac.yaml:
url: https://example.org/
webmentions: true
Webmentions are off without this line, and need url. A relay,
webmention.io, receives them for your site and
keeps them: sign in there with your site’s address. Statac fetches them
from it with the token it gives you.
The token
STATAC_WEBMENTION_TOKEN=your-token statac refresh
The token reads your mentions. It is yours, not the site’s, so it never
goes in statac.yaml or any file you commit. statac refresh and
statac serve read it from STATAC_WEBMENTION_TOKEN, or else from the
one line of .statac/token.
The .gitignore that statac new writes keeps .statac/ out
of the site’s history. Deleting .statac/ deletes the token too.
`.statac/token` wasn't used: it holds more than one line.
Webmentions weren't asked for: no token is in `STATAC_WEBMENTION_TOKEN` or `.statac/token`.
A token file that is empty, holds more than one line or is a link is
left unused. Without a token, refresh fetches the stills alone.
Statac never shows the token or writes it anywhere, and statac build
and statac check never need it. If a token has ever been committed,
ask webmention.io for a new one.
Tell other sites where to send them
In the template that writes your pages’ <head>:
<head>
<title>{{ headline(page) }}</title>
{{ webmention() }}
</head>
webmention() writes the line that tells another site where to send a
mention of the page:
<link rel="webmention" href="https://webmention.io/example.org/webmention">,
with your site’s host. A preview built with --url
still names the site’s own host.
What refresh keeps
Fetched `fetched/faces/0123456789abcdef.jpg`.
Fetched `fetched/mentions/2028899.yaml`.
Webmention `2028900` not kept: `https://example.com/post-1/` isn't on this site.
Fetched 1 webmention and 1 face. 1 not kept.
statac refresh asks the relay for every mention since the last one it
kept, and keeps each as a file of its own in fetched/mentions/. Serve
does the same as it starts.
In fetched/mentions/2028899.yaml:
kind: "replied"
author: "A"
author_url: "https://a.example/"
face: "0123456789abcdef.jpg"
url: "https://a.example/post-1/"
target: "https://example.org/post-1/"
published: "2026-09-03"
fetched: "2026-10-06"
words: "Words."
| Field | What it holds |
|---|---|
kind |
replied, liked, reposted, bookmarked or mentioned. |
author, author_url |
Who sent it, and their own page. |
face |
Their picture, in fetched/faces/. |
url |
The page that sent it. |
target |
The page of yours it is of. |
published, fetched |
The day the mention was published, and the day Statac fetched it. |
words |
Its text alone, never its HTML, cut to 2,000 characters. |
Everything in a webmention was written by someone else, so Statac keeps
its words as plain text, and an address only where it starts http://
or https://.
A mention of an address that isn’t on your site isn’t kept. Kept or
not, each has its line in fetched/record, so
it is never asked for again.
Faces
The sender’s picture is fetched once, made again from its pixels at 96
pixels wide with nothing else of the file kept, and kept in
fetched/faces/.
A page shows a face only as the copies Statac makes of it, so your readers’ browsers never ask another site for it.
How old they are
Fetched: 87 stills, the oldest 6 days ago; 12 webmentions, the newest today.
statac build says how long ago the newest webmention was fetched, after
the stills: see how old is it?
Show them on a page
In a post’s front matter:
mentions: true
In a new site, this shows the post’s webmentions after the post, with
snippets/mentions.html: see the starter. On its notes
and photographs, mentions: true only writes the line saying where to
send them.
mentions: true gives page.mentions, which a layout reads. Write it on
one page, or hand it down. Statac ships
no layout for webmentions. In one of yours, such as
templates/page.html after {{ page.content }}:
{% set mentions = page.webmentions %}
{% if page.mentions and (mentions.said or mentions.faces) %}
<section class="mentions">
{% for mention in mentions.faces %}
<a href="{{ mention.url }}" rel="nofollow ugc">{% if mention.face %}<img src="{{ mention.face.src }}" width="{{ mention.face.width }}" height="{{ mention.face.height }}" alt="">{% endif %}{% if mention.author %}{{ mention.author }}{% endif %} {{ mention.kind }}</a>
{% endfor %}
{% for mention in mentions.said %}
<article>
<p><a href="{{ mention.url }}" rel="nofollow ugc">{% if mention.author %}{{ mention.author }}{% endif %} {{ mention.kind }}</a></p>
{% if mention.words %}<p>{{ mention.words }}</p>{% endif %}
</article>
{% endfor %}
</section>
{% endif %}
page.webmentions gives the page’s mentions, oldest first: said, the
replies, mentions and bookmarks, and faces, the likes and reposts.
What a mention gives lists the fields.
Each link carries rel="nofollow ugc": it leads to a page someone else
chose.
Warning:
Print a mention’s words as they are, as above. Never hand them to
markdown or safe: both put any HTML in them into your page as HTML,
and the words are someone else’s.
A mention belongs to the page published at its address, or to the page
whose redirect_from holds that address.
Microformats
The starter’s snippets/mentions.html marks each mention up as
microformats:
| Class | On |
|---|---|
h-cite |
Each mention. |
u-url |
The link to the page that sent it. |
p-author h-card |
Who sent it, with u-photo on their face and p-name on their name. |
dt-published |
Its day, in a <time>. |
p-content |
Its words. |
u-in-reply-to, u-like-of, u-repost-of, u-bookmark-of |
The page a reply, a like, a repost or a bookmark answers, on a <data> element that shows nothing. |
Sending webmentions
Statac never sends a webmention: when a post of yours links to another site, that site isn’t told. Send one yourself once the post is published.
Turn one away
statac refuse https://a.example/post-1/
Refused `https://a.example/post-1/`: 1 webmention removed.
Removed `fetched/mentions/2028899.yaml`.
statac refuse turns a mention away for good: it removes it now, and
statac refresh never keeps it again.
Give it the address of the page that sent the mention, as the mention’s
file gives it in url. The scheme, a leading www., the capitals of
the host and a final slash make no difference.
Everything from one sender
statac refuse https://a.example/
Refused everything from `https://a.example/`: 3 webmentions removed.
Removed `fetched/mentions/2028899.yaml`.
Removed `fetched/mentions/2028900.yaml`.
Removed `fetched/mentions/2028907.yaml`.
Removed `fetched/faces/0123456789abcdef.jpg`.
Give the address of the sender’s site, or of a part of it. That takes
every mention whose url or author_url is at the address or under it,
and whatever comes later from there: https://a.example/a takes in
https://a.example/a/post-1/ but not https://a.example/ab/.
An address is taken as one mention only where exactly one mention was sent from it and nothing else kept is under it.
Options
| Option | What it does |
|---|---|
--site=<folder> |
The site’s folder, the same as naming it after the address: statac refuse https://a.example/ docs. |
--today=<date> |
The day the refusal is recorded on, as 2026-10-04. Without it, today on this computer’s clock. |
refuse reads only fetched/, so it works on a site with an error
elsewhere.
Nothing refused: no webmention kept here is from `https://a.example/`.
What refuse changes
In fetched/refused:
statac refused 1
sender https://a.example/ 2026-10-06
mention https://b.example/post-1/ 2026-10-06
refuse lists the address in fetched/refused, removes each mention’s
file and marks its line in fetched/record
absent. It removes a face no mention left names.
Webmention `2028911` not kept: `https://a.example/` is refused.
From then on, statac refresh keeps nothing from a refused address,
and says so.
Undo a refusal
Remove the address’s line from fetched/refused, and each refused
mention’s absent line from fetched/record, then run statac refresh.
Note:
The relay sends only what is newer than the newest mention the record has a line for, so a mention comes back only where nothing newer has been kept since.
When something is wrong
Statac reports each problem itself, with what to do: Problems explained lists them. The one that isn’t a mistake:
| Problem | What happened | What to do |
|---|---|---|
mention-of-no-page |
A mention is of an address where nothing is published, mostly because a page moved. No page shows it. | Add the old address to the page’s redirect_from, or delete the mention’s file. |
Where next?
- Fetching from other sites:
statac refresh,fetched/and its record - Templates: what a mention gives a template
- Settings:
webmentionsand where the token goes - The starter:
snippets/mentions.html, and every word indata/words.yamlwith its English
Something unclear or wrong? Open an issue on GitHub.