Indie web building blocks

I was back in Nürnberg last week for the second border:none. Joschi tried an interesting format for this year’s event. The first day was a small conference-like gathering with an interesting mix of speakers, but the second day was much more collaborative, with people working together in “creator units”—part workshop, part round-table discussion.

I teamed up with Aaron to lead the session on all things indie web. It turned out to be a lot of fun. Throughout the day, we introduced the little building blocks, one by one. By the end of the day, it was amazing to see how much progress people made by taking this layered approach of small pieces, loosely stacked.


The first step is: do you have a domain name?

Okay, next step: are you linking from that domain to other profiles of you on the web? Twitter, Instagram, Github, Dribbble, whatever. If so, here’s the first bit of hands-on work: add rel="me" to those links.

<a rel="me" href="">Twitter</a>
<a rel="me" href="">Github</a>
<a rel="me" href="">Flickr</a>

If you don’t have any profiles on other sites, you can still mark up your telephone number or email address with rel="me". You might want to do this in a link element in the head of your HTML.

<link rel="me" href="" />
<link rel="me" href="sms:+447792069292" />


As soon as you’ve done that, you can make use of IndieAuth. This is a technique that demonstrates a recurring theme in indie web building blocks: take advantage of the strengths of existing third-party sites. In this case, IndieAuth piggybacks on top of the fact that many third-party sites have some kind of authentication mechanism, usually through OAuth. The fact that you’re “claiming” a profile on a third-party site using rel="me"—and the third-party profile in turn links back to your site—means that we can use all the smart work that went into their authentication flow.

You can see IndieAuth in action by logging into the Indie Web Camp wiki. It’s pretty nifty.

If you’ve used rel="me" to link to a profile on something like Twitter, Github, or Flickr, you can authenticate with their OAuth flow. If you’ve used rel="me" for your email address or phone number, you can authenticate by email or SMS.


Next question: are you publishing stuff on your site? If so, mark it up using h-entry. This involves adding a few classes to your existing markup.

<article class="h-entry">
  <div class="e-content">
    <p>Having fun with @aaronpk, helping @border_none attendees mark up their sites with rel="me" links, h-entry classes, and webmention endpoints.</p>
  <time class="dt-published" datetime="2014-10-18 08:42:37">8:42am</time>

Now, the reason for doing this isn’t for some theoretical benefit from search engines, or browsers, but simply to make the content you’re publishing machine-parsable (which will come in handy in the next steps).

Aaron published a note on his website, inviting everyone to leave a comment. The trick is though, to leave a comment on Aaron’s site, you need to publish it on your own site.


Here’s my response to Aaron’s post. As well as being published on my own site, it also shows up on Aaron’s. That’s because I sent a webmention to Aaron.

Webmention is basically a reimplementation of pingback, but without any of the XML silliness; it’s just a POST request with two values—the URL of the origin post, and the URL of the response.

My site doesn’t automatically send webmentions to any links I reference in my posts—I should really fix that—but that’s okay; Aaron—like me—has a form under each of his posts where you can paste in the URL of your response.

This is where those h-entry classes come in. If your post is marked up with h-entry, then it can be parsed to figure out which bit of your post is the body, which bit is the author, and so on. If your response isn’t marked up as h-entry, Aaron just displays a link back to your post. But if it is marked up in h-entry, Aaron can show the whole post on his site.

Okay. By this point, we’ve already come really far, and all people had to do was edit their HTML to add some rel attributes and class values.

For true site-to-site communication, you’ll need to have a webmention endpoint. That’s a bit trickier to add to your own site; it requires some programming. Here’s my minimum viable webmention that I wrote in PHP. But there are plenty of existing implentations you can use, like this webmention plug-in for WordPress.

Or you could request an account on, which is basically webmention-as-a-service. Handy!

Once you have a webmention endpoint, you can point to it from the head of your HTML using a link element:

<link rel="mention" href="" />

Now you can receive responses to your posts.

Here’s the really cool bit: if you sign up for Bridgy, you can start receiving responses from third-party sites like Twitter, Facebook, etc. Bridgy just needs to know who you are on those networks, looks at your website, and figures everything out from there. And it automatically turns the responses from those networks into h-entry. It feels like magic!

