webmention.io: a hosted receiver for a protocol your site only points at
Easily enable webmentions on any web site
At a glance
- What is it?
- webmention.io is a PHP implementation of the Webmention protocol, built so the receiving side runs as its own service rather than inside your blog. It gives static sites a webmention endpoint, a JSON Feed API, and a dashboard, and the README is unusually explicit about how mentions get filed.
- Who is it for?
- Adopt webmention.io if you publish a static site and want webmentions without writing a receiver, since the whole protocol reduces to one link tag and a JSON Feed endpoint you can poll. Do not adopt it if your blogging platform already implements Webmention, since you would then maintain a second site registration for no gain, and do not self-host it without reading the private network and proxy settings first.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository last received commits 6 days ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The receiving end is a separate service, not a blog plugin
webmention.io is an implementation of the Webmention protocol, written in PHP, whose stated purpose is to let the webmention receiving service be run separately from the blogging software or website environment, which makes it easier to manage and integrate with other services.
That separation is the whole design. The protocol lets a site announce, in a link tag or a response header, that it accepts webmentions and where to send them. A receiving service then holds the mentions on the site's behalf. By moving that service out of the blog, a static site gets the feature without a plugin, a database migration or a server-side language it otherwise does not run.
The README spells out the scenario: a statically generated site using Jekyll or something similar can add the appropriate link tag pointing at this service, and now it has webmentions enabled. The homepage recorded for the repository is the service itself at webmention.io, and the topics are indieweb and webmention, so the project sits squarely in the IndieWeb corner of the web rather than in general comment tooling.
The audience is therefore site owners rather than framework developers. If you run a server-rendered application with its own comment system and its own database, the separation buys you little, because you already control the runtime.
Two ways to announce the endpoint, and one URL to remember
A site opts in with a link tag. The README gives the exact form to add to the page head, with the domain of your site substituted for example.com:
<link rel="webmention" href="https://webmention.io/example.com/webmention" />The protocol also allows the endpoint to be specified in headers instead, which the README shows as:
Link: <https://webmention.io/example.com/webmention>; rel="webmention"The header form matters for sites that are generated or served by something you do not control the markup of. A static generator gives you a template, so the tag is trivial, but a site served by a platform that renders HTML itself may have to add the header at the edge.
Once the tag is in place, the endpoint path carries your domain, which means one service can host mentions for many sites, and the site is identified by the path segment rather than by an account. The features list in the README is short and concrete: accept webmentions for any site by adding that tag, an API to get a list of pages linking to your site or a specific page, web hooks when a webmention is received or deleted, support for Private Webmentions, and a dashboard to delete webmentions.
The API speaks JSON Feed, with a count endpoint for badges
The API documentation is rendered at webmention.io/api, and the README reproduces it. Finding the pages that link to a given page is a single GET with a target parameter:
GET https://webmention.io/api/mentions.jf2?target=https://indieweb.orgThe response is a JSON Feed document with a type of feed and a name of Webmentions, whose children are entries. Each entry carries the author card, the source URL, the target, the protocol, the wm-id, the received timestamp, the content in both text and html, and a wm-property describing the kind of mention. Private mentions are marked with a wm-private field.
For a badge or a counter there is a lighter endpoint:
GET https://webmention.io/api/count?target=https://example.com/page/100It returns a count plus a breakdown by type, where the types are bookmark, mention and the RSVP values rsvp-maybe, rsvp-no and rsvp-yes.
Filtering is done with the wm-property parameter, which can be repeated to request several types at once, for example wm-property=in-reply-to or wm-property[]=in-reply-to&wm-property[]=rsvp. The recognized properties are in-reply-to, like-of, repost-of, bookmark-of, mention-of and rsvp. The README notes that mention-of matches every mention that is not one of the other kinds, including older mentions stored without a type, and that rsvp matches all RSVP values.
Beyond a single target, the API accepts repeated target parameters for several pages, a domain parameter to list every link to a whole domain, and a since parameter for polling, with a timestamp in the form used in the example, since=2017-06-01T10:00:00-0700. Sorting is selectable with sort-by, where created is the default, updated reflects the updated date of the page as seen by the service rather than the date the post itself reports, and published follows the date the page reports. The README's sorting section is cut off mid sentence, so the details of the published option are not spelled out.
Which URL a mention is filed under is decided by your redirects
This is the part of the README that deserves a careful read, because it explains where a mention ends up, and the answer is not the URL the sender used.
A mention is filed under the target's canonical URL. When a target is first seen, the service fetches it, follows your site's redirects, honours its rel="canonical" link, and files the mention under the URL it ends up at, as long as that URL is on one of your sites. A fragment in the target is ignored when deciding which page the mention belongs to, but it is recorded: a query for a target with a fragment returns only the mentions sent to that fragment, each carrying it as wm-fragment, while a query without the fragment returns all of them. Two mentions from one source to two fragments of a page are two separate webmentions.
There is a transition built into that design. Rows received before September 2026 have no fragment recorded and answer only the fragment-less query, so the behaviour of old data differs from the behaviour of new data by design rather than by accident.
Every other form that led to the page is remembered as an alias, so queries for the URL as the sender gave it, for an old URL that now redirects, or for a fragment URL all return the same mentions, while wm-target is always the canonical URL. One rule is deliberately left to you: trailing slashes and http versus https are not treated as equivalent, and your site decides, by redirecting. If you change a page's URL later, the Sites page offers a "Moved a page?" option to re-file its mentions once the old URL redirects.
Deleting a mention also blocks the source URL
The dashboard is more than a list. The README describes a dashboard to delete webmentions, and the deletion has a side effect: deleting a mention also blocks its source URL for that site, so a mention from that source is refused if it is sent again. Blocked URLs and blocked domains are listed, and can be unblocked, under Settings, then Blocklists.
That behaviour answers a question every mention receiver has to answer. A spammer whose link was removed will simply send it again, and a plain delete loop would make the dashboard busy rather than make the problem smaller. Blocking the source at the same time turns the delete into a decision that persists.
The same section of the README covers Private Webmentions, which are only included in token-authenticated listings, and states that queries by target are public and never return them. The token can be sent as a query parameter or as a header, and the README notes that the header form keeps it out of URLs and access logs:
GET https://webmention.io/api/mentions.jf2?domain=indiewebcamp.com
Authorization: Bearer xxxxxThe web hooks feature completes the picture: the service can notify you when a webmention is received or deleted, which is how a site that renders its own mention list stays current without polling on every request.
A self-hosted deployment is a PHP app with MySQL, Redis and a proxy decision
The repository is a PHP application rather than a library. The top level holds composer.json and composer.lock, a database/ directory, a public/ directory with the web root, src/ for the code, templates/ for the views, tools/, tests/, a phpunit.xml, a docs/ directory and a .env.example that documents the configuration surface.
The example environment file is the best specification the project offers. It sets the public URL and a MySQL database, with the port 3306, plus a Redis host and port 6379, which is what a worker queue needs:
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=webmention
DB_USER=webmention
DB_PASS=Three of those settings decide whether a deployment behaves safely, and each is a trap worth reading twice. ALLOW_PRIVATE_NETWORK is 0 by default, and outgoing requests refuse private and loopback addresses; the file says to set it to 1 only for local development. TRUST_PROXY is 0 by default and should be 1 only behind a proxy that sets X-Forwarded-Proto and X-Forwarded-For. APP_DEBUG is 0 by default, with a note to never enable it in production, since it shows exception details on error pages.
Two more settings shape behaviour. INDIELOGIN_URL points at indielogin.com and is how sites with no IndieAuth endpoint, only rel="me" links, are signed in; the file says to set it to off to refuse them instead. ADMIN_USERS lists the accounts that can reach the admin section, matched against a username or a domain, and an empty value means nobody. The CA3DB settings are for avatar archiving, and leaving CA3DB_API_ENDPOINT empty keeps the original photo URLs.
The README gives no install command, so there is nothing to copy into a terminal. The deployment path runs through composer, the .env file and the worker processes that write to the log directory.
Where the hosted service is the wrong choice
There is a clear case for not using it. If your site already runs a comment system or a mention receiver of its own, registering a second domain with an external service splits your mentions across two places and adds a network dependency to a page you already control. The whole premise of webmention.io is that the receiver lives elsewhere, which is an advantage for a static site and a liability for a dynamic one.
The service also defines a vocabulary you have to accept. Mentions are grouped by canonical URL, redirected forms become aliases, fragments create separate entries, and the type taxonomy is fixed to the recognized properties. If your content model needs a different notion of a page, or your links need to be grouped by topic rather than by URL, the model has to be bent to fit the service.
Two packaging facts deserve a look before you rely on the code. The repository listing shows no licence for the project, and the README in this repository does not carry a licence section, so if you intend to run your own instance rather than use the hosted one, that question needs an answer from the project before you deploy it. There are also no GitHub releases, so version tracking is a matter of commits rather than tags. The last push was on 2026-10-17, and the default branch is named php, which suggests the branch carries the PHP implementation of the service rather than a static site.
Editorial conclusion
Adopt webmention.io if you publish a static site and want webmentions without writing a receiver, since the whole protocol reduces to one link tag and a JSON Feed endpoint you can poll. Do not adopt it if your blogging platform already implements Webmention, since you would then maintain a second site registration for no gain, and do not self-host it without reading the private network and proxy settings first. Before you point a site at it, confirm how a mention is filed under the canonical URL of your page, including what happens to a fragment after you change a page address, because the README documents that behaviour in detail and it decides where a mention ends up.
Frequently asked questions
How do I enable webmentions on a static site with webmention.io?
Add the link tag the README gives, pointing at the endpoint for your domain, for example a link tag with rel="webmention" and an href of https://webmention.io/example.com/webmention. The endpoint can also be advertised with a Link header instead of a tag.
How do I list the pages that link to my site through the webmention.io API?
Request the mentions.jf2 endpoint with a target parameter, or a domain parameter for every link to a whole domain, and authenticate with a token. The response is a JSON Feed document whose entries carry the source, target, author and the wm-property describing the kind of mention.
Are private webmentions visible through the webmention.io API?
Private Webmentions are only included in token-authenticated listings, and queries by target are public and never return them. The token can be passed as a query parameter or as an Authorization header, and the README notes the header form keeps it out of URLs and access logs.
What happens when I delete a webmention in the webmention.io dashboard?
Deleting a mention also blocks its source URL for that site, so it is refused if sent again. Blocked URLs and blocked domains appear under Settings, Blocklists, where they can be unblocked.
What does it take to self-host webmention.io?
The repository is a PHP application with composer.json, a database/ directory, a public/ web root and a .env.example covering MySQL on port 3306 and Redis on port 6379. The README gives no install command, and the example environment file warns that ALLOW_PRIVATE_NETWORK should stay 0 outside local development, TRUST_PROXY should be 1 only behind a proxy, and APP_DEBUG should never be on in production.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/aaronpk-webmention-io)Was this useful?