Index: src/routes/activitypub.js
===================================================================
--- src/routes/activitypub.js	(revision bf4596d656b6f6cfed0106e540a0342cb99a070a)
+++ src/routes/activitypub.js	(revision 49edc72f102927812ac0294fdf7816d624a56cde)
@@ -97,10 +97,23 @@
     // Could be one of OUR outbound replies (ap_outbox), not a post.
     const note = AP.getOutboxNote(baseUrl(req), req.params.id);
-    if (note) return AP.sendAP(res, { '@context': 'https://www.w3.org/ns/activitystreams', ...note });
-    return res.status(404).end();
+    if (!note) return res.status(404).end();
+    if (!AP.apWants(req)) {
+      // A browser hit a reply's AP URL → send them to the source it replies to
+      // (where the post + its reactions live), falling back to the site home.
+      const src = (typeof note.inReplyTo === 'string' && /^https?:\/\//i.test(note.inReplyTo))
+        ? note.inReplyTo : (baseUrl(req) + '/');
+      return res.redirect(302, src);
+    }
+    return AP.sendAP(res, { '@context': 'https://www.w3.org/ns/activitystreams', ...note });
   }
   const site = db.prepare('SELECT * FROM sites WHERE id = ?').get(post.site_id);
   if (!site) return res.status(404).end();
-  AP.sendAP(res, { '@context': 'https://www.w3.org/ns/activitystreams', ...AP.buildNote(baseUrl(req), site, post) });
+  const note = AP.buildNote(baseUrl(req), site, post);
+  if (!AP.apWants(req)) {
+    // A browser hit a post's AP note URL → send them to the human post page
+    // (which shows the post + its "from the fediverse" reactions).
+    return res.redirect(302, note.url || (baseUrl(req) + '/'));
+  }
+  AP.sendAP(res, { '@context': 'https://www.w3.org/ns/activitystreams', ...note });
 });
 