Here are responses from Twitter to my posts, as captured by Bridgy.


That was mostly what Aaron and I covered in our one-day introduction to the indie web. I think that’s pretty good going.

The next step would be implementing the idea of POSSE: Publish on your Own Site, Syndicate Elsewhere.

You could do this using something as simple as If This, Then That e.g. everytime something crops up in your RSS feed, post it to Twitter, or Facebook, or both. If you don’t have an RSS feed, don’t worry: because you’re already marking your HTML up in h-entry, it can be converted to RSS easily.

I’m doing my own POSSEing to Twitter, which I’ve written about already. Since then, I’ve also started publishing photos here, which I sometimes POSSE to Twitter, and always POSSE to Flickr. Here’s my code for posting to Flickr.

I’d really like to POSSE my photos to Instagram, but that’s impossible. Instagram is a data roach-motel. The API provides no method for posting photos. The only way to post a picture to Instagram is with the Instagram app.

My only option is to do the opposite of POSSEing, which is PESOS: Publish Elsewhere, and Syndicate to your Own Site. To do that, I need to have an endpoint on my own site that can receive posts.


Working side by side with Aaron at border:none inspired me to finally implement one more indie web building block I needed: micropub.

Having a micropub endpoint here on my own site means that I can publish from third-party sites …or even from native apps. The reason why I didn’t have one already was that I thought it would be really complicated to implement. But it turns out that, once again, the trick is to let other services do all the hard work.

First of all, I need to have something to manage authentication. Well, I already have that with IndieAuth. I got that for free just by adding rel="me" to my links to other profiles. So now I can declare as my authorization endpoint in the head of my HTML:

<link rel="authorization_endpoint" href="" />

Now I need some way of creating and issuing authentation tokens. See what I mean about it sounding like hard work? Creating a token endpoint seems complicated.

But once again, someone else has done the hard work so I don’t have to. Tokens-as-a-service:

<link rel="token_endpoint" href="" />

The last piece of the puzzle is to point to my own micropub endpoint:

<link rel="micropub" href="" />

That URL is where I will receive posts from third-party sites and apps (sent through a POST request with an access token in the header). It’s up to me to verify that the post is authenticated properly with a valid access token. Here’s the PHP code I’m using.

It wasn’t nearly as complicated as I thought it would be. By the time a post and a token hits the micropub endpoint, most of the hard work has already been done (authenticating, issuing a token, etc.). But there are still a few steps that I have to do:

  1. Make a GET request (I’m using cURL) back to the token endpoint I specified—sending the access token I’ve been sent in a header—verifying the token.
  2. Check that the “me” value that I get back corresponds to my identity, which is
  3. Take the h-entry values that have been sent as POST variables and create a new post on my site.

I tested my micropub endpoint using Quill, a nice little posting interface that Aaron built. It comes with great documentation, including a guide to creating a micropub endpoint.

It worked.

Here’s another example: Ben Roberts has a posting interface that publishes to micropub, which means I can authenticate myself and post to my site from his interface.

Finally, there’s OwnYourGram, a service that monitors your Instagram account and posts to your micropub endpoint whenever there’s a new photo.

That worked too. And I can also hook up Bridgy to my Instagram account so that any activity on my Instagram photos also gets sent to my webmention endpoint.

Indie Web Camp

Each one of these building blocks unlocks greater and greater power:

Each one of those building blocks you implement unlocks more and more powerful tools:

But its worth remembering that these are just implementation details. What really matters is that you’re publishing your stuff on your website. If you want to use different formats and protocols to do that, that’s absolutely fine. The whole point is that this is the independent web—you can do whatever you please on your own website.

Still, if you decide to start using these tools and technologies, you’ll get the benefit of all the other people who are working on this stuff. If you have the chance to attend an Indie Web Camp, you should definitely take it: I’m always amazed by how much is accomplished in one weekend.

Some people have started referring to the indie web movement. I understand where they’re coming from; it certainly looks like a “movement” from the outside, and if you attend an Indie Web Camp, there’s a great spirit of sharing. But my underlying motivations are entirely selfish. In the same way that I don’t really care about particular formats or protocols, I don’t really care about being part of any kind of “movement.” I care about my website.

