Index: src/routes/posts.js
===================================================================
--- src/routes/posts.js	(revision 4c9c60bcdb47e6d3cc3b4b710bfb61477bb9f554)
+++ src/routes/posts.js	(revision ca25f360f73b889318b921f39ff70bb54eb6d853)
@@ -354,12 +354,15 @@
   } catch (e) { /* FTS issues non-fatal */ }
 
-  // ActivityPub: federate when a post BECOMES published (draft/scheduled → published
-  // via the editor). A brand-new published post is handled in the create route.
-  if (finalStatus === 'published' && post.status !== 'published' && !fanOnly) {
-    ActivityPubService.deliverCreate(site, {
+  // ActivityPub: federate edits to followers. A post that BECOMES published →
+  // Create (new post); an already-published post that's edited → Update (so
+  // Mastodon refreshes its cached copy). fan_only posts don't federate.
+  if (finalStatus === 'published' && !fanOnly) {
+    const apPost = {
       id: post.id, slug: finalSlug, title: title || finalSlug,
       content: cleanContent, cover_image_url: cover_image_url || null,
       published_at: publishedAt, created_at: post.created_at,
-    }).catch(() => { /* best-effort */ });
+    };
+    if (post.status !== 'published') ActivityPubService.deliverCreate(site, apPost).catch(() => { /* best-effort */ });
+    else ActivityPubService.deliverUpdate(site, apPost).catch(() => { /* best-effort */ });
   }
 
