What's with ActivityPub + WordPress + Cloudflare not working? 1

I’ve been on a journey to get WPwatercooler.com to be on the fediverse and it hasn’t been as easy as I was thinking it was going to be. Folks that run a website and want to use Mastodon to build community around it have a few options for being presented on the Fediverse. This post isn’t going to help you solve this yet but I hope we can work out what needs to be done in order to do so.

  1. Make an account on an existing Mastodon instance, share your links to your content there
  2. Build your own Mastodon instance and make an account there and share your content
  3. Use ActivityPub and allow people to follow your website on any Fediverse property

There is most likely more but for the most part its those 3 that you can select from.

I started out with WPwatercooler on Mastodon.online which can be found at https://mastodon.online/@wpwatercooler this works fine and people can follow it but I wanted to see if I could take it a step further and add WPwatercooler.com to the Fediverse directly and this is where ActivityPub comes in.

I installed these three plugins so that I could offer up ActivityPub via WebFinger and people on Mastodon will be able to find the website on there. I created 2 accounts on WPwatercooler.com one for each show wpwatercooler and devbranch and then I did a search for @wpwatercooler and @devbranch on my Mastodon instance and was able to find them.

The Problem

The thing I and other folks in our community ran into was that we could follow the accounts and if we tried a bunch sometimes it should register. Seems to be a caching issues I think. Let’s check and see.

WebFinger

In part WebFinger is a protocol that allows for discovery of information about people and things identified by a URI. Information about a person might be discovered via an acct: URI, for example, which is a URI that looks like an email address.

Both of these accounts can be found by going to https://wpwatercooler.com/author/devbranch/ and https://wpwatercooler.com/author/wpwatercooler/ as you can see both of these are authors on the WPwatercooler website like I spoke about before. When someone tries and searches for @wpwatercooler on Mastodon it accesses https://wpwatercooler.com/.well-known/webfinger?resource=acct:[email protected] and as you can see from the url it looks at the domain name in the .well-known/webfinder and looks for the resource with the account name of [email protected]

Webfinger offers up a json output that looks like the following:

{
“subject”: “acct:[email protected]”,
“aliases”: [
“acct:[email protected]”,
“https://wpwatercooler.com/author/wpwatercooler/”,
“mailto:[email protected]”
],
“links”: [
{
“rel”: “http://webfinger.net/rel/profile-page”,
“href”: “https://wpwatercooler.com/author/wpwatercooler/”,
“type”: “text/html”
},
{
“rel”: “http://webfinger.net/rel/avatar”,
“href”: “https://wpwatercooler.com/wp-content/uploads/2015/06/cropped-WPwatercooler-Avatar-2023-2-96x96.png”
},
{
“rel”: “http://webfinger.net/rel/profile-page”,
“href”: “https://www.wpwatercooler.com”,
“type”: “text/html”
},
{
“rel”: “self”,
“type”: “application/activity+json”,
“href”: “https://wpwatercooler.com/author/wpwatercooler/”
},
{
“rel”: “http://nodeinfo.diaspora.software/ns/schema/2.0”,
“href”: “https://wpwatercooler.com/wp-json/nodeinfo/2.0”
},
{
“rel”: “http://nodeinfo.diaspora.software/ns/schema/1.1”,
“href”: “https://wpwatercooler.com/wp-json/nodeinfo/1.1”
},
{
“rel”: “http://nodeinfo.diaspora.software/ns/schema/1.0”,
“href”: “https://wpwatercooler.com/wp-json/nodeinfo/1.0”
},
{
“rel”: “https://feneas.org/ns/serviceinfo”,
“type”: “application/ld+json”,
“href”: “https://wpwatercooler.com/wp-json/serviceinfo/1.0”,
“properties”: {
“https://feneas.org/ns/serviceinfo#software.name”: “WPwatercooler”
}
},
{
“rel”: “http://schemas.google.com/g/2010#updates-from”,
“href”: “https://wpwatercooler.com/author/wpwatercooler/feed/ostatus/”,
“type”: “application/atom+xml”
},
{
“rel”: “http://ostatus.org/schema/1.0/subscribe”,
“template”: “https://wpwatercooler.com/?profile={uri}”
},
{
“rel”: “feed”,
“type”: “application/stream+json”,
“title”: “Activity-Streams 1.0 Feed”,
“href”: “https://wpwatercooler.com/author/wpwatercooler/feed/as1/”
},
{
“rel”: “feed”,
“type”: “application/activity+json”,
“title”: “Activity-Streams 2.0 Feed”,
“href”: “https://wpwatercooler.com/author/wpwatercooler/feed/as2/”
}
]
}