As it happens, my selfish motivations align perfectly with the principles of an indie web.

Have you published a response to this? :



btw - freshly posted by adactio on IndieWeb building blocks they got numerous folks up & running with social personal sites last weekend in Nürnberg:

# Posted by tantek on Tuesday, October 21st, 2014 at 9:46am

Bridgy by Ryan Barrett is another fantastic-looking and new-to-me tool for spinning up your own piece of the indie web. I’m really excited for the possibilities presented by it and, both of which I found via Jeremy Keith. His Indie web building blocks post is one more great primer on how all this works.

I have so much to do.

# Wednesday, October 22nd, 2014 at 1:48am


Last week I had the chance to attend the border:none 2014 and listen to very interesting talks. I didn’t know anything about the IndieWeb so far but that changed during the second day with hands-on work on how to integrate the first aspects and ideas.

Basically, own your content, be better connected and in control. Don’t let the social networks become „silos“ by posting everything only there.

Jeremy and Aaron mentioned and shared a lot of services, Github code and behind-the-scene examples.

Do you want to know more? Jeremy wrote a nice introduction on his blog. Enjoy!


# Posted by Bernd on Wednesday, October 22nd, 2014 at 4:55pm

Stephen Hay

Great stuff. I find it kind of sad, in a way, that we have to use the term Indie Web to describe the Web as it once was.

# Posted by Stephen Hay on Saturday, October 14th, 2017 at 12:31pm

Sara Soueidan 🐦

I’m plannin on hostin an ongoing AMA section on my site, as opposed to hashnode or Github or etc. I love this concept of it all being there.

In a recent Twitch session I decided to add Webmentions to my blog, specifically in the flavour of showing “liked” from other websites (though, who am I kidding, it’ll just be Twitter…).

Amazingly I managed it in 90 minutes (with 3 stream crashes to boot).

Adding Webmentions to a site seemed straightforward and a well trodden path. Sending outgoing webmentions on the other hand seems to have been generally left to ones own devices.

So I decided to take up the challenge and build a platform agnostic method of sending outgoing webmentions, that anyone can use.

TL;DR - sending webmentions

I’ve written an application that will scan a URL’s contents and deliver webmentions (and pingbacks where supported) to those sites your post links to (and supports webmentions).

This works with a homepage containing multiple posts, single stand alone posts and RSS feeds. For homepage (or pages with multiple blog posts) there’s the expectation that the h-entry microformat schema is being used.

Please let me introduce:

If relying on a third-party isn’t your bag, then you can install the command line tool and use it with your deploy process.

TL;DR - receiving webmentions

Webmentions are cool and the evolution of pingbacks. They’re also (supposed to be) decentralised, like the web. I’m pretty sure the way I’m using Webmentions on my blog today is wrong…ish.

To add webmentions to your site: connect your socials with Bridgy, auth with and add client side JS webmention.js to your site.

Max Böck also has an excellent write up on using Webmentions on a static site.

My concerns are currently that there’s a great gravitation towards Twitter as the place a post gets mentioned. This lead directly to my next issue: that sending Webmentions for normal blog posts didn’t seem to have a common solution, yet…

Webmentions, Twitter and the status quo

From what I’ve seen so far there’s a particularly strong relationship between Twitter likes/retweets/replies and webmentions appearing on blogs. In fact it’s exactly how I approached Webmentions in the first place - to add likes on my posts. Those “likes” on my site are driven entirely by Twitter.

My site is picking up Twitter likes because Bridgy is checking social media and sending Webmentions to my site. What I’m keen to see is Webmentions properly supersede their predecessor of pingbacks. I want to see “replies” on blog posts actually pointing to other blog posts.

Reading Jeremy Keith’s post on Indie Web building blocks, he touches on what I’m already starting to see:

My site doesn’t automatically send Webmentions to any links I reference in my posts—I should really fix that—but that’s okay; Aaron—like me—has a form under each of his posts where you can paste in the URL of your response.

I’ll acknowledge that starting with a form is a great form of progressive enhancement, but it’s how Jeremy, or anyone for that matter, might go about automating the problem that bothers me. It’s not a particularly simple task and potential bespoke for many.

