IndieWeb adventure: First steps
Introduction
Earlier this year I decided that this blog should receive an update in 2024. It hasn’t changed since I moved to hugo in 2021. One of the changed I had in mind for some time was about “joining the indieweb”. I’ve found the concept interesting but never made the effort to actually look into the details of what it would mean.
I had a wrong idea of it being mainly use for webmentions and thus having more comments and likes. And that it was not possible with a static site like mine (not entirely wrong though). Well, there is a bit of that, but that’s not all this is! So following my idea of improving this blog in 2024, I started exploring this rabbit hole… Thanks to Sara Jakša and Ben Werdmuller posts that convince me going that road!
It shouldn’t be a surprise for my gemini reader, as I’ve tried to standardize gemini mention, a simplified / gemini-zed approach of webmentions.
This post will talk about the indieweb and the different steps to get to different “level” of your indieweb usage (I defined my own levels, as opposed to the community validated ones). I will also cover the implementation steps to get to them (not going in the details of the site generator though).
Nota: I’m no expert at all so I might be wrong… Please feel free to send me a webmention (or see my contact page) to let me know. Please also note that I’m not displaying webmentions or any interaction on this site (yet? read bellow for the reasons why) so don’t hammer refresh to see them :).
Nota2: While this blog use hugo as a static site generator, this post doesn’t go into any details about hugo config/changes but stay at the concept layer.
Nota3: Noticing today that Google either has issue with youtube RSS feeds or that they are dropping support for RSS feeds is comforting my choice of owning my own content while leveraging standards to keep interoperability alive!
IndieWeb you say?
I’m sure most readers will have at least heard this term once or twice, but for the sake of it:
The IndieWeb is a people-focused alternative to the “corporate web”.
And also:
We should all own the content we’re creating, rather than just posting to third-party content silos. Publish on your own domain, and syndicate out to silos. This is the basis of the “Indie Web” movement.
Well, that’s a start but what does it really mean? It means it focus on owning your own place on the web while still being able to stay connected with others, thanks to the usage of standards. In other words, you own a domain and a web server, and that became your “public home” on the internet. Then, if you follow some standards, it becomes possible to interact with others. You respond to other people article by writing on your own site/blog, you can respond to invitation, share bookmarks or even authenticate to some sites… It can become complicated, so I won’t go more in details and will let you browse the indieweb to discover more.
According to indiewebify.me, there are 3 levels of “indieweb citizenship”:
- Level 1 - Become a citizen: own a domain and setup Web Sign In
- Level 2 - Publish on the IndieWeb: Add markup to your html page and ability to send webmentions to others
- Level 3 - Federated conversations: Leverage replies and be able to receive webmentions
Based on indiewebify.me, I’m somewhere in Level 3, as this blog can send (via telegraph) and receive (via webmention.io) webmentions. But I’m not using other kind of post than the basic one. I’m not (yet?) using replies, RSVP, or others. Check the “goals” section for more info.
I don’t really like these 3 levels, I would prefer:
- Level 0 - Become a
citizenof the (indie)web: own a domain and post content there (no standards / markups, anyone owning a site / blog is there) - Level 1 - Become a
registered citizen: Implement h-card standard and indieAuth authentication (withrel="me"links in header) - Level 2 - Become an
author citizen: Standardize content markup: Implement h-entry for your posts and potentially h-feed. Could also be replies, rsvp, bookmarks, or any standard indieweb format you choose. Here the point is: any new content on your site should follow the right indieweb format - Level 3 - Become a
connected citizen: Be able to send and receive webmentions and other interactions. Notes that displaying said webmentions/interactions is fully optional, the point is I can send reactions and read others, that’s all - Level 4 - Become a
commited citizen: Everything is either published first on your own website and then elsewhere, or at least fully copied and available on your site.
Level names are the not the best, but that’s all I could think of apparently^^. Not necessarily very different, but for me the main point is that it focus on having your place to publish content in a standardize format to motivate exchanges. Then only can we think about interactions at all, if we want it. At this stage this blog is level 3, even though I’m limited to articles and no other type of post.
On the indieweb wiki, there is another classification called “IndieMark”, way more complex with a long checklist of thing you could/should do with on the indieweb. I’m not using this scale because of complexity (and unclear level 5/6).
Goals
Why?
Multiple reasons, in no particular order:
- Owning my own content. Shouldn’t be a surprise for any reader of this blog… “Committing” to the indieweb is just another way of continuing that road. Even though I’m not fully POSSE (Publish (on your) Own Site, Syndicate Elsewhere) compliant (see below why), it is a good start.
- It almost feels like a bridge between smolweb and social web. Keeping the blogging era alive while allowing social interaction (with the benefit and drawbacks it brings, like likes/reposts or similar)
- Centered around author content
- For the “fun” of doing it, also known as “why not”… (#geekeries)
Jamie Tanna post is quite interesting about the what / why of the indieweb.
No other post types?
There are different type of post with specific markups that can be used in the indieweb:
Replies: I don’t really reply to people when I blog, I usually write more for myself. I don’t intend at this stage to use this blog as a conversational channel, but I’m not opposed to it if I see a real interest in the future. For now, just linking to other people blog entry and alerting with webmention is enough.Bookmarks: This one I’m thinking about. As of now, I managed my bookmarks with linkding, which provide a public page of my publically shared bookmarks. I’m thinking that maybe I can create a small script extracting bookmarks from linkding API and generate a page on this blog with the right markups… Probably something to do later this year. In any case, the “truth” will stay in linkding at this stage.Notes: An interesting one… For more unstructured content that could be thought as toots / tweet / (short?) message. I prefer to keep my social media separated from my blog, so I don’t intend to change that. Nothing posted on my Fediverse account is that important that it should be saved here (IMHO). And I’m running my own Fediverse instance, so I’m still owning that content.RSVPs: I don’t participate in any event where I could RSVP like this, so I don’t see the point. But maybe some day?Likes/Reposts: I don’t care for these that are usually more harmful than beneficial.
Why don’t I show webmentions?
I’ve never liked the idea of comments that are usually irrelevant or not useful for the article. I don’t intend to use webmentions as comment and way of displaying likes / reposts or bookmarks. I don’t even want to know to be honest. What I want to know is if someone wrote a response or related article that I may want to read. If I think it should be linked on my blog, I’ll edit the content myself to add it. If it calls for another public response, I’ll write another post linking that response. Or if it leads to a 1:1 conversation, I would move it to my prefer channel: email.
Other reason, I didn’t want to add javascript to this site, which would have been the easiest to retrieve webmentions from webmention.io and display them here. Lastly, I want to own what is displayed here, I don’t want “spam webmention” just for people to link their page here. And I don’t want to spend time moderating stuff.
So don’t expect to see your reaction here, sorry :).
How will I know about other webmentions? Well, webmention.io provides a nice private atom feed, so I added to my miniflux so I’ll see them there :).
No bridge?
I’m not interesting in bridging with the fediverse for the same reasons I don’t display webmentions, so read above. Also, I’m not on TwitX, instagram or other private silos, so classical bridge didn’t interest me.
Updating this blog for the indieweb
Going through my own levels definition, I was already at level 0, so let’s implement the next one.
Achieving Level 1
To implement level 1 (“Registered Citizen”), all I needed was to implement an h-card and some rel="me" links… I already changed a bit the homepage earlier this year with an introduction of myself (see screenshot below), so I all needed was to add the right markup to it.