the section we’re interested in is

{
“rel”: “self”,
“type”: “application/activity+json”,
“href”: “https://wpwatercooler.com/author/wpwatercooler/”
},

This section has the author url and you can see that its using the type of application/activity+json instead of something like text/html so when it queries that url it passes along in the header Accept: application/activity+json and it should return it in that format.

Running the following command

$ url -i -H “Accept: application/activity+json” “https://wpwatercooler.com/author/wpwatercooler”

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 —:—:— —:—:— —:—:—     0HTTP/2 200
date: Tue, 17 Jan 2023 17:42:34 GMT
content-type: text/html; charset=UTF-8
last-modified: Tue, 17 Jan 2023 16:11:53 GMT
cache-control: max-age=0
expires: Tue, 17 Jan 2023 16:40:07 GMT
vary: Accept-Encoding
age: 3746
x-cache: HIT
cf-cache-status: DYNAMIC
server-timing: cf-q-config;dur=9.0000003183377e-06
report-to: {“endpoints”:[{“url”:“https:\/\/a.nel.cloudflare.com\/report\/v3?s=hTOA5wCRdqy4Lkoey48CmI2UIbYym8HWAvtsssC4lRsXgli7%2BchgWImTY3ol%2FdS8gTcodIYZQ6cbSDMVZ1co5brwCJxEpkIdqJ%2FCEjwKcxOWhomHgfWISN9qzmkfkMd7tNo8fQORQ%3D%3D”}],“group”:“cf-nel”,“max_age”:604800}
nel: {“success_fraction”:0,“report_to”:“cf-nel”,“max_age”:604800}
server: cloudflare
cf-ray: 78b0e3631e862ea3-LAX
alt-svc: h3=“:443”; ma=86400, h3-29=“:443”; ma=86400

Cloudflare returns back text/html and outputs the author page and not the json we wanted. Talking with a few folks on Mastodon we found that people are having this issue with using Cloudflare and there isn’t anything we can do to resolve it sadly. I tried excluding the caching for the specific paths /author/(.*) and other such tricks and it keeps changing the content-type to text/html and not application/activity+json as requested.

Looking online I’ve found a bunch of people using Cloudflare + ActivityPub running into this issue and other technologies that also use Accept: application/activity+json and finding that it only returns text/html. Bummer.

The code that makes all that work can be found in the WordPress ActivityPub plugin class-activity-pub.php

/**
	 * Return a AS2 JSON version of an author, post or page.
	 *
	 * @param  string $template The path to the template object.
	 *
	 * @return string The new path to the JSON template.
	 */
	public static function render_json_template( $template ) {
		if ( ! \is_author() && ! \is_singular() && ! \is_home() ) {
			return $template;
		}

		// check if user can publish posts
		if ( \is_author() && ! user_can( \get_the_author_meta( 'ID' ), 'publish_posts' ) ) {
			return $template;
		}

		if ( \is_author() ) {
			$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/author-json.php';
		} elseif ( \is_singular() ) {
			$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/post-json.php';
		} elseif ( \is_home() ) {
			$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/blog-json.php';
		}

		global $wp_query;

		if ( isset( $wp_query->query_vars['activitypub'] ) ) {
			return $json_template;
		}

		if ( ! isset( $_SERVER['HTTP_ACCEPT'] ) ) {
			return $template;
		}

		$accept_header = $_SERVER['HTTP_ACCEPT'];

		if (
			\stristr( $accept_header, 'application/activity+json' ) ||
			\stristr( $accept_header, 'application/ld+json' )
		) {
			return $json_template;
		}

		// Accept header as an array.
		$accept = \explode( ',', \trim( $accept_header ) );

		if (
			\in_array( 'application/ld+json; profile=“https://www.w3.org/ns/activitystreams”', $accept, true ) ||
			\in_array( 'application/activity+json', $accept, true ) ||
			\in_array( 'application/ld+json', $accept, true ) ||
			\in_array( 'application/json', $accept, true )
		) {
			return $json_template;
		}

		return $template;
	}