If the status quo gets stuck at: I write a post, you write a response post (or separate commentary), then you have to come back to my post to enter your blog post URL to notify me of a WebMention, then…it feels…cumbersome, and like it probably won’t stand the test of time.

The ability to send Webmentions needs to be a part of an automated workflow - the same way as posting a new WordPress blog post automatically sent pingbacks.

Equally important is that a website that doesn’t accept webmentions should be able to send webmentions.

So lets go about fixing that.

How to automatically send outgoing Webmention notifications

In the workflow for webmentions, the biggest part that I believe needs automation is sending outgoing webmentions to links referenced in new posts.

So I’ve written my own solution to this:

🎉🌈 ✨💫

I’ve also tried very hard to get the documentation to be as welcoming as I can. I’ve tried to think about my dear visitor and what they want to do with the software, rather than type my typical developer approach to documentation - listing all the features and options.

In addition, (and as usual with most of my projects) the source code is available on Github.

You can give the service a URL to the /check endpoint and it will give you a preview of notifications it will send. To send outgoing notifications (rather than a dry-run) use a POST request.

You can pass it:

  • An RSS feed
  • A single post URL
  • Any URL to find multiple posts (like your homepage)

In addition, if you’re not comfortable relying on a third-party for the notification process (because: who knows what site will outlast yours), you can install it as a command line tool and run locally.

The command line can be used as part of a build process - in this case we’re assuming that the project will generate a static RSS feed in ./_site/feed.xml.

$ npm install @remy/webmention
$ npx webmention ./_site/feed.xml --limit 1 --send

This will pick the first item in the RSS (the latest post) and send any Webmentions found.

In my personal case, I’m using the service as part of my Netlify post-build process - described in detail on the website. Each successful Netlify build calls the service using my token and points to my RSS. As Netlify sends a POST webhook, the webmentions are automatically sent out.

Give it a try, let me know what you think. As I said, I’ve tried to put extra effort into the documentation and my hope that it is simple enough to follow for most bloggers:

# Tuesday, June 18th, 2019 at 2:00pm



I had tried to use IntenseDebate and Disqus with this blog. I found the layout of IntenseDebate is outdated. Automattic did not update this project for a long time. It is a maintained state without new development. For Disqus, it had two problems. Looks like it may display Ads for the free plan. Also the long loading time for a webpage could be a problem. Below is a table that displayed the amount of data transferred by loading a single page. The amount of data transferred from Disqus or Disqus CDN is quite large.

Data transferred by domains Domain Bytes 252,636 154,129 100,758 62,944 52,846 41,215 25,158 22,841 18,469 17,728 17,650 16,947 1,644 1,336 1,087 374 50 43 0 0

This image below shows that about half of the loading time of a website was spent on loading Disqus. Total loading time is 12 seconds (an emulated older version of Android). The total loading time on a desktop should be about 4 seconds.

So I decided to stop loading Disqus and start looking for alternatives.

Enter Webmention

Webmention is a W3C recommendation, which provides a way to notify any URL when it was mentioned on other websites. It could also be used for comments, reply or pingbacks between two different people. Webmention was initially developed or designed by IndieWebCamp. In 2017, it became a W3C recommendation.

Webmention is a improved version of Pingback. It does not use XML-RPC for communication. Also, it is better than trackbacks (less spammy or less fake messages) because of stronger URL checking mechanism in Webmention. This article from wpmudev explained it in detail.

The principle or tenets behind IndieWeb is own your data. It is a huge topic. Webmention is one of the ‘building block’ of Indieweb, also mentioned by Chris Coyier.

In this article, we focus on how to enable Webmention in Pelican. The steps are similar for any other static site generators or website. You should also configure the parameters to use in the template in

Before we get into details, I recommended using to keep track and test if your setting is working. If you are not sure or getting lost, run each checking procedure. Fix the problem and move to next step.

Authenticate to Indieauth

For my blog, I use

We need a way to let Indieauth and other authentication implementations know you are the author of the webpage. It’s the rel=”me” entry.

Add this in the head section of the your home page:

<link rel="me" href="" />

GitHub was used in here. Make sure your GitHub profile contains the URL of your home page.

Then you could sign-in to by Indieauth. Refer to Indieweb authorization endpoints for more details.

