Alle artikelen

Er kwam net een AI-agent langs op je site. Hoe weet je of die echt is?

Een User-Agent die GPTBot zegt bewijst niets. Zo verifieer je AI-agents echt, met gepubliceerde IP-ranges, reverse DNS en ASN-checks, en zo classificeer je alles wat zakt voor die test.

12 augustus 20267 min leestijd

Er is net een pagina opgehaald door een AI-agent, en in de logregel staat GPTBot/1.2. Die string is geen bewijs. Een User-Agent wordt door de client zelf geschreven, dus iedereen kan hem sturen, en de enige betrouwbare manier om een AI-agent te verifiëren is kijken naar het adres waarvandaan de verbinding kwam. Drie checks doen het echte werk: het bron-IP matchen tegen het gepubliceerde rangebestand van de operator, een forward-confirmed reverse DNS-lookup uitvoeren, of bevestigen dat het IP wordt aangekondigd door het eigen ASN van de operator.

Welke van de drie geldt, hangt volledig af van de operator, en dat is het vervelende deel. OpenAI publiceert JSON-rangebestanden per bot. Anthropic publiceert één prefixbestand dat ClaudeBot, Claude-User en Claude-SearchBot samen afdekt. Apple ondersteunt zowel reverse DNS als een rangebestand. Meta publiceert geen van beide en verwacht dat je AS32934 controleert. Een flink aantal crawlers publiceert helemaal niets, en dan is de naam in de header decoratie en beslis je op herkomst en gedrag.

De claim is gratis, de check niet

Denk na over wat een allowlist-regel als "als User-Agent GPTBot bevat, sla de botregels over" eigenlijk zegt. Hij zegt: elke client die bereid is negen tekens te typen krijgt de behandeling die je had gereserveerd voor een crawler die je vertrouwt. Scrapers lezen je robots.txt, zien welke agents welkom zijn, en kleden zich ernaar. Die imitatie is niet slim, ze is gewoon goedkoop, en ze werkt bij elke site die nooit verder kijkt dan de header.

De check kost meer. Je hebt de actuele ranges van de operator nodig, een DNS-resolver of routinggegevens, per operator, bijgewerkt zodra er iets verandert. Onderzoekers hebben al verkeer gemeld dat zich voordeed als PerplexityBot vanaf adressen buiten de gepubliceerde ranges van Perplexity. Precies dat is het faalpatroon: de naam was geleend, het netwerk niet.

Bekijk uit welk netwerk een agent-aanvraag echt kwam

Elke operator documenteert dit anders

Er is geen gedeelde standaard, dus verificatie is een lookup per operator. Een greep uit de AI-crawlergids, die 150 agents bijhoudt:

AgentOperatorWat er wordt gepubliceerdHoe je verifieert
GPTBotOpenAIopenai.com/gptbot.jsonIP-rangematch, geen reverse DNS-conventie
ClaudeBotAnthropicclaude.com/crawling/bots.jsonIP-prefixmatch, één bestand voor alle Anthropic-bots
PerplexityBotPerplexityperplexitybot.jsonIP-rangematch, spoofing al aangetoond
ApplebotAppleRangebestand plus rDNSReverse DNS onder applebot.apple.com, forward-confirmed
AmazonbotAmazonReverse DNS-conventieForward-confirmed reverse DNS, geen rangebestand
Meta-ExternalAgentMetaNiets per botBevestigen dat het IP door AS32934 wordt aangekondigd
BytespiderByteDanceNietsNiet te verifiëren, negeert robots.txt
CCBotCommon CrawlNiets bruikbaarsNiet te verifiëren, draait op gehuurde cloudcapaciteit
Google-ExtendedGoogleAlleen robots-tokenNiets te verifiëren, komt nooit voor als User-Agent

Google-Extended verdient een tweede blik, omdat het het gangbare denkmodel breekt. Het is een robots.txt-directive om je af te melden voor Gemini-training, geen crawler. Komt er een aanvraag binnen die zich Google-Extended noemt, dan is die per definitie vals. Een echte versie bestaat niet.

Van de 150 agents in de gids bieden er 33 een bruikbare check via gepubliceerde ranges, reverse DNS of een ASN. Negen staan gedocumenteerd als agents die robots.txt gewoon negeren. De rest zit ertussenin: een naam, en niets wat die onderbouwt.

Zo ziet imitatie eruit in je logs

Twee aanvragen, dezelfde header, een paar minuten uit elkaar:

198.51.100.7  "Mozilla/5.0 ...; compatible; GPTBot/1.2; +https://openai.com/gptbot"
203.0.113.44  "Mozilla/5.0 ...; compatible; GPTBot/1.2; +https://openai.com/gptbot"

Niets in de regel onderscheidt ze. Resolve de adressen en het verschil is meteen duidelijk: de ene valt binnen de gepubliceerde egress-ranges van OpenAI, de andere wordt aangekondigd door een hoster die servers per uur verhuurt. Die tweede haalde bovendien 400 productpagina's op in negentig seconden en laadde nooit een stylesheet, wat geen enkele crawler doet die zijn eigen gepubliceerde crawlbudget respecteert.

Het patroon herhaalt zich bij alle operators. Een valse ClaudeBot uit een VPS-range. Een valse Applebot met een PTR-record dat uitkomt op het domein van een hoster, of zonder PTR. Een valse Amazonbot waarvan de PTR plausibel oogt tot de forward lookup een ander adres teruggeeft, en daarvoor bestaat die bevestigingsstap. Steeds klopte de header perfect en het netwerk niet.

