Index: src/services/ActivityPubService.js
===================================================================
--- src/services/ActivityPubService.js	(revision 0f48411a45b8908eb1849b383af40a3b658f68c9)
+++ src/services/ActivityPubService.js	(revision cae8ded17a1ccf1df4860076a16aa2d9394881f7)
@@ -96,4 +96,12 @@
   if (!addrs.length || addrs.some((a) => isBlockedIp(a.address))) throw new Error('ssrf-blocked-host');
 }
+// One honest name on ALL outbound federation traffic (Robins vraag, 31-7):
+// safeFetch went out with the bare Node default before, and polite fediverse
+// citizens say who they are (some instances even refuse anonymous UAs). A
+// caller-provided User-Agent (the EmbedResolver) still wins.
+let _uaVer = '1.0';
+try { _uaVer = JSON.parse(fs.readFileSync(new URL('../../package.json', import.meta.url))).version || _uaVer; } catch { /* keep default */ }
+const KLONKT_UA = `Klonkt/${_uaVer} (+https://klonkt.com)`;
+
 export async function safeFetch(url, opts = {}, maxRedirects = 3) {
   let target = url;
@@ -102,5 +110,10 @@
     if (u.protocol !== 'https:' && u.protocol !== 'http:') throw new Error('ssrf-bad-scheme');
     await assertPublicHost(u.hostname);
-    const r = await fetch(target, { ...opts, redirect: 'manual', signal: AbortSignal.timeout(8000) });
+    const r = await fetch(target, {
+      ...opts,
+      headers: { 'User-Agent': KLONKT_UA, ...(opts.headers || {}) },
+      redirect: 'manual',
+      signal: AbortSignal.timeout(8000),
+    });
     const loc = (r.status >= 300 && r.status < 400) ? r.headers.get('location') : null;
     if (loc && hop < maxRedirects) { target = new URL(loc, target).toString(); continue; }
Index: src/services/StatsService.js
===================================================================
--- src/services/StatsService.js	(revision 0f48411a45b8908eb1849b383af40a3b658f68c9)
+++ src/services/StatsService.js	(revision cae8ded17a1ccf1df4860076a16aa2d9394881f7)
@@ -47,5 +47,5 @@
 // Skip known bots/crawlers + link-preview fetchers + scripts so they don't inflate
 // view/visitor-day counts. Empty UA = almost always automated.
-const BOT_RE = /bot|crawl|spider|slurp|mediapartners|bingpreview|facebookexternalhit|whatsapp|telegram|discord|twitter|linkedin|embedly|pinterest|redditbot|applebot|petalbot|yandex|baidu|duckduckbot|semrush|ahrefs|mj12|dotbot|uptimerobot|pingdom|statuscake|headless|lighthouse|gptbot|claude|ccbot|perplexity|bytespider|amazonbot|googleother|google-read-aloud|python-requests|scrapy|curl|wget|axios|node-fetch|go-http|java\/|okhttp|libwww|httpclient|mastodon|pleroma|akkoma|misskey|calckey|firefish|friendica|hubzilla|lemmy|pixelfed|peertube|gotosocial|honk|http\.rb|activitypub/i;
+const BOT_RE = /bot|crawl|spider|slurp|mediapartners|bingpreview|facebookexternalhit|whatsapp|telegram|discord|twitter|linkedin|embedly|pinterest|redditbot|applebot|petalbot|yandex|baidu|duckduckbot|semrush|ahrefs|mj12|dotbot|uptimerobot|pingdom|statuscake|headless|lighthouse|gptbot|claude|ccbot|perplexity|bytespider|amazonbot|googleother|google-read-aloud|python-requests|scrapy|curl|wget|axios|node-fetch|go-http|java\/|okhttp|libwww|httpclient|mastodon|pleroma|akkoma|misskey|calckey|firefish|friendica|hubzilla|lemmy|pixelfed|peertube|gotosocial|honk|http\.rb|activitypub|klonkt/i;
 function isBot(req) {
   const ua = (req && req.headers && req.headers['user-agent']) || '';