Next, we need to add the h-card and h-entry into the articles.


h-card is in microformat2. h-card is used to specify the details of an author. A sample h-card looks like below. Add the elements inside your article. Note I didn’t have styling because I had hidden the h-card with CSS. Feel free to design your own h-card and add other meta tags.

 1 2 3 4 5 6 7 8 9
<section class="h-card"> <p> <a class="u-url u-uid" href="{{ SITEURL }}"> <span class="p-name">{{ AUTHOR }}</span> </a> : <span class="p-note">{{ SITE_DESCRIPTION }} </span> </p> <span>Social contact:</span> <br> <ul> <li> <a class="u-url" rel="me" href="{{ GITHUB_USER }}">Github</a> </li> <li> <a class="u-url" rel="me" href="{{ TWITTER_USERNAME }}">Twitter</a> </li> </ul>

The important elements that were added:


Since we are using Pelican, we could add the h-card and h-entry elements into the suitable place in the Jinja2 article template.


h-entry is in microformat2. h-entry is mainly used to specify the metadata of an article. A sample h-entry looks like below. Add the elements inside your article.

 1 2 3 4 5 6 7 8 9
<article class="h-entry"> <h1 class="p-name">Using MeiliSearch with Pelican</h1> <a class="p-author h-card" href=""> Kappa Wingman</a> <a class="u-url" href="" rel="bookmark" title="Permalink to Using MeiliSearch with Pelican"> </a> </h1> <span class="published"> <time class="dt-published" datetime="2020-07-03T23:20:02+00:00"> 2020-07-03</time> </span> <span class="p-summary">Using MeiliSearch with Pelican</span> <div class="e-content"> <p>Blah blah blah</p> </div> </article>

The important elements that were added:

Receiving Webmention

Basic steps to receive Webmentions:

  • The articles should have - Specify the Webmention endpoint - Enable h-card and h-entry in your articles

With the above setting, when a Webmention is to be send, a remote program could scan the article for the h-card and h-entry microformat. Then the remote program extracts the elements from the article and send the Webmention to the endpoint.

Specify the Webmention endpoint

You could self host your endpoint or using for free. Inside the head section of your article, specify:

<link rel="webmention" href="" />
<link rel="pingback" href="" />

Replace username by your username in If you don’t want pinback support then remove the last line to turn it off.

Displaying Webmention in the blog

There are several methods. During generation of the static pages by Pelican, you could integrate tools fetch webmentions and include it in the static pages. This method is more complex. Check out the theme by drivet and a gist by stuartlangridge.

Another easier method is to use a JavaScript and run and display it in client side. But the client side a bit more workload or network connections. For my blog, I use webmention.js by Fluffy. Just a few steps to add in your Pelican template.

Sending Webmentions

Since I am using, I could also use it to send webmentions too. Add a form inside your template. For my template, it is something like below. There some styling because I use Bootstrap.

 1 2 3 4 5 6 7 8 9
<section> <form action="{{ WEBMENTION_USERNAME }}/webmention" method="post" class="form-webmention"> <span class="mt-2"> <label for="form-webmention-url">Input the URL of your article that had mentioned my article:</label><br> </span> <div class="mt-2 d-flex row"> <input id="form-webmention-url" class="col-md-8 col-md-auto ml-1 mr-auto flex flex-row" type="url" value="" name="source" placeholder="" required/> <input name="target" value="{{ SITEURL }}/{{ article.url }}" type="hidden"/> <button type="submit" class="btn btn-nero col-md-3 col-md-auto ml-auto mr-1 flex flex-row">Send Webmention</button> </div> </form>

You could test it at the end of my articles.

We had discussed about how to enable Webmention in Pelican. Happy mentioning. Til next time.

Further reading

# Posted by Kappa on Wednesday, July 15th, 2020 at 9:48am

1 Share

# Shared by keith•j•grant on Saturday, October 14th, 2017 at 1:54pm


# Liked by Ryan Barrett on Tuesday, October 21st, 2014 at 3:47pm

# Wednesday, October 22nd, 2014 at 5:16pm

# Liked by Tantek Çelik on Thursday, October 23rd, 2014 at 12:01am