Geverifieerd, niet te verifiëren en vijandig zijn drie verschillende antwoorden

Zodra je de header niet meer als bewijs behandelt, valt het verkeer uiteen in groepen die om verschillende behandeling vragen, en een binaire botvlag kan dat niet uitdrukken.

Een geverifieerde AI-crawler is een beleidsbeslissing: toelaten, meten en factureren, of gericht op naam blokkeren omdat je je content niet in dat corpus wilt. Een aanvraag die een naam claimt en de IP-check niet haalt is geen beleidsbeslissing maar een vervalsing, en die mag nooit de toegang erven die de echte agent krijgt. Een crawler zonder enige publicatie valt niet te verifiëren en vraagt om rate limits in plaats van een verdict. En een headless browser uit een datacenterrange die helemaal geen botnaam claimt, is precies het geval waar je allowlist nooit naar keek.

Daarom geeft Agentscan een klasse terug in plaats van een boolean. Elke aanvraag komt terug als human, known_bot, ai_agent of malicious_automation, met een confidence-waarde en de signalen die tot dat oordeel leidden.

De signalen die een valse User-Agent overleven

De IP-herkomst beantwoordt het grootste deel van de vraag, maar niet alles. Een agent kan vanaf een residentieel adres draaien, en een scraper kan een schoon IP zonder verleden huren. Drie andere signalen houden stand als de header liegt:

JA4 TLS-fingerprint. De client verraadt hoe hij TLS spreekt voordat hij ook maar één header stuurt: ciphervolgorde, extensions, ALPN, versie. Een Python-script dat zich Chrome 124 noemt produceert een handshake die geen enkele Chrome-build ooit heeft geproduceerd. De header is een bewering, de handshake is gedrag.

Headless-sporen. De webdriver-vlag, ontbrekende browservlakken en automatiseringsmarkers van Playwright, Puppeteer of een kale HeadlessChrome-build. Afzonderlijk is elk spoor zwak. Samen met een datacenterherkomst is het beeld niet dubbelzinnig.

Headerconsistentie. Echte browsers sturen Accept, Accept-Language en Accept-Encoding als set, in een stabiele volgorde, passend bij de versie die ze claimen. Zelfgebouwde request-objecten sturen meestal een deelverzameling, en die gaten zijn consistent genoeg om als signaal te dienen.

Geen van deze signalen beslist alleen, en dat is precies het punt. Pas de combinatie scheidt een geverifieerde crawler van een bekwame imitator, en beide van een gewone bezoeker. Dezelfde logica geldt voor oudere crawlers, beschreven in hoe je Googlebot verifieert.

Een beleid dat het contact met echt verkeer overleeft

Verificatie is alleen nuttig als er daarna iets gebeurt. Een werkbare standaard:

  1. Toets de claim met de methode van de operator voordat er ook maar één allowlist-regel afgaat. Rangebestand, reverse DNS of ASN, net wat die operator publiceert.
  2. Een mislukte verificatie is geen zacht signaal. Een aanvraag die zich ClaudeBot noemt en van buiten de prefixes van Anthropic komt, is imitatie. Behandel die als elke andere ongeïdentificeerde scraper, niet als een misschien.
  3. Splits de toelatingsbeslissing naar doel. Trainingscrawlers, AI-zoekcrawlers en assistent-fetchers verdienen eigen regels. Een zoekcrawler blokkeren haalt je uit de antwoorden die die engine citeert, en dat is een verkeersbeslissing, geen beveiligingsbeslissing.
  4. Zet rate limits op het niet-verifieerbare. Bij Bytespider, CCBot en al het andere zonder gepubliceerde check zegt de naam niets. Aanvraagsnelheid, padpatroon en herkomst zeggen des te meer.
  5. Log de klasse, niet alleen de blokkade. Weten dat het AI-agentverkeer vorige maand verdrievoudigde is meer waard dan een teller van geblokkeerde aanvragen, zeker als je nog moet bepalen wat je gaat verzilveren.

Agentscan doet dit met één POST naar /v1/agentscan/check: via reverse DNS geverifieerde allowlist, matching op gepubliceerde ranges, IP-herkomst, headless-vlaggen en JA4 in één aanroep, gecached zodat herhaalde lookups binnen 50ms terugkomen. Snel genoeg voor middleware in plaats van een nachtelijke logronde.

Kort samengevat

Je kunt een AI-agent niet verifiëren op basis van hoe hij zichzelf noemt. Toets het bron-IP tegen wat de operator daadwerkelijk publiceert, behandel een mislukte check als vervalsing, en gebruik JA4, headless-sporen en IP-herkomst voor alles waar de allowlist niet voor kan instaan. De vraag was nooit "is dit een bot". De vraag is welke van de vier soorten verkeer er net binnenkwam, en wat je met elk daarvan wilt doen.

FAQ

Veelgestelde vragen

Controleer het bron-IP, nooit de User-Agent. Afhankelijk van de operator betekent dat: het IP matchen tegen een gepubliceerd rangebestand (OpenAI publiceert gptbot.json, Anthropic één prefixbestand voor al zijn bots), een forward-confirmed reverse DNS-lookup uitvoeren (Applebot, Amazonbot), of bevestigen dat het adres wordt aangekondigd door het ASN van de operator (Meta gebruikt AS32934). Publiceert een operator geen van die drie, dan valt de claim helemaal niet te verifiëren.

Gerelateerde artikelen