published on 16 Oct 2025, edited on 17 Oct 2025

Comment systems on static sites have always been tricky. For previous iterations of my blog I have used several third-party comment plugins, I don’t even remember which ones, but something similar to Disqus. They worked, but either had a limited free tier with clunky code or were slick but short-lived - I think all third-party comment systems I have used have been long dead, of course along with my comments.

I accidentally learned that Losses built his own comment system on a static site with Cloudflare Worker and somehow believed that this should be a feasible task for a non-programmer like me with the help of LLM, then decided to spend the rest of my day on this. Since I am going to build my own, I had some more thoughts in addition to self-hosting - Fediverse.

I like the idea of Fediverse, I mainly use Fediverse to post my own stuff, and I saw some examples of federated blogs. Integrating Fediverse replies as blog comments seems a really good idea. However I still want to keep the ability of anonymous, or unregistered reply - probably the biggest problem of Fediverse for general users is still registering an account and even figuring out what an “instance” means, and that includes many of my friends. Although I like Fediverse, I also don’t want to exclude many potential commenters because they don’t know how or don’t want to bother registering a Fediverse account. Also most implementations I could find need to direct readers to Fediverse posts outside the blog to comment - I just still want people to be able to comment right under the post.

So the mechanism I would like to achieve is pretty clear: A hybrid comment system that has both self-hosted comments that everyone could comment on, and integrated Fediverse comments. I built the self-host part based on Antoine’s code, storing comments in Cloudflare’s KV storage. For the Fediverse part, I referred to Carl Schwan’s and Jeff Kaufman’s implementations. Since implementing Fediverse login inside the blog would bring unnecessary complexity, the actual Fediverse comment would happen outside the blog, where I just provide a link to the Fediverse post.

Eventually the system looks like this:

  1. a “fedipost” attribute is added to my Hugo posts’ header, pointing to the Fediverse post where the comment of a blog post should be pulled from;
  2. the public comments from the Fediverse post would be pulled on page load;
  3. when “fedipost” url is set, the respective blog post page will provide a link to the post for readers;
  4. unregistered comments are collected via on-site input boxes and stored in the Cloudflare KV, pulled on page load;
  5. in the frontend comments from both sources are mixed and simply ordered by time.

The process is pretty straightforward, though strictly developed for my own needs, it should work for other static sites as well. And it does have some shortcomings. The “fedipost” url needs to be manually assigned for every post, which means an article needs to be published a second time to enable the Fedivers comments. Since Fediverse posts need to be published after the blog post, and the Fediverse url can only be retrieved after the Fediverse post is published, there seems no better way. There are some practices of federating the blog itself, which may be a solution to this, but I don’t want to overengineer this and I prefer to post on Fediverse using my main account instead of having a separate handle for the blog. Also I would have to manually moderate comments - if it is ever needed at all - from the KV database.

Above is what the hybrid comments would look like. I am pretty happy with it. If you’re interested in implementing something similar, here are my implementation in three parts (I should warn that it’s hacky and tailored specifically to my Hugo setup, and full of code written by LLM): 1. Cloduflare Worker code for storing on-site comments; 2. Javascript code for retriving and displaying comments from both sources; 3. and to pass the “fedipost” attribute to the Javascript:

1window.fedipostUrl = {{ if .Params.fedipost }}'{{ .Params.fedipost }}'{{ else }}null{{ end }};