# Liked by Antoine Renault on Saturday, October 14th, 2017 at 12:52pm

# Liked by Fred Simmons on Saturday, October 14th, 2017 at 12:52pm

# Liked by James Starkie on Saturday, October 14th, 2017 at 12:53pm

# Liked by Jan Skovgaard on Saturday, October 14th, 2017 at 12:53pm

# Liked by TJ Nicolaides on Saturday, October 14th, 2017 at 12:53pm

# Liked by Charis in 🇵🇹 on Saturday, October 14th, 2017 at 1:25pm

# Liked by Rik Schennink on Saturday, October 14th, 2017 at 1:26pm

# Liked by Ethan MarGHOSTScotte on Saturday, October 14th, 2017 at 1:26pm

# Liked by Marc Drummond on Saturday, October 14th, 2017 at 1:26pm

# Liked by Marcos Peebles on Saturday, October 14th, 2017 at 1:26pm

# Liked by Benjamin Read on Saturday, October 14th, 2017 at 1:26pm

# Liked by John F Croston III on Saturday, October 14th, 2017 at 1:57pm

# Liked by ✌️ ERI•MA on Saturday, October 14th, 2017 at 3:13pm

# Liked by Heather on Saturday, October 14th, 2017 at 4:15pm

# Liked by JohnQ on Saturday, October 14th, 2017 at 5:10pm

# Liked by Luke Minall on Saturday, October 14th, 2017 at 5:10pm

# Liked by Andrew Woods on Saturday, October 14th, 2017 at 6:17pm

# Liked by vollepeer on Saturday, October 14th, 2017 at 8:48pm

# Liked by DeShawn Williams on Sunday, October 15th, 2017 at 12:44am

# Liked by Jason Murray on Sunday, October 15th, 2017 at 1:16am

# Liked by Brian Muenzenmeyer on Sunday, October 15th, 2017 at 4:16am

# Liked by Daᴎilo Vәga on Sunday, October 15th, 2017 at 10:45am

# Liked by Segi Henggana on Sunday, October 15th, 2017 at 10:26pm

# Liked by ge ricci on Saturday, January 6th, 2018 at 10:19am

# Liked by Chris Taylor on Saturday, January 6th, 2018 at 10:55am

# Liked by Jeff Jones on Saturday, January 6th, 2018 at 12:02pm

# Liked by Derek Featherstone on Saturday, January 6th, 2018 at 12:03pm

# Liked by Alex Carpenter on Saturday, January 6th, 2018 at 1:06pm

# Liked by David N Atkinson on Saturday, January 6th, 2018 at 3:36pm

# Liked by Paul Howie on Saturday, January 6th, 2018 at 7:27pm

# Liked by John F Croston III on Sunday, January 7th, 2018 at 2:32am

# Liked by Tomas Jakl on Sunday, April 5th, 2020 at 5:33pm

# Liked by marxwood on Tuesday, November 23rd, 2021 at 11:47am

# Liked by Taurean Bryant on Tuesday, May 31st, 2022 at 2:01pm

# Liked by Ryan Barrett on Tuesday, May 31st, 2022 at 2:01pm

# Liked by Ryan Barrett on Friday, July 14th, 2023 at 8:20pm

Previously on this day

10 years ago I wrote Classy values

Semantics and such.

11 years ago I wrote Visionaries

A memory of Austin prompted by a readlist of seminal technology papers.

12 years ago I wrote Candygram

A child’s Halloween in Ireland.

15 years ago I wrote Typorn

Geeking out on the printing press.

16 years ago I wrote Closed open data

When is an hCard not an hCard?

21 years ago I wrote Stop the Patent Process Madness

Wired has published an excellent article by Lauren Weinstein on the ludicrous state of Intellectual Property patents:

21 years ago I wrote The Morning News - The Opposite of Sex and the City

What if Sex and the City had been written by Beckett?…

21 years ago I wrote Guess the Dictator or Sit-Com Character

This is a scarily accurate online version of twenty questions. It didn’t take long for it to guess that I was thinking of Alex P. Keaton from Family Ties.

22 years ago I wrote Multiculturalism vs. feminism

The situation in Afghanistan has highlighted something of a dilemma for the liberal left - a group I would usually consider myself a part of.