Index: src/routes/posts.js
===================================================================
--- src/routes/posts.js	(revision 2d6a9c360f54275fa0b6256c64c88bf2d5782554)
+++ src/routes/posts.js	(revision af210025a9f53cf80906d139049d8febdf1539ab)
@@ -243,8 +243,19 @@
 // baking on the fly if this ever fails.
 function cacheRenderedContent(postId, rawContent) {
+  const raw = rawContent || '';
+  // 1. Immediate + synchronous: bake #hashtags + URLs so the post renders enriched at once.
   try {
     db.prepare('UPDATE posts SET content_rendered = ? WHERE id = ?')
-      .run(ActivityPubService.bakePostContent(rawContent || ''), postId);
+      .run(ActivityPubService.bakePostContent(raw), postId);
   } catch (e) { /* fallback bake in the render route keeps display correct */ }
+  // 2. Async: resolve @mentions (webfinger, once) and re-store, WITHOUT blocking the save
+  //    response — a moment later the post's @mentions are clickable too. A slow/dead remote
+  //    server can't stall the save; on failure the sync bake from step 1 stands.
+  ActivityPubService.bakePostContentWithMentions(raw)
+    .then((html) => {
+      try { db.prepare('UPDATE posts SET content_rendered = ? WHERE id = ?').run(html, postId); }
+      catch (e) { /* keep the sync bake */ }
+    })
+    .catch(() => { /* keep the sync bake */ });
 }
 
Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision 2d6a9c360f54275fa0b6256c64c88bf2d5782554)
+++ src/services/ActivityPubService.js	(revision af210025a9f53cf80906d139049d8febdf1539ab)
@@ -1472,4 +1472,15 @@
 }
 
+// Step 2: the full bake, incl. @mention links. Resolves @user@host via webfinger ONCE (the
+// same resolver the federated copy uses) and bakes the profile links into content_rendered,
+// so page views never do a per-view lookup. Unresolvable handles stay plain text; on any
+// failure it degrades to the sync #hashtag/URL bake. Async (webfinger) → callers run it off
+// the save response so the request never blocks on a slow/dead remote server.
+export async function bakePostContentWithMentions(source) {
+  const withHashUrls = bakePostContent(source);
+  try { const m = await resolveMentionsInText('', withHashUrls); return m.html; }
+  catch { return withHashUrls; }
+}
+
 // Extract the AP Hashtag tag objects from already-linked reply content.
 function hashtagTags(base, content) {
@@ -2471,4 +2482,4 @@
   deliverWithRetry, enqueueDelivery, processDeliveryQueue, startDeliveryWorker,
   getReplyUris, markNotificationsSeen, countUnseenNotifications, hasPlayableAudio,
-  linkifyBody, bakePostContent, listFollowers, removeFollower,
+  linkifyBody, bakePostContent, bakePostContentWithMentions, listFollowers, removeFollower,
 };
