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.
| Kind | What it is | What you do |
|---|---|---|
| AI Assistants (acquisition) | A human visitor who arrived from an AI assistant | Nothing — it works already |
| AI crawlers | Training and indexing bots hitting your site | Nothing — excluded by default |
| AI chatbots | The on-demand fetcher an assistant fires to read a page for a user | Opt 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.