Figure 1: Screenshot of my bio displayed on the homage of this blog
Implementation
The html looks like this now:
<div class="h-card p-author" style="text-align:center;">
<img class="u-photo avatar" src="{{ .Site.Author.avatar }}" alt="bacardi55's avatar" />
<p>
I'm <a href="{{ .Site.BaseURL }}" rel="me" class="p-nickname p-name u-url">bacardi55</a>, welcome to my blog where I talk about stuff I don't really know :).
<br/>
<span class="p-note">{{ .Site.Author.bio }}</span><br />
You can find all my public links on my <a href="https://about.bacardi55.io" target="_blank">about page</a> or you can <a href="/pages/contact-me/">contact me</a>.
<br />
<br />
<hr/>
</p>
</div>
The important part are the classes in the markup:
h-card: Indicates that this div contains h-card infop-author: The author nameu-photo: The author photo- the
rel="me"in the <a> tag: Link to author main page (so this blog) p-note: a summary of the author
There are many other possible markup class to use, read the h-card documentation.
I then moved this html to a hugo partial (a reusable partial template), so I could also include it in any article page in h-entry easily (see level 2).
The second part was adding rel="me" links. You could either do it with visible links (<a> tags) or via <link> tags in the header. I choose the later option and added:
<link rel="me" href="https://framapiaf.org/@bacardi55" />
<link rel="me" href="https://sr.ht/~bacardi55" />
<link rel="me" href="https://github.com/bacardi55" />
<link rel="me" href="https://gitlab.com/bacardi55" />
<link rel="me" href="mailto:bac@rdi55.pl" />
<link rel="pgpkey" href="/files/pubkey.txt">
My GoToSocial link is not there because GtS doesn’t support rel="me" links yet, but there is a github issue for this. When this is implemented, I’ll add it.
Validating your markup
To test the validity of your markup, one can use IndieWebifyMe. To test that your identify is working as expected, go to the Validate Rel Me page and enter your homepage URL and see the results. In my case:

