Debugging and fixing the conflict I had between Let’s Encrypt certificates and the ActivityPub plugin for WordPress (for joining the Fediverse!)

Photo of a person of color's hands on the keyboard of an Apple laptop, with lines of green code displayed on the screen, and a gadget on the table next to it.

I’ve been joining and rejoining bits of my Internet footprint to the Fediverse. I think a couple of years ago I started a mastodon account and dropped off because my corner of it was dead and I didn’t really understand how self-serve it was. More recently, after starting to record myself streaming video games (in an attempt to get comfortable with it for, possibly, my leukemia project, where my goal is to do education for other people who might go through it and want to know the hard things before finding out for themselves), I created my own instance of PeerTube on an idle Raspberry Pi, and joined it to the Fediverse.

As you know, I’ve been using WordPress to blog for a long time, and I was thrilled to learn that it might be relatively simple to integrate my self-hosted WordPress site into the Fediverse. By the simple installation of Matthias Pfefferle’s ActivityPub plugin. So I tried it. And it did not work. (Sad trombone noise.)

But as you may also know, I do try to figure things out and not simply give up. So I figured out, at least for me, how to fix it.

(Note for this writeup I’m trying to provide tech info in the ALT text for images, but not be too spammy. Feel free to email me if you want/need specific text.)

The thing to know about ActivityPub is that it’s lightweight, works with the Webfinger protocol, and basically provides a pointer to where ActivityPub does its federation business. So in order to debug you need to know how to look at the problem as the machines would. Before I implemented my fix, Webfinger was getting an HTTP 404: Bad Request.

Screenshot of WebFinger results queying malcolm@geekblog.malcolmgin.com and returning "22:06:01 Error getting JRD: 400 Bad Request", and a JSON Resource Descriptor (JRD) of "null" value.
Send a request, get a 404 and a null object.

I knew this was a problem. One of my rules in troubleshooting is to address issues as I find them, or at least keep a list. (Spoiler: This basically turned out to be the ONLY problem. I just needed to know precisely what was broken and how to fix it.)

Armed with this info I did a couple of good Google Searches (this is a good time to plug the Advanced Search or Power Search courses – if you doubt you could find something on modern search engines on the Internet, or at least give it a good go, consider spending time on these self-paced technical courses that are generally free) and eventually hit good paydirt – a WordPress.org support discussion on this kind of issue).

Here’s the discussion I found. It talks about the way that .htaccess was messing with others’ ActivityPub installs and discusses in general how to fix it.

Now’s the time to admit that I am not strong with .htaccess directives. I understand them to be a bit like Firewall rules – you set rules about access and forbidding access. Things are processed in order, and directives are line by line. Until this morning, I wasn’t sure exactly how to parse RewriteCond and Rewrite directives, nor what [L] or [F] meant in relation to the .htaccess I was seeing. I was sensible enough to see that there was a comment and that that comment was placing the blame on Let’s Encrypt for restricting my blog’s /.well-known/ directory and forbidding ActivityPub requests. Fortunately, the Internet is full of documentation, so I had the chance to, at least for the moment, relearn what I needed to get to the next step.

Here’s the old .htaccess file before I fixed it:

# Permit access to the challenge files but nothing else
Order allow,deny
Allow from all

RewriteCond %{REQUEST_URI} ^/[.]well-known/acme-challenge/[a-zA-Z0-9_-]+$
RewriteRule .* - [L]

RewriteRule .* - [F]

Note that Let’s Encrypt isn’t mentioned in the # comment at the top, but if you Google acme-challenge, you can see that that’s the organization that uses that URL.

It was now time to apply what I learned from the WordPress.org  support discussion, and what I learned (re-learned) about .htaccess directives, and use a freely available web tool or two to try this fix.

First, I found an online .htaccess parser. This tool helps us understand how .htaccess directives work with incoming Web requests and what results we’ll get. This is an important step in testing your fix before deploying it to your production server. I noodled a bit and settled on the parser at madewithlove.com. Here’s the URL: https://htaccess.madewithlove.com/

I also took the API URL provided by the plugin author on the WordPress.org post above and repurposed it to work with my blog (to make sure that end was working on my blog: https://geekblog.malcolmgin.com/wp-json/activitypub/1.0/webfinger?resource=acct:malcolm@geekblog.malcolmgin.com

It is. And in order to come up with the right modification to the .htaccess file, the salient part of the URL is: /wp-json/activitypub/1.0/webfinger

So my goal for this fix is to come up with the right .htaccess directive to allow traffic to come into the WebFinger URL and get redirected (with QueryString – the bit after the ? – intact).

Here, I read up on relevant .htaccess directives, made some guesses, did some iterative testing, and came up with the following modification (adding two lines – in red, below) to my busted .htacess file:

# Permit access to the challenge files but nothing else
Order allow,deny
Allow from all

RewriteCond %{REQUEST_URI} ^/[.]well-known/webfinger.*$
RewriteRule .* /wp-json/activitypub/1.0/webfinger [L]

RewriteCond %{REQUEST_URI} ^/[.]well-known/acme-challenge/[a-zA-Z0-9_-]+$
RewriteRule .* - [L]

RewriteRule .* - [F]

In order to test this in the parser, I input the following:

  • URL applying the rules to: https://geekblog.malcolmgin.com/.well-known/webfinger?resource=acct%3Amalcolm%40geekblog.malcolmgin.com
  • .htaccess rules (as above)
  • Click the Test button

Results:

  • Output:
    • Output URL: https://geekblog.malcolmgin.com/wp-json/activitypub/1.0/webfinger?resource=acct%3Amalcolm%40geekblog.malcolmgin.com
    • And the debugging info reads correct (here’s the shareable link).

After parsing and testing, I felt confident enough to actually implement this in my .htaccess file for the .well-known subdirectory.

Immediately, WebFinger looked like this:

WebFinger showing a proper response to the user query, including a well populated JRD, and no error code.
Send a request, and get a good response!

I was then able to search for @malcolm on Mastodon and follow the account, and this post is a test to see if I can see the post on Mastodon now that I’m following myself.

(Photo by Sora Shimazaki: https://www.pexels.com/photo/crop-cyber-spy-hacking-system-while-typing-on-laptop-5935794/)