Skip to main content

AI assistants

Matomo splits AI traffic three ways, and they are genuinely different things. The package covers all three, but each needs something different from you.

KindWhat it isWhat you do
AI Assistants (acquisition)A human visitor who arrived from an AI assistantNothing — it works already
AI crawlersTraining and indexing bots hitting your siteNothing — excluded by default
AI chatbotsThe on-demand fetcher an assistant fires to read a page for a userOpt in, below

AI Assistants — already working

Matomo 5.5+ derives this acquisition channel from the visit referrer. The package already forwards the referrer, so the report populates with no configuration at all.

That also means the referrer must survive: URL redaction is careful to keep the referrer host intact while scrubbing the parameters of a tracked URL, which is what keeps this channel working.

AI crawlers — already excluded

The training and indexing bots are excluded by default. See bots and AI crawlers.

AI chatbots — the one that needs you

When someone asks an assistant about your page, the assistant fetches it. That fetch runs no JavaScript, so the client-side tracker never sees it, and it is not a visit — nobody browsed anything.

Matomo's own collector for this is a Cloudflare Worker at the edge. The primitive that Worker sends is just a Tracking-API hit with recMode set, which this package can emit itself from PHP — same telemetry, no edge infrastructure and no per-request edge cost.

'ai_chatbots' => [
'track' => true, // off by default
'auto' => true, // auto-register the matomo.chatbots middleware on the 'web' group
],

Or attach the middleware to specific routes:

Route::get('/docs/{page}', DocsController::class)->middleware('matomo.chatbots');

Or record a fetch yourself, wherever you detect one:

Matomo::aiChatbot($request);

What it records, and what it does not

Each recognized fetch is sent as Matomo bot telemetry: recMode set, no image response requested, the fetched URL (redacted like any other), the fetch time and a source label identifying the collector. The User-Agent is forwarded so Matomo can attribute the fetch to the right assistant.

It never creates a visit. That is the entire point: these fetches stay in the AI Chatbots report and out of your human analytics, so your visit and page-view numbers are unaffected by however much attention an assistant pays you.

The telemetry deliberately bypasses the visitor tracking gate — these are bots on purpose, and the gate exists to exclude bots. Its own switch is ai_chatbots.track, which is off by default, so nothing is sent until you ask for it. The enabled master switch and a configured connection are still required.

The recognized fetchers

The default list is the narrow set of on-demand fetchers Matomo surfaces — ChatGPT-User, Claude-User, Gemini-Deep-Research, Google-NotebookLM, MistralAI-User, Perplexity-User — matched case-insensitively as substrings. These are the live fetchers, not the training crawlers; the two lists are separate on purpose.

Matomo only surfaces User-Agents it recognizes under recMode, which is why the default list is narrow rather than generous. Override it if you know Matomo recognizes more:

'ai_chatbots' => [
'user_agents' => ['ChatGPT-User', 'Claude-User', 'Your-Assistant-Here'],
],

The other two settings

'ai_chatbots' => [
'rec_mode' => 1, // 1 = bot only (non-bots discarded), 2 = auto (Matomo decides)
'source' => 'Laravel', // label sent with each hit to identify the collector
],

rec_mode => 1 is the safe default: Matomo discards the hit if it does not consider the User-Agent a bot, so a misconfigured list cannot inject fetches into your human analytics. 2 lets Matomo decide how to file it.

source is a free label that identifies this collector in Matomo, which is how you tell hits sent from here apart from hits sent by an edge Worker if you ever run both.

Requirements

The AI Chatbots report needs Matomo 5.8+. The package sends the telemetry against any version; an older Matomo simply has nowhere to display it.

Only GET requests with a recognized User-Agent are captured, and the tracking happens in the middleware's terminate() — after the response has been sent, so the fetcher never waits for it.