Figure 2: Screenshot of IndieWebifyMe validating my homepage
Authentication validation
You could either try to connect to the indieweb wiki or other sites working with indieAuth. In my case, I connected to webmentions.io and telegraph as I’m going to use these services for level 3.
-
Authentication via GPG key
I decided to authenticate via my GPG key (via the
<link rel="pgpkey" href="/files/pubkey.txt">html in the <head> section). To sign the text provided to confirm authentication, I used the following command:gpg --clearsign --armor indieauth“indieauth” being the name of the text file where I pasted the text to sign. Then, paste the content of the
indieauth.ascfile into the textarea and confirm. You should be authenticated :).
Achieving Level 2
Level 2 is about displaying content in a standard format. As said before, there are many format type for posts, responses, bookmarks, RSVP, etc, but at this stage I’m keeping it simple with only blog posts being marked as h-entry. Maybe later on I’ll want to implement some others (like replies), but not at this stage. If I do, I’ll try to document it as well in later posts.
Implementation
-
Article / Blog post Entry: h-entry
A stripped version of an article now like this:
<div class="article-container h-entry"> <section class="article header"> <h1 class="article title p-name">Sharing public links from linkding to gotosocial with Feed2fedi and RSS Bridge</h1> <a href="https://bacardi55.io/2024/01/11/sharing-public-links-from-linkding-to-gotosocial-with-feed2fedi-and-rss-bridge/" class="u-url u-uid" hidden="">Permalink</a> <p class="article date"><span> <time class="dt-published" datetime="2024-01-11">Thursday, January 11, 2024</time> </span></p> </section> <section class="article labels"> <a class="tag p-category" href="/tags/fediverse/">fediverse</a> <a class="tag p-category" href="/tags/gotosocial/">gotosocial</a> […] </section> <article class="article markdown-body e-content"> […] </article> […] <div hidden=""> [INCLUDE h-card.html TEMPLATE (see level 1)] </div> </div>The important part are:
h-entry: Starting to declare an entry, all other markups are inside the h-entryp-name: The entry titleu-urlandu-uid: The url and uuid of the entry (both are the permalink url)dt-published: The publishing date. Using<time>allow to have the date in the right microformat (YYYY-MM-DD) in"datetime"and still display it the way you like for visitors.p-category: The different category, can have multiplee-content: The actual content of the entry- Included
h-card: You don’t have to, you could just use some of the markup but I find it best to be consistant and provide a full h-card within each h-entry for clarity.
Was a “simple” as editing the right template files :).
-
Generating a feed with h-feed
H-feed are the indieweb equivalent of RSS/Atom feeds, but generated from the html markups like the rest of it. It means that the blog posts list page was a good start… This page displays neither the content or a summary of it and I didn’t want to change that, so the h-feed generated is just a list of title/link/authors, but doesn’t have the content within it. That is because I didn’t want to add a summary (and even less the full content) to keep this page as lean as possible. I could have added as hidden, but didn’t even want the html to be heavier just for that.
If people starts to actually following this blog via the h-feed, I may consider creating a dedicated page that will contain the full content of the last X articles, but I’m not there yet.
The overall html looks like this:
<ul class="note list h-feed"> <div class="indieweb" hidden=""> [INCLUDE h-card.html TEMPLATE (see level 1)] </div> <li class="item h-entry"> <a class="note u-url" href="/2024/01/11/sharing-public-links-from-linkding-to-gotosocial-with-feed2fedi-and-rss-bridge/"> <p class="note title p-name"> Sharing public links from linkding to gotosocial with Feed2fedi and RSS Bridge </p> <p class="note date"> <time class="dt-published" datetime="2024-01-11">Thursday, January 11, 2024</time> </p> </a> </li> <li class="item h-entry"> … </li> […] </ul>Important part are:
h-feed: Declaring an h-feedh-entry: One h-entry per article/post. Same as for the article page itself, with the same markup inside the h-entry: u-url, p-name and dt-publishedh-card: Full author h-card
Validation
-
Article validation
To validate your entry markup, simply try it on the IndieWebifyMe validate-h-entry service. Example:

