skip to main content

Host your own webmention receiver

published icon  |  category icon webdesign

Yesterday’s “Webmentions Beyond” IndieWeb event (see notes) exposed a problem: many bloggers exploring the IndieWeb and Webmention world simply make use of, a hosted solution that handles receiving mentions, enabling you to treat the whole system as a black box. Aaron wondered: “why are so many people using" - and the answer is quite obvious: because it’s easy. Just add <link rel="webmention href=""> to your HTML header after signing up and you’re done.

The IndieWeb community sometimes forgets that the community mostly consists of tech enthusiasts who know how to program and configure stuff, while many bloggers just want a way to interact with other blogs. They discover the Webmention system, read somewhere it’s a contemporary and maybe better alternative to Pingbacks (although I contested that), and just want to “make use” of it, without losing hours and hours of fiddling with scary code.

So, is there a decent alternative? No. But their are alternatives which allow you to host your own receiver. Again, these are very much geared towards techies…

Enabling Webmentions on Hugo

Another sore point seemed to be the lack of decent guides for people to follow when trying to enable Webmentions - beyond the “standard” - on their static sites. Since I wrote a Jamstack/webmention microservice called go-jamming which is used on my blogs, I’d like to take this opportunity to show you how easy it is to do it yourself.

Step 1: Running go-jamming yourself

The service is a single binary that is super easy to run anywhere. An amd64 version binary is available in the GitHub releases page. Follow the instructions if you’d like to install it as a Linux service or set up a reverse proxy through Nginx. If all goes according to plan, you’ll end up with a and pingback endpiont others can POST to. There!

Go-jamming should be an easy drop-in replacement for However, since I wrote it with my mainly own needs taken into consideration, and nobody else as far as I know is running it, it could be that you require a missing feature or so. Ping me, fork the code, … and we’ll work it out.

Step 2: Retrieve mentions and store locally

There are simple GET endpoints available that allow you to fetch mentions from the go-jamming service. I store these under data/webmentions.json, enabling the Hugo template engine to access this data. We’ll get to that in a minute. How to retrieve? Issue a simple GET request.

The question is, how to fit this into a typical Hugo/Jamstack build? I have a single JS file in the root of my website that fetches this data using jam-my-stack, a few simple wrappers around services such as Lunr and go-jamming:

const mentions = await webmention.getWebmentions("")
const json = JSON.stringify(mentions, null, 4)
await fsp.writeFile(`${__dirname}/data/webmentions.json`, json, 'utf-8')

There is zero magic involved in getWebmentions(): see webmention.get.js source, if you do not want to depend on jam-my-stack.

What triggers this JS script? A GitHub actions workflow (Addendum 07/2022: I no longer rely on GitHub’s workflows). It also automatically checks in any changes! This is a great way to get notified of new mentions. git pull tells me if new entries are added into the JSON file. This saves me from implementing a stupid mail function in the service.

Step 3: Integrate webmentions in your Hugo template

Now that we have a local JSON file, things are dead simple:

{{ $mentions := (where .Site.Data.webmentions "relativeTarget" "==" $.RelPermalink) }}
{{ range $mentions }}
  <p class="p-content p-name">
    {{ .content | safeHTML }}
{{ end }}

See my full template file for a complete example. How do you know which properties to use? Well, a go-jamming mention looks like this:

        "author": {
            "name": "Wouter Groeneveld",
            "picture": "/pictures/"
        "name": "",
        "content": "More nineties collecting nostalgia. After I wrote about it at my wife dug up a shoebox full of… well… judge for yourself. From left to right:Kinder Surprise figurines, Polly Pock...",
        "published": "2021-05-13T15:20:00+00:00",
        "url": "",
        "type": "mention",
        "source": "",
        "target": "",
        "relativeTarget": "/post/2021/05/nineties-collecting-nostalgia/"

See the go-jamming documentation for more information.

Step 4: Sending out webmentions

You can also leverage go-jamming’s built-in ability to scan your RSS to periodically send out webmentions. Again, if you use jam-my-stack, it’s as easy as writing await webmention.send("") that is also triggered using the same GitHub build action.

Go-jamming does all the rest, such as:

  1. Checking whether there’s a new article in your /index.xml feed;
  2. Collecting links to send out;
  3. Checking if the links have a webmention or pingback endpoint;
  4. Sending out the mentions accordingly.

It remembers the last processed post as not to overwhelm other endpoints, so you can call send() as many times as you’d like. If you do not like jam-my-stack as a dependency, just write your own: it’s simply a PUT request (see send.js in jam-my-stack)!

Does this solve the issue?

Well, yes and no. There are alternatives, such as the one I’ve written myself, but these do assume a certain level of technical proficiency. Go-jamming makes it possible to service multiple websites: in fact, my endpoint takes care of this site, and But I do not want to host mentions of others, that kind of defeats the purpose of “owning your data”.

There was also talk about writing “plugins” for static site generators in the meeting yesterday. However, I don’t think a plug-and-play version of something like this is likely to ever exist: templates are inherently personal. In my opinion, the method explained in this post is as good as it gets. I do not want to conform API contracts or input/output of go-jamming to yet another (IndieWeb or otherwise) standard that eases drop-ins of webmention servers but puts more burden on the developers of those services…

tags icon indieweb webmentions hugo

I’ve done a lot of work to this site since my last post. First and foremost, I got Webmentions setup 🎉🎊🕺. I’ve documented the step-by-step setup in the repo already, so I won’t repeat it here but I thought it would be fun to record who...

 | by 

💬 Comment on… (…)

 | by 

💬 Comment on

 | by 

💬 Comment on

 | by 

I actually can’t recall anymore, what got me started with this whole IndieWeb thing. According to my browser history, I visited on June 15, 2022, so around a month ago. I read up on Miriam Suzanne’s hugely popular post Am I on the In...

 | by 

Yes and yes. See and

 | by 

This is the first webmention I have sent in a while. Super excited! That aside, I wanted to say that I agreed with your point about being the most convenient option. I am presently considering whether to set up a webmention receiver in...

 | by 

I'm Wouter Groeneveld, a Brain Baker, and I love the smell of freshly baked thoughts (and bread) in the morning. I sometimes convince others to bake their brain (and bread) too.

If you found this article amusing and/or helpful, you can support me via PayPal or Ko-Fi. I also like to hear your feedback via Mastodon or e-mail. Thanks!