Can this be done?

After discussing this with @tomfinley and @tim on mastodon in this tread https://simian.rodeo/@jasontucker/109701436056062907 we found that there is an endpoint that will return json https://wpwatercooler.com/author/wpwatercooler/activitypub

I’m wondering if instead we can force Webfinger to return that url with /activitypub at the end instead of expecting Cloudflare to return a non text/html content type. You can see this JSON output here https://wpwatercooler.com/author/wpwatercooler/activitypub/

“id”: “https://wpwatercooler.com/author/wpwatercooler/”,
“type”: “Person”,
“name”: “WPwatercooler Show”,
“summary”: “The world’s most influential WordPress podcast brought to you by Jason Tucker, Sé Reed & Jason Cosper”,
“preferredUsername”: “wpwatercooler”,
“url”: “https://wpwatercooler.com/author/wpwatercooler/”,
“icon”: {
“type”: “Image”,
“url”: “https://wpwatercooler.com/wp-content/uploads/2015/06/cropped-WPwatercooler-Avatar-2023-2-120x120.png”
},
“inbox”: “https://wpwatercooler.com/wp-json/activitypub/1.0/users/6260/inbox”,
“outbox”: “https://wpwatercooler.com/wp-json/activitypub/1.0/users/6260/outbox”,
“followers”: “https://wpwatercooler.com/wp-json/activitypub/1.0/users/6260/followers”,
“following”: “https://wpwatercooler.com/wp-json/activitypub/1.0/users/6260/following”,
“manuallyApprovesFollowers”: false,
“publicKey”: {
“id”: “https://wpwatercooler.com/author/wpwatercooler/#main-key”,
“owner”: “https://wpwatercooler.com/author/wpwatercooler/”,

What’s involved to make this work on Cloudflare? I’d love to hear in the comments and we can see about getting this combo of WordPress + CloudFlare + ActivityPub working, I’ve seen so many people complain about this not working with this very popular combo of technologies in the WordPress support forums.

Maybe related content

15 responses to “What’s with ActivityPub + WordPress + Cloudflare not working?”

  1. Let me know what you think @tim @tomfinley

  2. @jasontucker I am trying to read your post but it’s not loading for me, the page only half loads. Also what’s with the hashtags that are all hex codes? I’m so confused.

  3. @shauny wow, that’s really weird

  4. @shauny of course the first time I ever try to actually put code on my website, something funky like this happens

  5. @shauny OK do you mind giving me a refresh?

  6. @dustinrue well the real issue is that it’s not returning the correct content type

  7. @jasontucker sorry was out. It’s working now. The long URLs add horizontal scrolling on mobile though, just FYI. I tried adding my site to the Fediverse but found that it’s probably better to just link to posts from a separate account, and track replies etc using webfinger. Less hassle!

  8. @dustinrue yeah you can force the format by adding /activitypub to the end of the author url https://wpwatercooler.com/author/wpwatercooler/activitypub

  9. @dustinrue yeah @tim and @tomfinley were also working out what the deal is here, Tim is using lightspeed and was having weird issues, i think this is a combo of cloudways hosting and their caching along with cloudflare for me.

  10. @jasontuckerYou could try the WPMU Dev plugins (Defender + Hummingbird + Smush). I haven’t used their paid CDN/WAF yet, but their lite plugins are very impressive to me.

  11. @wpwatercooler I sent this to @boogah, I think this is my 2023 question #willitactivitypub
    willitactivitypub

  12. @jasontucker I’d love to go all in with my WordPress blog as well, but that doesn’t seem to be possible if hosting with WordPress.com. Why they haven’t added activitypub in any way over there is a mystery to me.

  13. @jasontucker @tim looks like I went down on a parallel path , the train of thought from the blogpost looks very similar 🙂 well, well…

  14. @imrehg @tim yeah I’ve learned to document stuff like this so smarter people can come by and tell me I did something wrong then

  15. @jasontucker @tim that sounds like how wisdom works 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.