Server log AI agent detection is the measurement layer sitting underneath everything your analytics platform reports. A JavaScript-based tag only fires when a browser executes it. An autonomous crawler issues an HTTP request, receives your HTML and leaves — no script execution, no session, no event. The request is real, it consumed your bandwidth and it may have shaped an answer about your brand, but the only place it exists is your access log.
That gap matters more every quarter. The vendors building answer engines now run separate agents for separate jobs — training-corpus collection, search-index building, live user-triggered fetches — and each one carries a different consequence when you block it. Treating them as a single bucket called “AI traffic” hides the decision you actually care about, and no client-side analytics product is positioned to tell them apart.
This playbook covers the structural reason the tag never fires, the crawl-purpose split running through the vendor documentation, a cross-vendor user-agent reference matrix, why a UA string is a claim rather than an identity, what the current traffic mix actually looks like, a six-step log-analysis recipe, and an honest limits section about the one number nobody can give you. For the analytics side of the same problem — how assistant-referred human sessions get classified — see our GA4 AI Assistant channel playbook, which owns that half and is not repeated here.
- 01Client-side analytics is structurally blind to agents.A crawler does not execute JavaScript, so no tag fires and no session is created. This is a property of how measurement works, not a configuration gap you can close with a channel group.
- 02OpenAI and Perplexity ship the same three-way split.Both separate training-corpus collection from search-index building from live user-triggered fetch. Neither vendor frames this as a pattern; assembling it across both does.
- 03Blocking GPTBot does not remove you from ChatGPT search.That control belongs to OAI-SearchBot, and the two settings are independent. A blanket block-all-AI-bots rule quietly forfeits citation visibility while doing nothing you intended.
- 04A user-agent string is spoofable; an IP range is not.OpenAI and Perplexity both publish machine-readable IP-range files precisely so log analysis can verify the claim. There is no equivalent verification step available to a client-side tag.
- 05Most agent traffic is still corpus-building, not answering.In the current rolling window, training and training-and-search purposes together account for 81.7% of AI-bot HTTP requests, while live user-action fetches account for 3.2%.
01 — The Blind SpotTwo data sources, two different questions.
Start with the mechanism rather than the dashboard. A client-side analytics platform works because a browser downloads your page, runs a script, and that script posts an event to a collection endpoint. Everything downstream — sessions, channels, attribution, conversion paths — is built on that one event having happened.
An autonomous crawler does none of that. It opens a connection, sends a request with a user-agent header, takes the HTML body, and disconnects. There is no script execution and therefore no event, no session and no channel assignment. The interaction is invisible to the analytics product not because the product is misconfigured but because the product was never in the request path.
The consequence is worth stating plainly, because a lot of published advice implies otherwise: you cannot fix agent measurement by adding a custom channel group, a regex on referrer, or a new dimension. Those tools operate on events that exist. Agent requests produce no events. The only artefact they produce is a line in an access log, which is why this is a server-side discipline and sits closer to server-side tracking methodology than to anything in a tag manager.
02 — Crawl PurposeOpenAI and Perplexity ship the same purpose split.
Read OpenAI and Perplexity’s crawler documentation side by side and a pattern emerges that neither of them describes as a pattern, because each is only documenting itself. Both separate the job of collecting training data from the job of building a search index from the job of fetching one page because a live user asked a question. The names differ. The taxonomy does not. OpenAI carries a fourth agent on top of those three for ad-page safety, which is why the grid below runs to four cards rather than three; Google’s documented controls, covered in the matrix that follows, do not include a user-fetch agent at all.
This is the single most useful thing to internalise before you touch a log file, because the purpose determines what a block actually costs you. Blocking a training crawler removes your content from a corpus. Blocking an index crawler removes you from an answer engine’s citations. Those are different business outcomes with different reversibility, and collapsing them into one row in a report makes the trade-off impossible to see.
Training crawl
Unattended collection for model training. GPTBot is a real crawler that issues its own requests and appears in your log as its own user agent. Google-Extended is different in kind: it is a robots.txt token controlling whether crawled content trains future Gemini models and is used for grounding in Gemini Apps and Vertex AI, and it rides existing Googlebot fetches rather than issuing requests of its own.
Search index
Builds the answer engine’s retrievable index. Perplexity states directly that PerplexityBot is not used to crawl content for AI foundation models — the index and the training corpus are separate systems with separate controls. This is the category whose blocking decision costs you citation supply.
User fetch
A person asked a question and the assistant fetched your page to answer it. OpenAI documents ChatGPT-User as not used for crawling the web in an automatic fashion, and Perplexity describes Perplexity-User as a live page fetch triggered by a specific user question. This is the only category with someone waiting on the other end, which is why it belongs in a traffic model rather than a bot report.
Ads safety
Validates advertising page safety rather than collecting a corpus or building an index. Low volume and easy to misfile as a training crawl if your classifier matches on the vendor rather than the specific agent, which is exactly the error that makes an AI-traffic report look bigger and less actionable than it is.
03 — Reference MatrixThe AI agent user-agent matrix.
Each vendor publishes its own agent list. Nobody publishes the cross-vendor version with the two columns that actually drive decisions: whether robots.txt is a documented control for that agent, and what blocking it does not affect. The table below assembles that from the OpenAI, Perplexity and Google primary documentation as it read at the time of writing.
Two rows carry an explicit gap. Anthropic’s crawler documentation was unreachable when this was compiled, and no Meta primary was available for Meta-ExternalAgent, so their exact user-agent strings are deliberately absent rather than guessed. Both agents are nevertheless significant by volume, so they appear with what is actually verifiable — their share of requests — and nothing more.
| Agent | Exact token | Declared purpose | Robots.txt control | IP verification file | What blocking it does NOT affect |
|---|---|---|---|---|---|
| OpenAI — four agents, four downstream roles | |||||
| GPTBot | GPTBot/1.4 | Training-data collection | Yes — the documented opt-out for training | openai.com/gptbot.json | Your presence in ChatGPT search results — that is a separate agent |
| OAI-SearchBot | OAI-SearchBot/1.4 | ChatGPT search index | Yes — the control for search inclusion | openai.com/searchbot.json | Model training collection — that remains a GPTBot decision |
| ChatGPT-User | ChatGPT-User/1.0 | User-initiated fetch; documented as not used for crawling the web in an automatic fashion | Not documented in the sources checked | openai.com/chatgpt-user.json | Training corpus and search index, both governed separately |
| OAI-AdsBot | OAI-AdsBot/1.0 | Validates ad-page safety | Not documented in the sources checked | openai.com/adsbot.json | Organic citation or training paths — different agents entirely |
| Perplexity — index versus live user question | |||||
| PerplexityBot | PerplexityBot/1.0 | Builds the search index; stated as not used to crawl content for AI foundation models | Yes — the crawl-purpose agent, contrasted against Perplexity-User | perplexity.com/perplexitybot.json | Live user-triggered fetches, which follow a different governance model |
| Perplexity-User | Perplexity-User/1.0 | Live page fetch triggered by a specific user question | No — generally ignores robots.txt, because a user rather than a crawl scheduler triggered the request | perplexity.com/perplexity-user.json | Index inclusion, which is the PerplexityBot decision |
| Google — a token, not only a crawler | |||||
| Google-Extended | Google-Extended | Controls whether crawled content trains future Gemini models and is used for grounding in Gemini Apps and Vertex AI | It is the robots.txt token; it issues no requests of its own | None published — nothing to verify, since it rides Googlebot fetches | Google states it does not impact inclusion in Google Search nor act as a ranking signal |
| Google-CloudVertexBot | Google-CloudVertexBot | Site-owner-requested crawl for building Vertex AI Agents | Not documented in the sources checked | Not published in the sources checked | Search crawling and Gemini training, which are separate decisions |
| Present in the traffic data — user-agent strings not verified from a vendor primary at the time of writing | |||||
| ClaudeBot | Not verified — do not hard-code a string you have not read in the vendor docs | Not sourced from a vendor primary | Not sourced | Not sourced | Verifiable at the time of writing: 14% of AI and crawl bot HTTP requests in the rolling window |
| Meta-ExternalAgent | Not verified — no vendor primary was available this pass | Not sourced from a vendor primary | Not sourced | Not sourced | Verifiable at the time of writing: 14.4% of AI and crawl bot HTTP requests, ahead of GPTBot |
Two structural observations fall out of the matrix once it is assembled. First, the “what blocking does not affect” column is almost always populated — which means almost every blocking decision people describe as one decision is really two or three independent ones. Second, the one agent that explicitly does not honour robots.txt is the one representing a live human request, which inverts the usual intuition that the more autonomous something is, the less it respects your rules.
04 — VerificationA user-agent string is a claim, not an identity.
Anyone can send a request announcing itself as GPTBot. The header is free text, set by the client, and there is nothing in the protocol that makes it true. Any report built purely on string matching is therefore counting assertions, not agents — and it will overcount in exactly the direction that flatters the report.
Both vendors examined here solve this the same way: they publish a machine-readable file of the IP ranges their agents actually fetch from. OpenAI publishes one per agent, and Perplexity does the same. The verification step is mechanical — match the user-agent token, then confirm the source address falls inside the published range, and only then increment the counter.
Published IP-range files
Four from OpenAI covering GPTBot, OAI-SearchBot, ChatGPT-User and OAI-AdsBot, plus two from Perplexity covering PerplexityBot and Perplexity-User. Cache them locally, refresh on a schedule, and treat an unverified match as a separate bucket rather than dropping it silently.
IP checks available to a tag
A JavaScript tag cannot verify the origin of a request it never received. Verification is a server-side capability by construction — which is the cleanest argument that log analysis complements client-side analytics rather than duplicating it.
Distinct agents from one vendor
OpenAI alone runs four, each with a different downstream consequence. Matching on the vendor name, or on a loose substring, collapses them into a single number and destroys the only distinction your blocking policy actually depends on.
A user-agent header is the only part of a request the client gets to write about itself. Treat it as testimony, then go and check the address it came from.— Digital Applied, editorial synthesis
05 — Traffic ShareMost agent traffic is still corpus-building.
Cloudflare Radar publishes a live view of AI and crawl bot activity across its network, including a breakdown by the crawl purpose each bot declares. The two charts below are a snapshot of its rolling seven-day window at the time of writing. That framing matters: this is a dashboard that moves, not a fixed historical statistic, so re-pull it before quoting rather than treating these figures as durable.
Top five AI and crawl bots by share of HTTP requests
Source: Cloudflare Radar AI Insights · live rolling seven-day window, snapshot taken at the time of writingDeclared crawl purpose · share of AI-bot HTTP requests
Source: Cloudflare Radar AI Insights · live rolling seven-day window, snapshot taken at the time of writingRun the arithmetic and the popular narrative inverts. Training and training-and-search together account for 81.7% of AI-bot requests in this window, while the live user-action category — the fetches where a person is genuinely waiting for an answer that includes your page — accounts for 3.2%. That is roughly twenty-five training-oriented requests for every one request with a human attached. Most agent traffic is not answering a question about you; it is reading you into a model.
The bot ranking carries a second correction. Meta-ExternalAgent sits at 14.4% against GPTBot’s 8.9% — about 1.6 times the volume — yet it is largely absent from AI-traffic coverage, which skews heavily toward OpenAI, Perplexity and Anthropic. If your classifier only knows the agents you have read about, your report will be wrong by roughly the size of the agent you have not. For the wider longitudinal picture, our AI crawler and bot traffic statistics reference tracks these series over time.
Our read on where this goes: the user-action slice is the one to watch, not the headline total. As assistants shift from answering out of a pre-built index toward fetching pages live at question time, that 3.2% is the series that should grow, and it is the only series whose growth plausibly correlates with commercial outcomes. A total that swells because a new lab started a training crawl tells you about someone else’s roadmap. A rising user-action count tells you people are asking questions your pages answer.
06 — Log RecipeSix steps from a raw access log to a number you can defend.
None of this requires a vendor. It requires access to your own request data and the discipline to run the same pipeline every month so the series is comparable with itself.
1. Get a log that actually contains the requests
You need timestamp, request path, HTTP status, user-agent and client IP at minimum. Take it from your CDN or edge layer rather than the origin where possible — anything served from edge cache may never reach an origin log, which silently undercounts exactly the high-frequency crawlers you most want to see.
2. Classify by agent, never by vendor
Exact-match the published tokens from the matrix above. Resist the substring shortcut: a rule matching on a vendor name folds four OpenAI agents with four different consequences into one meaningless row, and a rule matching on the word “bot” will sweep in every uptime monitor you run.
3. Verify before you count
For every matched request, check the source address against the vendor’s published IP-range file. Keep unverified matches in their own bucket rather than discarding them — a rising unverified count is itself a signal, either that a range file changed or that something is impersonating a crawler.
4. Report purpose as separate series
Training, search-index, user-fetch and ads-safety go in four different lines on the chart. Never sum them into a single “AI traffic” figure. They move for different reasons, they respond to different controls, and averaging them produces a number that cannot drive any decision.
5. Join the log to your content map
Which URLs are being fetched, how deeply, how often, and what status codes they receive. A 404 or an unnecessary redirect served to an index crawler is a citation you quietly lost, and it is only visible here — the analytics side has no record of a request that never became a session. If citation volume is the outcome you care about, pair this with our analysis of AI search citation behaviour on top-ranking pages.
6. Reconcile against the analytics side
The user-fetch series is the only one that should correlate with assistant-referred sessions; the training and index series should not. OpenAI states that ChatGPT automatically includes the UTM parameter utm_source=chatgpt.com in referral URLs, which gives you a clean join key on the analytics side without any custom tagging — see our UTM tagging fundamentals if that layer is not already clean. Building this reconciliation into a monthly reporting rhythm is standard scope in our analytics engagements.
07 — The SeamLogs plus analytics — not instead of.
The two systems are complements with a narrow, valuable overlap. The analytics side answers how many people arrived because an assistant surfaced you, and what those people did once they landed. The log side answers which agents fetched what, for which declared purpose, and whether you served them correctly. Neither can answer the other’s question, and a report that pretends otherwise is the most common failure mode in this space. Both feed the same downstream decision, which is why we treat log-side agent data as an input to agentic SEO programmes rather than as a separate infrastructure report.
We are not going to re-explain channel configuration here, because that is a solved problem with its own home: our GA4 AI Assistant channel playbook owns the analytics-side setup, the referrer-classification gaps and the dark-traffic framing in full. Read it as the companion piece; everything on this page assumes it and adds the layer beneath it.
One mechanical detail is worth carrying across the seam, because it shows where the classification actually comes from. Default channel assignment works on exact matches — a session lands in the AI Assistant channel when its medium exactly matches ai-assistant, and lands in Direct only when the source is exactly (direct) and the medium is (none) or (not set). Google documents the automatic path as applying when the referrer matches its list of AI assistants — a list you neither own nor see. That is the structural argument for keeping a log-side series that does not depend on anyone else’s classification decisions.
Sessions that carry a referrer
A person clicked a citation and arrived in a browser that ran your tag. This is a client-side measurement problem end to end — channel definitions, UTM hygiene, engagement metrics. Server logs add nothing here that analytics does not already do better.
Training and index fetches
No browser, no tag, no session. Adding the training, training-and-search and search-only purposes together, these account for 95.7% of AI-bot requests in the current window — everything except live user actions and undeclared traffic. They exist exclusively as log lines, and no analytics configuration surfaces them, so do not spend a sprint looking for one.
ChatGPT-User and Perplexity-User
The one series that appears on both sides at different moments: the fetch lands in your log, and the click — if it happens — lands in analytics. Joining them gives you a fetch-to-visit ratio you cannot obtain from either source alone, and it is the closest thing to an AI-visibility conversion rate available today.
Policy, then economics
Start from the log so you know which agents are actually present and at what volume, then price the trade-off rather than applying a blanket rule. The purpose split is what makes this tractable: training and citation supply are independently controllable.
08 — Honest LimitsWhat nobody can honestly tell you yet.
The question every stakeholder asks first is what share of AI traffic is invisible. There is no defensible single answer, and the reason is more interesting than the missing number. Published figures do not disagree about one quantity — they measure three different quantities and present all three as if they were the same statistic.
| Figure as circulated | What it measures | Source type | Why it cannot be compared with the others |
|---|---|---|---|
| Three published figures — three different denominators | |||
| 1.08% is AI referral | AI-referred sessions divided by all web traffic in one tracker’s panel | growth-onomics.com, a third-party tracker; methodology and panel not disclosed | The denominator is the entire web, so the figure is small by construction and says nothing about invisibility |
| 12–18% is AI referral | AI-referred sessions divided by referral traffic only | upgrowth.in and theworlddata.com, different third-party trackers; also non-primary | The denominator excludes direct, organic and paid entirely — a far smaller base, so a far larger percentage |
| Roughly 60–70% arrives dark | AI traffic with no referrer divided by AI traffic itself | Our own prior estimate in the companion post — not an external primary | Measures invisibility inside AI traffic rather than AI traffic’s size; cannot be averaged with either row above |
The gap between the smallest and largest circulating figure is more than tenfold, but that spread is not evidence of a disagreement about one number. It is evidence that no one has settled which number is being asked about. None of the three is a primary source with a disclosed panel and a stated denominator, which is why this page prints all three with their denominators attached and refuses to reduce them to one.
The structural argument does not need a percentage anyway, and it holds regardless of which tracker turns out to be closest. A request that never executes JavaScript cannot reach a client-side tag. A request arriving with no referrer header cannot be attributed by referrer. Both statements are true of your site today, and your own logs will give you your own figure for your own domain — the only number that should drive a decision about your own budget.
09 — ConclusionThe measurement layer under the measurement layer.
Your analytics tells you who arrived. Your logs tell you who came looking.
The agent layer is not a rounding error on a marketing report; on the current mix it is the majority of the machine traffic reaching your pages, and it is entirely absent from the surface most teams use to make decisions. Client-side analytics was never designed to see it, and there is no configuration that changes that. The work is to add a second data source — one that reads requests rather than sessions — and to report it as its own series rather than folding it into a total.
Get the vocabulary right and most of the confusion dissolves. There is a training crawl, a search-index crawl, and a live fetch on behalf of a person asking a question, and both OpenAI and Perplexity ship all three. Those are three different business relationships with three different blocking consequences, and the moment a report calls them one thing, the decision it exists to support becomes impossible to make.
Where this heads next is reasonably legible. The user-action slice is small today, and it is the slice that grows if assistants keep shifting toward live retrieval at question time. Teams that start counting it now — with agents matched exactly, source addresses verified, and purposes reported separately — will have a baseline when it matters. Teams waiting for an industry benchmark to tell them what normal looks like will be waiting a while, because the three figures currently circulating are not even measuring the same thing.