Een access log lezen in 30 minuten: wat de AI-bots echt op je site doen
Geen enkel dashboard vertelt een uitgever of ChatGPT, Claude of Perplexity zijn pagina's lezen. Search Console dekt alleen Google, en de generatieve zoekmachines leveren niets. De enige bron die bestaat, is het logboek van de verzoeken aan de server, de access log, die elke hosting aanmaakt en die vrijwel niemand openslaat. In een half uur lees je daarin, met de tools die op elke Linux-server staan, wie er langskomt, waarvoor, en wat de server heeft geantwoord. Dit is de methode die wij toepassen, stap voor stap, met de commando's. Zo valt dat half uur uiteen: vijf minuten voor de extractie, vijf voor het tellen per bot, tien voor het controleren van de adressen, vijf voor de statuscodes, vijf voor de paden en de frequentie.
Alle onderstaande commando's gaan uit van een log in combined-formaat en draaien op het bestand dat bij stap 1 is aangemaakt. Ze gebruiken alleen grep, awk, sort en uniq.
Wat er in één regel staat
Een access log in het combined-formaat, het standaardformaat van Apache en nginx, bevat één regel per verzoek. Elke regel geeft, op volgorde: het IP-adres van de client, de datum en het tijdstip, de gevraagde methode en het gevraagde pad, de statuscode, de verzonden omvang, de referer en de user-agent. Voor de analyse van bots tellen vier velden:
| Veld | Waar het voor dient |
|---|---|
| IP-adres | Nagaan of de bot wel is wie hij beweert te zijn |
| Pad | Weten welke pagina's worden gelezen, en welke nooit |
| Statuscode | Weten of de bot de pagina heeft gekregen, een redirect, een fout of een weigering |
| User-agent | De familie en de rol van de bot vaststellen |
Eerste controle, vóór elke analyse: is het geregistreerde IP-adres dat van de bezoeker, of dat van een tussenschakel? Achter een CDN of een proxy ziet de server het adres van die tussenschakel, tenzij hij is ingesteld om de header te lezen die het oorspronkelijke adres meedraagt. Lijken alle verzoeken van een paar tientallen adressen te komen, dan is de verificatiestap die verderop wordt beschreven niet mogelijk en moet je eerst de configuratie herstellen. Die valkuil hebben we beschreven in "Wat de firewall voor je logs verbergt".
Stap 1: de periode en de AI-bots isoleren
Je werkt op een volledige periode, minimaal zeven dagen, omdat de AI-bots niet elke dag langskomen en conclusies uit vierentwintig uur vrijwel altijd onjuist zijn. Vervolgens haal je de regels eruit waarvan de user-agent een van de tokens bevat die de aanbieders publiceren: GPTBot, ChatGPT-User, OAI-SearchBot voor OpenAI; ClaudeBot, Claude-User, Claude-SearchBot voor Anthropic; PerplexityBot en Perplexity-User voor Perplexity; Google-Extended verschijnt niet in de logs, want dat is een token voor robots.txt en geen bot. De referentielijsten zijn die van OpenAI, van Anthropic en van Perplexity.
Een grep-filter op die tokens, weggeschreven naar een werkbestand, volstaat. Op een medium van gemiddelde omvang houd je enkele honderden tot enkele duizenden regels per week over.
zcat -f access.log* \
| grep -E 'GPTBot|ChatGPT-User|OAI-SearchBot|ClaudeBot|Claude-User|Claude-SearchBot|PerplexityBot|Perplexity-User' \
> ia.log
Stap 2: tellen per bot en per rol
Elke aanbieder zet meerdere bots in die niet hetzelfde doen, en dat is het nuttigste onderscheid van de hele analyse. Een trainingsbot verzamelt pagina's voor toekomstige modellen. Een zoekbot bouwt een index om vragen te beantwoorden. Een bot op aanvraag haalt een pagina op voor iemand die de assistent een vraag stelt. De rollen en de tokens hebben we uitgewerkt in "robots.txt en AI".
Je telt de regels dus per user-agent en groepeert ze in drie kolommen: training, zoeken, gebruikersagent. Het resultaat lees je zo:
- Alleen verzoeken voor training betekent dat de site wordt verzameld, maar niets zegt dat hij wordt geciteerd.
- Verzoeken voor zoeken geven aan dat de site door de generatieve zoekmachine is geïndexeerd, een noodzakelijke voorwaarde om geciteerd te worden.
- Verzoeken van een gebruikersagent zijn het waardevolste signaal: elk daarvan hoort bij een vraag van een gebruiker waarvoor de assistent deze pagina is gaan ophalen.
grep -oE 'GPTBot|ChatGPT-User|OAI-SearchBot|ClaudeBot|Claude-User|Claude-SearchBot|PerplexityBot|Perplexity-User' ia.log \
| sort | uniq -c | sort -rn
Stap 3: nagaan of de bots echt zijn
Een user-agent geef je vrij op. Elk script kan zich als GPTBot voordoen. Voordat je telt, ga je na of de IP-adressen tot de ranges behoren die elke aanbieder publiceert: OpenAI, Anthropic en Perplexity publiceren bestanden met ranges, en Google documenteert de verificatie van zijn bots via reverse DNS. Je haalt de verschillende adressen per user-agent eruit, vergelijkt ze met de ranges, en zet opzij wat er niet in staat. De volledige methode, met haar valkuilen, staat in "Is een bot wel wie hij beweert te zijn?".
Op de meeste sites die wij doorlichten vinden we ze: verzoeken die zich als Googlebot of GPTBot aandienen vanaf adressen die noch van Google noch van OpenAI zijn. We hebben er geen publiceerbaar volumecijfer bij; de test daarentegen kost één commando. Tellen zonder te verifiëren betekent dat je het risico loopt vermomde scrapers te meten.
# de verschillende adressen van een bot, te vergelijken met de gepubliceerde ranges
awk '/GPTBot/ {print $1}' ia.log | sort -u
Stap 4: de statuscodes lezen
Voor elke echte bot splits je de statuscodes uit. De tabel die je op een gezonde site verwacht, staat vrijwel helemaal op 200, met een paar 304's als de server conditionele verzoeken afhandelt, en een paar 404's op verwijderde URL's. Drie afwijkingen vragen om actie:
| Waarneming | Waarschijnlijke oorzaak | Actie |
|---|---|---|
| Veel 301 of 302 | De bot volgt oude URL's, of de sitemap geeft niet-canonieke adressen op | De sitemap corrigeren, alleen de definitieve URL's opgeven |
| 403 of 429 bij een echte bot | Een firewallregel of een regel voor snelheidsbeperking weigert hem | De regel herlezen: je weigert een bot die je de site zou willen zien citeren |
| Een 200 op robots.txt en verder niets | De bot leest robots.txt en vindt er geen enkele URL om te bezoeken, of alles is er verboden | Robots.txt token voor token nakijken |
Nog iets over weigeringen: een 403 die door de origin server wordt teruggegeven, staat in de log. Een 403 die het CDN teruggeeft, staat er niet in, want het verzoek is er nooit aangekomen. Een log zonder enige fout is dus geen bewijs dat er geen verzoek wordt geweigerd.
# de statuscode is het 9e veld van het combined-formaat
awk '/GPTBot/ {print $9}' ia.log | sort | uniq -c | sort -rn
Stap 5: kijken welke pagina's worden gelezen
Veel uitgevers stoppen vóór deze stap, terwijl die het meeste leert. Je telt voor elke zoekbot en elke gebruikersagent de gevraagde paden, en je legt ze naast de lijst met gepubliceerde pagina's. Die telling beantwoordt vragen die geen enkele andere tool beslecht:
- Welke pagina's worden nooit gelezen? Een artikel dat al twee maanden online staat en dat geen enkele zoekbot heeft opgevraagd, staat waarschijnlijk niet in de sitemap of is via geen enkele link bereikbaar.
- Welke pagina's worden door de gebruikersagenten gelezen? Dat zijn de pagina's die de assistenten werkelijk citeren. De vergelijking met de pagina's die je geciteerd zou willen zien, laat zien waar je je schrijfinspanning moet leggen.
- Leest de bot gewijzigde pagina's opnieuw? Als een pagina die vorige week is bijgewerkt niet opnieuw is opgevraagd, komt het actualiteitssignaal niet door. Waarom dat zo is, hebben we uitgelegd in "Een sitemap met identieke datums heeft geen nut".
# de meest gelezen paden van de zoekbot, 7e veld
awk '/OAI-SearchBot/ {print $7}' ia.log | sort | uniq -c | sort -rn | head -30
Stap 6: de frequentie noteren en de meting herhalen
De laatste lezing gaat over de tijd. Voor elke bot noteer je het aantal bezoekdagen in de periode, het interval tussen twee passages en het gebruikelijke tijdstip. Een zoekbot die elke dag langskomt, heeft de site in zijn cyclus opgenomen; een bot die één keer is langsgekomen om alles te lezen en daarna alleen nog terugkeerde voor de sitemap, wacht op een signaal dat er iets is veranderd. Die frequentie is het ijkpunt waarmee je de volgende metingen vergelijkt, één keer per week of één keer per maand, afhankelijk van het publicatieritme.
Bewaar ook het werkbestand. De bewaartermijn hangt af van de hostingprovider en van het contract, en telt vaker in dagen dan in maanden: ga de uwe na voordat u hem nodig hebt. Daarna bestaat het gegeven niet meer, en een vraag die je in december stelt over wat er in september is gebeurd, blijft onbeantwoord.
# bezoekdagen, daarna het gebruikelijke uur
awk '/GPTBot/ {print substr($4,2,11)}' ia.log | sort -u
awk '/GPTBot/ {print substr($4,14,2)}' ia.log | sort | uniq -c
De drie fouten die je de eerste keer vrijwel altijd maakt
De eerste is een conclusie trekken uit een steekproef: de laatste uren, de eerste pagina van een tool, één enkele dag. Een bot kan op een dinsdagavond de hele site hebben gelezen en daarna acht dagen niet meer opduiken. Je kijkt naar de hele periode voordat je zegt dat hij niet langskomt.
De tweede is testen vanaf je eigen werkplek. Een verzoek sturen met de user-agent van GPTBot vanaf een kantoornetwerk en een 403 terugkrijgen, bewijst alleen dat de spoofers worden geweigerd; de echte bot kan er wel doorheen komen. De enige test die iets waard is, is de vergelijking tussen wat de log voor de echte bot laat zien en wat je zelf terugkrijgt.
De derde is vergeten wat nooit tot de log komt. Een site achter een CDN dat de AI-bots weigert, heeft een volmaakt schone access log waarin geen enkele AI voorkomt. Het lezen van de logs begint met het lezen van de configuratie van het CDN, en met de logs van het CDN zelf wanneer die toegankelijk zijn.
Wat dat half uur oplevert
Aan het eind van de oefening weet een uitgever drie dingen die geen enkel dashboard hem geeft: welke generatieve zoekmachines zijn site indexeren, welke pagina's ze lezen, en of er echte mensen via een assistent binnenkomen. Dat is de basis van elke strategie voor zichtbaarheid in de AI's, nog vóór de metingen van de share of voice die we beschrijven in "Je zichtbaarheid in de AI's meten". Een redactioneel platform kan die lezing automatiseren en elke dag tonen; maar de eerste keer moet je haar met de hand hebben gedaan, om te weten waar je naar kijkt.
Geschreven door
Yann Léguillon
Voorzitter van Mazette, de uitgever van Joornal. Hij begeleidt bedrijven die hun eigen medium opbouwen: redactionele structuur, zichtbaarheid in zoekmachines en in AI-antwoorden, productie op schaal.