IABIG

Robots d'IA · Mis à jour le 2026-10-25 · Version Markdown

Mesurer les visites des robots d'IA dans les logs nginx et Apache - grep, awk et GoAccess

Compter les visites de GPTBot, ClaudeBot, PerplexityBot et autres robots d'IA dans les logs nginx ou Apache, par robot et par page, avec un script complet.

Les outils d'audience fondés sur un script JavaScript ne voient que les visiteurs qui exécutent ce script. Les robots d'IA qui ne l'exécutent pas, comme CCBot selon Common Crawl, n'y apparaissent jamais. Pour savoir qui lit réellement votre site, quelles pages et à quel rythme, la source fiable reste le journal d'accès du serveur web. Ce guide part du format de log standard, donne les commandes grep, awk et GoAccess utiles, puis un script complet qui produit un résumé par robot.

Le format de log à connaître

nginx utilise par défaut le format « combined », qu'Apache fournit aussi sous le même nom. Une ligne ressemble à ceci :

20.171.206.5 - - [24/Oct/2026:10:00:01 +0200] "GET /guide/ HTTP/1.1" 200 5120 "-" "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; GPTBot/1.4; +https://openai.com/gptbot"

Dans nginx, ce format est prédéfini sous le nom combined et c'est celui qu'utilise access_log quand aucun format n'est précisé :

# Définition intégrée à nginx (inutile de la recopier)
log_format combined '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

Dans Apache, le format porte le même nom ; vérifiez que votre CustomLog l'utilise bien, et non common qui n'enregistre pas le User-Agent :

LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined
CustomLog ${APACHE_LOG_DIR}/access.log combined

Emplacements habituels : /var/log/nginx/access.log pour nginx, /var/log/apache2/access.log sur Debian et Ubuntu, /var/log/httpd/access_log sur les distributions dérivées de Red Hat. Les journaux plus anciens sont souvent compressés (access.log.2.gz) : zcat -f lit indifféremment les fichiers compressés et non compressés.

Le découpage sur le guillemet est la clé des commandes awk qui suivent. Avec awk -F'"', le champ $2 contient la requête (GET /guide/ HTTP/1.1), $3 le code HTTP et la taille, $6 le User-Agent.

Les robots à rechercher

Les jetons ci-dessous sont ceux que publient les éditeurs. Ils apparaissent dans l'en-tête User-Agent des requêtes, à l'exception de Google-Extended et Applebot-Extended, qui ne sont que des jetons robots.txt : vous ne les verrez jamais dans un journal.

Famille Jetons à chercher
Entraînement GPTBot, ClaudeBot, CCBot, meta-externalagent, MistralAI-Training, Amazonbot, Bytespider
Recherche et indexation OAI-SearchBot, Claude-SearchBot, PerplexityBot, Applebot, Amzn-SearchBot, meta-webindexer, MistralAI-Index
À la demande d'un utilisateur ChatGPT-User, Claude-User, Perplexity-User, MistralAI-User, meta-externalfetcher, Amzn-User, Google-Agent