Figure 3: Screenshot of IndieWebifyMe validating my entry, part 1

Figure 4: Screenshot of IndieWebifyMe validating my entry, part 2
-
Feed validation
IndieWebifyMe doesn’t provide a test for h-feed, so instead I used granary.io, a service that transforms an h-feed into a RSS feed… Yes, that seems weird, but didn’t want to install a h-feed reader just for that and there was no validator… So instead, I’m checking that an RSS feed can be generated out of my h-feed page. As it works, I’m guessing my h-feed is well formated (even though there is no content, just title, author, date, as explained above).
You can see the generated link here.
Unlocking Level 3 with 3rd party services
While everything could be done with selfhosted application, I decided to first try to 3rd party services webmentions.io and telegraph. The main reason being that I don’t know how much I’ll be invested in this indieweb thing, so I want to start with as little work as possible. Editing the markup format and subscribing to services was enough for now. Maybe in some months / year I’ll decide that selfhosting a webmentions.io equivalent would be worth it, let’s see. Telegraph should be easy to replace with a simple bash script and curl commands.
Sending webmentions
Sending webmentions is as easy as scanning all links in your page, loading these links and checking if a rel=“webmention” exist on the page, and if so sending it this link. For now, as I’m just starting and didn’t want to invest too much time, I’m using telegraph for this. Once you have logged in via IndieAuth, you can provide your article page and it will send webmentions to all pages that accept webmentions. Or you can define the recipient manually if you don’t want to send to all.
Recieving webmentions
As for sending webmentions, I went the easy road at this stage by using webmention.io that manage recieving webmention. It also provides an API or a private feed for reading webmentions. I could have used the javascript library to easily display webmentions here but decided not to do so and just added the private feed to my RSS reader so I can recieve and read those webmentions without them being displayed here.
Bonus: Publishing to indienews
Indienews is a “News aggregator for IndieWeb-related posts”. Old people like me can think about it as a “planet” type of site, based on the indieweb standard instead of RSS/Atom feeds, where people posts article about indieweb related stuff. This entry will most probably be posted there to.
To post a link there, first you need to write the article, which needs to contain (within the h-entry) either:
<a href="https://news.indieweb.org/en" class="u-syndication">
Also posted on IndieNews
</a>
Or:
<a href="https://news.indieweb.org/en" class="u-category">#indienews</a>
As I’m not intending to post all my article there (wouldn’t make sense), I needed a way to automatically add this. This is more specific to hugo, but more SSG user will understand.
In the frontmatter, I added a custom parameter:
indieweb: 1
Then, in my single.html template, I added:
{{- if .Params.Indienews -}} |~
{{- partial "article-indienews.html" -}}
{{- end -}}
In order to include the additional template file only if the indieweb frontmatter entry is set. Otherwise it won’t add it.
The partial template article-indienews.html simply contains:
<div class="indienews">
<br />
<a href="https://news.indieweb.org/en" class="u-syndication">
Also posted on IndieNews
</a>
<br /><br />
</div>
Conclusion
These are my first steps into the indieweb world. I find it quite interesting, even though I don’t see it massively used yet. But I guess it just needs more people like me making the effort to join this small part of the web, trying to make the web more open :).
We’ll see where this goes, it might at the end may never be really used, but now I can’t say I didn’t at least try!