ActivityPub inaccessible on admin page

I’m running Ghost 6.0.0 using the Docker setup. I migrated over from my previous Ghost CLI installation when 6.0.0 was released. My Ghost instance is available at https://policyflow.nz.

Weirdly enough, I’ve just tried to set up ActivityPub. Everything seemed to have gone smoothly, yet I cannot access ActivityPub from the admin site. I get hit with the generic loading interrupted popup after it shows the Network tab with all of its components loading for a second or two.

In the logs for the ghost-activitypub-1 container, I keep getting logs such as:

13:53:25.973 INF activitypub: 'GET' 'policyflow.nz' 'https://policyflow.nz/.ghost/activitypub/v1/blocks/accounts' '[random string]' 500 2136ms
13:53:25.980 ERR activitypub: SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
    at JSON.parse (<anonymous>)
    at parseJSONFromBytes (node:internal/deps/undici/undici:5738:19)
    at successSteps (node:internal/deps/undici/undici:5719:27)
    at fullyReadBody (node:internal/deps/undici/undici:4609:9)
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async consumeBody (node:internal/deps/undici/undici:5728:7)
    at async getKey (file:///opt/activitypub/dist/app.js:386:18)
    at async roleMiddleware (file:///opt/activitypub/dist/app.js:428:17)
    at async dispatch (file:///opt/activitypub/node_modules/hono/dist/compose.js:22:17)
    at async dispatch (file:///opt/activitypub/node_modules/hono/dist/compose.js:22:17)
13:53:25.982 INF activitypub: 'GET' 'policyflow.nz' 'https://policyflow.nz/.ghost/activitypub/v1/account/me/follows/following' 'c9baaee5-f43e-4fca-b4d9-5b17d5c019a4' 500 2229ms
13:57:01.378 INF activitypub: 'POST' 'policyflow.nz' 'https://policyflow.nz/.ghost/activitypub/inbox/index' '[random string]'
13:57:01.399 INF activitypub: No site found for 'policyflow.nz'
13:57:01.400 INF activitypub: 'POST' 'policyflow.nz' 'https://policyflow.nz/.ghost/activitypub/inbox/index' '[random string]' 403 22ms

(While the [random string]'s are probably meaningless, I’ve redacted them anyway… I don’t know that much about how ActivityPub has been implemented :slight_smile:).

In my .envfile:

# Use the below flags to enable the Analytics or ActivityPub containers as well
COMPOSE_PROFILES=analytics,activitypub

# Ghost domain
# Custom public domain Ghost will run on
DOMAIN=policyflow.nz
url=https://policyflow.nz

# Ghost Admin domain
# If you have Ghost Admin setup on a separate domain uncomment the line below and add the domain
# You also need to uncomment the corresponding block in your Caddyfile
ADMIN_DOMAIN=cms.policyflow.nz
admin__url=https://cms.policyflow.nz

ACTIVITYPUB_TARGET=activitypub:8080

Caddy has largely been unchanged, but I have made some miscellaneous modifications to the compose file (adding redis, and adding a mount for Caddy for some static files I serve alongside my website separately from Ghost, as well as the necessary minor configuration for that within Caddy). Of course, this is alongside uncommenting out the necessary lines in Caddy for the admin page. Overall, none of the changes I have made should realistically be conflicting with ActivityPub at all.

I haven’t tested anything beyond the admin page other than some curl/wget attempts, but I suspect my activitypub instance probably isn’t working anywhere else either.

2 Likes

For what it’s worth, I’m having the exact same error on my Ghost 6.0 installation (manually migrated by importing into a fresh install), and I’ve made no changes to the compose file, so I can confirm that it’s nothing you’ve done.

Right after restarting Ghost, check your ghost logs. Do you see it setting up/confirming webhooks with activitypub?

What network status (in the browser) do you see for the /.ghost/* requests when you visit the network tab?

Hm. I’m seeing this:

[2025-08-07 19:10:26] INFO "GET /ghost/.well-known/jwks.json" 301 1672ms
[2025-08-07 19:10:27] INFO "GET /ghost/.well-known/jwks.json/" 404 222ms
[2025-08-07 19:10:27] INFO "GET /ghost/.well-known/jwks.json" 301 3ms
[2025-08-07 19:10:27] INFO "GET /ghost/.well-known/jwks.json/" 404 15ms
[2025-08-07 19:10:27] INFO "GET /ghost/.well-known/jwks.json" 301 9ms
[2025-08-07 19:10:27] INFO "GET /ghost/.well-known/jwks.json/" 404 11ms
[2025-08-07 19:10:27] INFO "GET /ghost/.well-known/jwks.json" 301 2ms
[2025-08-07 19:10:27] INFO "GET /ghost/.well-known/jwks.json/" 404 12ms
[2025-08-07 19:10:27] INFO "GET /ghost/.well-known/jwks.json" 301 4ms
[2025-08-07 19:10:27] INFO "GET /ghost/.well-known/jwks.json/" 404 18ms
[2025-08-07 19:10:27] INFO "GET /ghost/.well-known/jwks.json" 301 2ms
[2025-08-07 19:10:27] INFO "GET /ghost/.well-known/jwks.json/" 404 21ms
[2025-08-07 19:10:27] ERROR Could not get webhook secret for ActivityPub FetchError: invalid json response body at https://eldraeverse.com/.ghost/activitypub/v1/site reason: Unexpected
 token 'I', "Internal S"... is not valid JSON
[2025-08-07 19:10:27] ERROR No webhook secret found - cannot initialise

(There’s a corresponding? error in the ActivityPub container’s logs, too:

19:10:25.097 INF activitypub: 'GET' 'eldraeverse.com' 'https://eldraeverse.com/.ghost/activitypub/v1/site' '4cfbab9a-6a3b-4bad-bcc9-69e6d8d193c7'
19:10:27.868 ERR activitypub: SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
    at JSON.parse (<anonymous>)
    at parseJSONFromBytes (node:internal/deps/undici/undici:5738:19)
    at successSteps (node:internal/deps/undici/undici:5719:27)
    at fullyReadBody (node:internal/deps/undici/undici:4609:9)
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async consumeBody (node:internal/deps/undici/undici:5728:7)
    at async getKey (file:///opt/activitypub/dist/app.js:386:18)
    at async roleMiddleware (file:///opt/activitypub/dist/app.js:428:17)
    at async dispatch (file:///opt/activitypub/node_modules/hono/dist/compose.js:22:17)
    at async dispatch (file:///opt/activitypub/node_modules/hono/dist/compose.js:22:17)

In the browser, if I bring up the dev tools to see actual errors, when I visit the network tab, all its subpages bring up dozens of variants on the theme of:

ghost/#/analytics:1  Access to XMLHttpRequest at 'https://eldraeverse.com/.ghost/activitypub/v1/notifications/unread/count' from origin 'https://admin.eldraeverse.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

…varying only by which /.ghost/* URL is involved.

I note that this also happens if I reconfigure the site to use the default (hosted) ActivityPub provider rather than running my own ActivityPub container.

(I can’t see the AP container’s logs there, obviously, but the Ghost container logs

[2025-08-09 23:45:38] INFO "GET /ghost/.well-known/jwks.json" 301 1201ms
[2025-08-09 23:45:38] INFO "GET /ghost/.well-known/jwks.json/" 404 218ms
[2025-08-09 23:45:38] INFO "GET /ghost/.well-known/jwks.json" 301 4ms
[2025-08-09 23:45:38] INFO "GET /ghost/.well-known/jwks.json/" 404 11ms
[2025-08-09 23:45:39] INFO "GET /ghost/.well-known/jwks.json" 301 4ms
[2025-08-09 23:45:39] INFO "GET /ghost/.well-known/jwks.json/" 404 15ms
[2025-08-09 23:45:39] INFO "GET /ghost/.well-known/jwks.json" 301 4ms
[2025-08-09 23:45:39] INFO "GET /ghost/.well-known/jwks.json/" 404 9ms
[2025-08-09 23:45:39] INFO "GET /ghost/.well-known/jwks.json" 301 7ms
[2025-08-09 23:45:39] INFO "GET /ghost/.well-known/jwks.json/" 404 9ms
[2025-08-09 23:45:39] INFO "GET /ghost/.well-known/jwks.json" 301 7ms
[2025-08-09 23:45:39] INFO "GET /ghost/.well-known/jwks.json/" 404 13ms
[2025-08-09 23:45:40] ERROR No webhook secret found - cannot initialise

are basically identical.)

Aha!

In other notes, having just tried disabling ADMIN_DOMAIN on a hunch, I have discovered that that fixes this problem.

This may mean that

ActivityPub Fails to Load When Self-Hosting ActivityPub and Ghost with Separate Admin URL Configuration · Issue #24639 · TryGhost/Ghost

will be the long-term solution this calls for.

https://eldraeverse.com/.ghost/activitypub/v1/site needs to be going to the admin domain.

I think that /ghost/.well-known/jwks.json should also be going to the admin domain, but I’m not 100% sure on that.

For me, the solution was an issue in Ghost that was fixed in v6.0.3. If you haven’t upgraded, please do so, it should resolve this issue. (Just noticed that v6.0.4 has come out a few minutes at the time of writing). It seems to have just been an issue with sites that have a separate admin domain.