Webmentions: Receiving and Sending Cross-Site Reactions
Webmentions are the IndieWeb’s cross-site notification protocol. When someone links to one of your posts from their own site, they can send you a webmention — a simple HTTP POST to your webmention endpoint. You can then display those mentions alongside your post.
The protocol is symmetric: you send webmentions out when you link to others, and you receive them when others link to you.
Receiving webmentions with webmention.io
webmention.io is a hosted endpoint that handles the receiving side. After logging in (which uses your domain as your identity — more on that below), it gives you two link tags to add to your <head>:
<link rel="webmention" href="https://webmention.io/adrian-altner.de/webmention" />
<link rel="pingback" href="https://webmention.io/xmlrpc" />
Any page on your site that includes these tags can now receive webmentions. Incoming mentions are validated and stored by webmention.io’s service.
Logging in without a password
webmention.io uses IndieLogin, which authenticates you by your domain. It follows the rel=me links from your homepage, finds one pointing to a provider that supports OAuth (GitHub, for example), and sends you through that OAuth flow. The result: you log in to IndieWeb services as adrian-altner.de, not as a username or email.
Bluesky with a custom domain handle works as a login provider too, but through the AT Protocol verification mechanism rather than rel=me HTML.
Displaying incoming webmentions at build time
Webmention.io exposes a REST API. A build-time Astro component fetches mentions for each page URL and renders them statically:
const WEBMENTION_IO = "https://webmention.io/api/mentions.jf2";
const url = `${WEBMENTION_IO}?target=${encodeURIComponent(pageUrl)}&per-page=100`;
const res = await fetch(url);
const data = await res.json();
const mentions = data.children ?? [];
Mentions are grouped by type — likes (like-of), reposts (repost-of), and replies (in-reply-to, mention-of) — and rendered in separate sections. If there are zero mentions, the component renders nothing.
An optional WEBMENTION_TOKEN environment variable allows authenticated requests, which raises the per-page limit. Without it, the public API works fine for most pages.
The component runs at build time, so mentions are baked into the static HTML. A redeploy is required to pick up new mentions — acceptable for a site that deploys on every content update anyway.
Sending outgoing webmentions with webmention.app
The receiving side is passive. The sending side requires action after each deploy.
webmention.app scans an RSS feed, finds all outgoing links in recent posts, checks whether those pages have webmention endpoints, and sends notifications to them. Authentication is via a token stored in .env.production.
The deploy script triggers this automatically after each successful deploy, once per RSS feed:
for feed in rss.xml rss/articles.xml rss/notes.xml rss/links.xml rss/photos.xml; do
curl -s -X POST \
"https://webmention.app/check?url=https://adrian-altner.de/${feed}&token=${TOKEN}"
done
Five feeds means five passes — articles, notes, links, photos, and the combined feed. Any outgoing link in any recent post gets a webmention if the target supports it.
What triggers a webmention in practice
The typical scenario: you write an article that links to another IndieWeb site. On the next deploy, webmention.app scans the RSS feed, finds the link, discovers the target’s webmention endpoint, and sends the notification. If the target site displays webmentions, your link shows up there.
Currently, most sites don’t support webmentions. But the infrastructure is in place. When a post links to a site that does, the notification goes out automatically.