Why your analytics shows zero AI crawler hits
Script-based analytics — ours included — works by executing JavaScript in the visitor’s browser. That is the right architecture for humans, and it even catches Googlebot, because Google renders pages with a real browser engine before indexing them (Google documents this rendering step).
Most AI crawlers do not render. GPTBot, ClaudeBot, CCBot, Meta-ExternalAgent, Amazonbot and the rest fetch your raw HTML, parse it, and leave — no JavaScript ever runs, so no analytics beacon ever fires. A script-based tracker is not undercounting them; it is structurally incapable of seeing them.
A CDN like Cloudflare sees everything because it terminates every request before your server does. Your analytics vendor is not in that path — which is exactly why the fix has to live on your server.
Why you should care about crawler traffic at all
AI crawler hits are not vanity data. They are the top of the **AI visibility** funnel: an assistant can only cite or recommend pages its crawlers have fetched. If you are investing in generative engine optimization, crawl activity is the first measurable signal that it is working.
- Coverage — which of your pages AI crawlers actually fetch (and which they ignore). Crawlers hammer `/robots.txt`, `/llms.txt` and your sitemap; if they never reach your money pages, your llms.txt and internal linking need work.
- Trend — whether AI attention is growing after content or GEO changes.
- Correlation — crawl activity → citations in AI answers → referral visits from assistants (the AI channel in your analytics). Seeing all three in one place tells you where the chain breaks.
- Policy — whether your robots.txt or CDN bot settings are silently blocking crawlers you actually want (check with an AI crawler checker).
Install: Next.js, Express, Cloudflare Workers
Your tracker ID (the `trk_…` value) is in your install snippet. Each example forwards suspected bots to the BusinessMCP bot-hit endpoint; adapt the regex freely — over-sending is fine because the server drops non-bots.
Next.js (middleware.ts — or add the same block to your existing middleware):
import { NextResponse, type NextRequest, type NextFetchEvent } from 'next/server'
const BOT = /bot|crawl|spider|slurp|gptbot|claude|anthropic|perplexity|amazonbot|bytespider|ccbot|meta-external|applebot|headless/i
export function middleware(req: NextRequest, event: NextFetchEvent) {
const ua = req.headers.get('user-agent') || ''
if (BOT.test(ua)) {
event.waitUntil(
fetch('https://businessmcp.com/api/tracker/trk_XXXXXXXX/bot-hit', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ userAgent: ua, path: req.nextUrl.pathname }),
}).catch(() => {}),
)
}
return NextResponse.next()
}
export const config = { matcher: ['/((?!api/|_next/).*)'] }Express / Node:
const BOT = /bot|crawl|spider|slurp|gptbot|claude|anthropic|perplexity|amazonbot|bytespider|ccbot|meta-external|applebot|headless/i
app.use((req, res, next) => {
const ua = req.headers['user-agent'] || ''
if (BOT.test(ua)) {
fetch('https://businessmcp.com/api/tracker/trk_XXXXXXXX/bot-hit', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ userAgent: ua, path: req.path }),
}).catch(() => {})
}
next()
})Cloudflare Worker (if your DNS is proxied through Cloudflare):
const BOT = /bot|crawl|spider|slurp|gptbot|claude|anthropic|perplexity|amazonbot|bytespider|ccbot|meta-external|applebot|headless/i
export default {
async fetch(request, env, ctx) {
const ua = request.headers.get('user-agent') || ''
if (BOT.test(ua)) {
ctx.waitUntil(
fetch('https://businessmcp.com/api/tracker/trk_XXXXXXXX/bot-hit', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ userAgent: ua, path: new URL(request.url).pathname }),
}).catch(() => {}),
)
}
return fetch(request)
},
}Blocking vs. measuring
Measuring crawlers and blocking them are different decisions. Some CDNs now block AI crawlers by default — Cloudflare, for instance, lets sites block or charge AI crawlers with managed robots.txt rules. That is a legitimate content-licensing posture, but it has a visibility cost: crawlers that cannot fetch you cannot cite you, and the assistant traffic those citations drive goes to whoever they can fetch.
Our recommendation for most B2B sites: measure first, then decide per crawler class. Training-only crawlers (CCBot, Bytespider) are a fair robots.txt target if training use bothers you; answer-engine crawlers (OAI-SearchBot, ChatGPT-User, PerplexityBot) are the ones that turn into cited answers and referral visits — blocking those is usually self-harm. Publish crawler policy in robots.txt (crawlers self-identify their user agents and IP ranges) and keep server-side tracking of what actually hits you.
Frequently asked questions
Why does my JavaScript analytics show Googlebot but not GPTBot?
Googlebot renders pages with a real browser engine, so it executes your analytics script like a human visitor would. GPTBot, ClaudeBot, CCBot and most AI crawlers fetch raw HTML without rendering — no JavaScript runs, so no beacon fires. Only a server-side hook (or your CDN) can see them.
Will forwarded bot hits inflate my visitor counts or bill?
No. Bot hits are stored with an is_bot flag and excluded from visitors, sessions, conversion rates and usage meters. They only feed the crawler analytics panel and the AI-visibility metrics.
Can someone spoof GPTBot and pollute my crawler data?
User agents are self-declared, so yes in principle — the same caveat applies to every log-based tool. Major operators publish official IP ranges (OpenAI and Perplexity publish JSON lists) that allow verification; spoofed crawler traffic is rare in practice because there is nothing to gain. Treat crawler analytics as directional, and visitor analytics — which layers several anti-bot checks — as the audited number.
Do I still need the JavaScript snippet if I install the server hook?
Yes — they answer different questions. The script tracks humans (sessions, goals, attribution, replays) with client-side signals a server cannot see. The server hook only adds the crawler layer the script cannot see. Together they cover both sides.
Sources
BusinessMCP Team
Every guide is written from running BusinessMCP on its own platform — the match rates, reply rates, and deliverability lessons are from our own data, not recycled blog folklore. About BusinessMCP
Turn your business into one AI-ready MCP server
Connect your tools, install one tracking script, and expose your unified data to any AI agent through a single secure endpoint.
Get started free