Server-side vs client-side tracking for AI traffic: why your JS analytics sees almost nothing
Here's a number from our own site that reframes the debate: in a recent 30-day window, over 90% of our AI and bot traffic was visible only server-side. Client-side analytics — GA4, Plausible, any script-tag tool — never saw it, and never could have. That's not a configuration problem. It's physics.
Why client-side tools can't see AI traffic
A JavaScript analytics tag fires when a browser executes it. Most AI agents never do:
- Training crawlers (GPTBot, ClaudeBot, Meta-ExternalAgent) fetch raw HTML. No JS engine, no tag, no pageview.
- AI search indexers (OAI-SearchBot, PerplexityBot) — same.
- Assistant fetchers (ChatGPT-User) — same again, which is why the pages they read must carry facts in HTML.
The only AI category a browser tag can catch is agentic browsers — AI driving a real browser. Those execute JavaScript like a human visitor, which creates the opposite problem: client-side tools count them as humans, silently polluting conversion rates and engagement metrics.
So client-side-only measurement fails in both directions at once: blind to the crawlers, fooled by the agents.
Why server-side-only isn't the answer either
Flip fully to server-side (log analysis, edge middleware) and you gain the complete picture of requests — every crawler, every fetcher, every impostor. But you lose the behavior layer: scroll depth, time on page, SPA navigation, and the client-side signals (webdriver flags, missing language settings, headless fingerprints) that are precisely how you tell an agentic browser from a person. You also inherit a double-counting trap: if a page has both a snippet and server reporting, a naive setup counts every human twice.
The architecture that actually works
The two vantage points see different halves; the design question is fusing them without double counting:
- A light client snippet handles humans and agentic browsers — engagement, behavioral signals, tag firing.
- Server-side collection (middleware or log forwarding) handles everything that never runs JS — the crawlers and fetchers.
- A dedupe rule so humans counted by the snippet aren't recounted server-side. (In our implementation, server-collect skips human-classified events by default for exactly this reason.)
- IP verification on claims — server-side is also where you catch that a "GPTBot" request came from the wrong network. On our site, about two-thirds of checkable agent claims failed verification; unverified server logs overcount AI badly.
The migration reality check
You don't need to rip anything out. Keep GA4 or whatever measures your human funnel — it's fine at that job. Add the server-side layer for the traffic it can't see, and a classification layer so each visitor type is counted once, correctly, with spoofs downgraded. The teams treating this as "replace analytics" stall; the teams treating it as "add the missing half" ship in an afternoon — a snippet plus a WordPress plugin or middleware adapter, not a data-engineering project.
The blunt summary: client-side tells you what humans do; server-side tells you what machines do; and in 2026 the machines are most of your visitors. Measuring only half the traffic isn't a simplification anymore — it's a blindfold.
See which AI agents visit your site — free.
Start with VisitorType