Les numéros de version changent (GPTBot/1.4 aujourd'hui) : cherchez toujours le jeton, jamais la chaîne complète.

Commandes rapides

LOG=/var/log/nginx/access.log
BOTS='GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|Claude-SearchBot|Claude-User|PerplexityBot|Perplexity-User|CCBot|Bytespider|meta-externalagent|meta-externalfetcher|meta-webindexer|Applebot|Amazonbot|Amzn-SearchBot|Amzn-User|MistralAI-User|MistralAI-Index|MistralAI-Training|Google-Agent'

# Nombre total de requêtes de robots d'IA
grep -ciE "$BOTS" "$LOG"

# Requêtes par robot, journaux archivés compris
# (grep -o compterait aussi les URL de documentation contenues dans le User-Agent :
#  on ne garde que la première occurrence dans le champ User-Agent ; GNU awk requis)
zcat -f /var/log/nginx/access.log* \
  | awk -F'"' -v bots="$BOTS" 'BEGIN { IGNORECASE = 1 } match($6, bots) { print substr($6, RSTART, RLENGTH) }' \
  | sort | uniq -c | sort -rn

# Les 20 pages les plus lues par GPTBot (paramètres d'URL retirés)
awk -F'"' 'tolower($6) ~ /gptbot/ { split($2, r, " "); sub(/\?.*/, "", r[2]); print r[2] }' "$LOG" \
  | sort | uniq -c | sort -rn | head -20

# Codes HTTP renvoyés à ClaudeBot (repérer les 403, 404 et 5xx)
awk -F'"' '$6 ~ /ClaudeBot/ { split($3, s, " "); print s[1] }' "$LOG" | sort | uniq -c | sort -rn

# Requêtes par jour pour OAI-SearchBot
awk -F'"' '$6 ~ /OAI-SearchBot/ { print substr($1, index($1, "[") + 1, 11) }' "$LOG" | uniq -c

# Qui lit robots.txt, et avec quel code de réponse
awk -F'"' '$2 ~ /^GET \/robots\.txt/ { split($3, s, " "); print s[1], $6 }' "$LOG" \
  | grep -iE "$BOTS" | sort | uniq -c | sort -rn

# Volume transféré (octets) par robot
awk -F'"' -v bots="$BOTS" 'BEGIN { IGNORECASE = 1 }
  match($6, bots) { split($3, s, " "); o[substr($6, RSTART, RLENGTH)] += s[2] }
  END { for (b in o) printf "%12d  %s\n", o[b], b }' "$LOG" | sort -rn

Les commandes qui utilisent IGNORECASE exigent GNU awk (gawk). Avec un autre awk, retirez le bloc BEGIN et respectez la casse des jetons.

La commande sur robots.txt est utile pour contrôler vos réglages : un robot qui reçoit une erreur 5xx sur ce fichier doit, selon la RFC 9309, considérer tout le site comme interdit.

Isoler les robots d'IA dans un journal dédié

Plutôt que de filtrer a posteriori, vous pouvez demander au serveur d'écrire ces requêtes dans un fichier séparé.

Avec nginx, dans le bloc http :

map $http_user_agent $robot_ia {
    default 0;
    "~*(GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|Claude-SearchBot|Claude-User|PerplexityBot|Perplexity-User|CCBot|Bytespider|meta-external|meta-webindexer|Amazonbot|Amzn-|MistralAI-|Google-Agent|Applebot)" 1;
}

Puis dans le bloc server :

# access_log dans server remplace les directives héritées : on garde le journal principal.
access_log /var/log/nginx/access.log combined;
access_log /var/log/nginx/robots-ia.log combined if=$robot_ia;

Avec Apache (modules mod_setenvif et mod_log_config) :

SetEnvIfNoCase User-Agent "(GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|Claude-SearchBot|Claude-User|PerplexityBot|Perplexity-User|CCBot|Bytespider|meta-external|meta-webindexer|Amazonbot|Amzn-|MistralAI-|Google-Agent|Applebot)" robot_ia
CustomLog ${APACHE_LOG_DIR}/robots-ia.log combined env=robot_ia

Pensez à ajouter ce fichier à la configuration de logrotate, comme les autres journaux du serveur.

GoAccess

GoAccess analyse les journaux au format combined et produit un rapport HTML. Depuis la version 1.12 (septembre 2026), il range les robots d'IA dans une catégorie « AI Crawlers » distincte des moteurs de recherche classiques.

# Rapport complet
goaccess /var/log/nginx/access.log --log-format=COMBINED -o rapport.html

# Rapport limité aux robots d'IA, journaux compressés compris
zcat -f /var/log/nginx/access.log* \
  | grep -iE 'GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|Claude-SearchBot|Claude-User|PerplexityBot|Perplexity-User|CCBot|Bytespider|meta-external' \
  | goaccess - --log-format=COMBINED -o robots-ia.html

Avec une version plus ancienne, déclarez vous-même les jetons dans une liste fournie via --browsers-file (un mot, une ou plusieurs tabulations, puis la catégorie) :

printf '%s\t\tCrawlers\n' GPTBot OAI-SearchBot ChatGPT-User ClaudeBot Claude-SearchBot Claude-User PerplexityBot Perplexity-User > ia.list
goaccess /var/log/nginx/access.log --log-format=COMBINED --browsers-file=ia.list -o rapport.html

Si votre serveur écrit l'hôte virtuel en tête de ligne, utilisez VCOMBINED au lieu de COMBINED.

Script prêt à l'emploi

Ce script lit un ou plusieurs journaux au format combined, compressés ou non, et affiche le nombre de requêtes par robot, les codes HTTP, le nombre d'adresses IP distinctes, l'activité par jour et les pages les plus lues.

#!/usr/bin/env bash
# robots-ia-logs.sh - résumé des visites de robots d'IA (format « combined »)
# Usage : ./robots-ia-logs.sh /var/log/nginx/access.log*   (fichiers .gz acceptés)
set -euo pipefail
[ "$#" -ge 1 ] || { echo "usage : $0 fichier_log [fichier_log…]" >&2; exit 1; }

# Jetons recherchés dans le User-Agent (insensible à la casse)
BOTS="GPTBot OAI-SearchBot ChatGPT-User ClaudeBot Claude-SearchBot Claude-User PerplexityBot Perplexity-User Google-Agent Applebot Amazonbot Amzn-SearchBot Amzn-User meta-externalagent meta-externalfetcher meta-webindexer CCBot Bytespider MistralAI-User MistralAI-Index MistralAI-Training"

zcat -f -- "$@" | awk -F'"' -v bots="$BOTS" '
BEGIN { n = split(bots, B, " "); for (i = 1; i <= n; i++) L[i] = tolower(B[i]) }
{
  ua = tolower($6); bot = ""
  for (i = 1; i <= n; i++) if (index(ua, L[i])) { bot = B[i]; break }
  if (bot == "") next
  split($2, req, " "); url = req[2]; sub(/\?.*/, "", url)      # chemin sans paramètres
  split($3, st, " "); code = st[1]
  split($1, f, "["); jour = substr(f[2], 1, 11)                  # 25/Oct/2026
  split($1, h, " "); ip = h[1]
  hits[bot]++; codes[bot " " code]++; pages[bot "\t" url]++
  jours[jour " " bot]++; ips[bot " " ip] = 1
}
END {
  print "== Requêtes par robot"
  for (b in hits) printf "%8d  %s\n", hits[b], b | "sort -rn"; close("sort -rn")
  print "\n== Codes HTTP par robot"
  for (k in codes) printf "%8d  %s\n", codes[k], k | "sort -k2,2 -k1,1rn"; close("sort -k2,2 -k1,1rn")
  print "\n== Adresses IP distinctes par robot"
  for (k in ips) { split(k, a, " "); nip[a[1]]++ }
  for (b in nip) printf "%8d  %s\n", nip[b], b | "sort -rn"; close("sort -rn")
  print "\n== Requêtes par jour"
  for (k in jours) printf "%8d  %s\n", jours[k], k | "sort -k2,2 -k1,1rn"; close("sort -k2,2 -k1,1rn")
  print "\n== 20 pages les plus lues (tous robots d IA)"
  for (k in pages) { split(k, a, "\t"); tot[a[2]] += pages[k] }
  for (u in tot) printf "%8d  %s\n", tot[u], u | "sort -rn | head -20"; close("sort -rn | head -20")
}'
chmod +x robots-ia-logs.sh
./robots-ia-logs.sh /var/log/nginx/access.log /var/log/nginx/access.log.*.gz
sudo ./robots-ia-logs.sh /var/log/apache2/access.log*

Exemple de sortie sur un petit journal de test :

== Requêtes par robot
       3  GPTBot
       1  PerplexityBot
       1  ClaudeBot
       1  Claude-SearchBot

== Codes HTTP par robot
       1  Claude-SearchBot 200
       1  ClaudeBot 200
       1  GPTBot 200
       1  GPTBot 403
       1  GPTBot 404
       1  PerplexityBot 200

Le script compte ce que les requêtes déclarent. Les dates sont triées comme du texte, ce qui convient pour un mois donné ; pour une période plus longue, exportez les données vers un tableur ou une base.

Séparer les résultats robot par robot

Le résumé global mélange des robots aux rôles très différents. Pour comparer ce que lit chaque robot, une boucle suffit : elle produit un classement des pages par robot, ce qui permet de voir si un robot de recherche atteint bien vos contenus importants, ou s'il s'épuise sur des pages de filtres, de pagination ou d'archives.

for bot in GPTBot OAI-SearchBot ClaudeBot Claude-SearchBot PerplexityBot; do
  echo "== $bot"
  zcat -f /var/log/nginx/access.log* \
    | awk -F'"' -v b="$bot" 'index(tolower($6), tolower(b)) { split($2, r, " "); sub(/\?.*/, "", r[2]); print r[2] }' \
    | sort | uniq -c | sort -rn | head -10
done

Suivre l'évolution dans le temps

Les journaux sont souvent effacés après quelques semaines par logrotate. Pour garder une trace, enregistrez chaque nuit un compteur par robot dans un fichier CSV, à partir du journal de la veille (ici access.log.1, nom habituel après rotation quotidienne) :

# /etc/cron.d/robots-ia : chaque jour à 00 h 30
30 0 * * * root awk -F'"' 'BEGIN { IGNORECASE = 1; d = strftime("\%Y-\%m-\%d", systime() - 86400) } match($6, /GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|Claude-SearchBot|Claude-User|PerplexityBot|Perplexity-User|CCBot/) { n[substr($6, RSTART, RLENGTH)]++ } END { for (b in n) print d ";" b ";" n[b] }' /var/log/nginx/access.log.1 >> /var/log/robots-ia.csv

Dans un fichier cron, le caractère % doit être échappé, d'où les \%. Les fonctions strftime et systime sont, elles aussi, propres à GNU awk. Le fichier obtenu s'ouvre dans n'importe quel tableur : une colonne date, une colonne robot, une colonne nombre de requêtes.

Que regarder en priorité ? Trois signaux suffisent le plus souvent. Les robots de recherche (OAI-SearchBot, Claude-SearchBot, PerplexityBot) doivent apparaître régulièrement si vous les autorisez ; leur absence durable mérite de vérifier robots.txt et le pare-feu. Les agents à la demande (ChatGPT-User, Claude-User, Perplexity-User) montrent quelles pages sont consultées au moment où un utilisateur pose une question. Enfin, un robot que vous avez interdit dans robots.txt et qui continue de lire d'autres pages que /robots.txt doit être vérifié : soit il s'agit d'un imitateur, soit la règle ne le vise pas correctement.

Interpréter les chiffres avec prudence

Pour vérifier votre site, utilisez le scanner IABIG.

Sources officielles

Vérifiez votre site en 10 secondes avec le scanner de visibilité IA ou le détecteur d'injection de prompt, gratuits.

À lire aussi