IABIG

Guides par plateforme · Mis à jour le 2026-10-25 · Version Markdown

Robots d'IA avec Next.js et Vercel - robots.ts, X-Robots-Tag et pare-feu Vercel

Configurer robots.txt avec app/robots.ts dans Next.js, poser un en-tête X-Robots-Tag et régler le pare-feu Vercel sans bloquer ChatGPT Search ni Perplexity.

Sur un site Next.js hébergé chez Vercel, trois couches peuvent décider du sort d'un robot d'IA : le fichier robots.txt, produit par le framework, les en-têtes HTTP envoyés avec chaque page, et le pare-feu de Vercel, qui peut refuser une requête avant même qu'elle n'atteigne l'application. Elles ne se recouvrent pas : robots.txt exprime une préférence que les robots sérieux respectent, le pare-feu bloque réellement, et un réglage trop large dans ce dernier peut effacer un site de ChatGPT Search ou de Perplexity sans que le robots.txt n'y soit pour rien. Ce guide décrit chaque couche avec l'App Router de Next.js, propose une configuration qui refuse l'entraînement tout en laissant passer les robots de recherche, et indique comment vérifier puis annuler chaque changement.

Choisir une seule source pour robots.txt

Next.js accepte trois façons de servir /robots.txt :

N'en gardez qu'une. Deux sources concurrentes rendent le résultat difficile à prévoir et, surtout, à relire six mois plus tard. Le fichier statique suffit si les règles ne changent jamais ; robots.ts devient utile dès que le contenu dépend de l'environnement, par exemple pour tout interdire sur un domaine de préproduction.

Le fichier app/robots.ts

La fonction exportée renvoie un objet de type MetadataRoute.Robots. Sa propriété rules accepte un objet unique ou un tableau d'objets ; dans chaque objet, userAgent, allow et disallow acceptent une chaîne ou un tableau de chaînes. La documentation mentionne aussi crawlDelay, sitemap, host et, depuis Next.js 16.3, un champ other pour les directives non standard.

// app/robots.ts
import type { MetadataRoute } from 'next'

// Zones qu'aucun robot ne doit explorer (à adapter)
const EXCLUSIONS = ['/admin/', '/panier/']

export default function robots(): MetadataRoute.Robots {
  return {
    rules: [
      {
        // 1. Entraînement de modèles : refusé (choix éditorial)
        userAgent: [
          'GPTBot',
          'ClaudeBot',
          'CCBot',
          'meta-externalagent',
          'MistralAI-Training',
          'Bytespider',
          'Applebot-Extended',
          'Google-Extended',
        ],
        disallow: '/',
      },
      {
        // 2. Recherche et récupération à la demande : autorisé.
        //    Ces robots n'appliquent que ce groupe : on y recopie les exclusions.
        userAgent: [
          'OAI-SearchBot',
          'ChatGPT-User',
          'Claude-SearchBot',
          'Claude-User',
          'PerplexityBot',
          'Perplexity-User',
        ],
        allow: '/',
        disallow: EXCLUSIONS,
      },
      {
        // 3. Tous les autres robots (Googlebot, Bingbot, Applebot...)
        userAgent: '*',
        disallow: EXCLUSIONS,
      },
    ],
    sitemap: 'https://www.example.com/sitemap.xml',
  }
}

Avec Next.js 16.4.3, que nous avons utilisé pour produire la sortie ci-dessous, les agents d'un même objet sont regroupés dans un seul groupe (extrait) :

User-Agent: GPTBot
User-Agent: ClaudeBot
User-Agent: CCBot
...
Disallow: /

User-Agent: OAI-SearchBot
User-Agent: Claude-SearchBot
...
Allow: /
Disallow: /admin/
Disallow: /panier/

User-Agent: *
Disallow: /admin/
Disallow: /panier/

Sitemap: https://www.example.com/sitemap.xml

L'exemple de la documentation officielle montre un groupe distinct par agent. Les deux écritures sont équivalentes au regard de la RFC 9309 ; ne vous fiez donc pas à la forme exacte, mais au contenu.

Deux précisions de la documentation : robots.ts est une route spéciale mise en cache par défaut, sauf si elle utilise une API dépendant de la requête ; et le fichier doit se trouver à la racine de app, pas dans un sous-dossier. Le rôle de chaque jeton et les erreurs de syntaxe classiques sont détaillés dans notre guide sur le robots.txt qui bloque l'entraînement sans quitter les recherches IA.

L'en-tête X-Robots-Tag

X-Robots-Tag transporte dans un en-tête HTTP les mêmes consignes qu'une balise <meta name="robots">, ce qui le rend utile pour les PDF ou les réponses non HTML. Dans Next.js, on le pose avec la fonction headers() de next.config :

// next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  async headers() {
    return [
      {
        // Tous les fichiers sous /documents/ : ne pas indexer
        source: '/documents/:path*',
        headers: [{ key: 'X-Robots-Tag', value: 'noindex' }],
      },
    ]
  },
}

export default nextConfig

Gardez trois limites en tête. Google documente cet en-tête, y compris la forme ciblée X-Robots-Tag: googlebot: noindex, mais les pages officielles d'OpenAI, d'Anthropic et de Perplexity consultées pour ce guide ne mentionnent ni X-Robots-Tag ni noindex : pour ces robots, robots.txt reste la seule consigne documentée. Ensuite, un robot ne lit l'en-tête que s'il a le droit de télécharger la page : une URL interdite dans robots.txt ne révèle jamais son noindex. Enfin, Vercel ajoute déjà X-Robots-Tag: noindex aux déploiements de prévisualisation, sauf si un domaine personnalisé leur est attribué ; ne soyez pas surpris de le voir en testant une URL de preview.

Le pare-feu de Vercel

Dans le tableau de bord du projet, Firewall puis Rules affiche une section Bot Management avec deux règles gérées, disponibles sur toutes les offres et inactives par défaut :

Règle gérée Actions proposées Effet
Bot Protection Log ou Challenge Défi JavaScript pour le trafic qui ne se comporte pas comme un navigateur ; les robots vérifiés par Vercel, comme Googlebot, sont exclus de l'évaluation
AI Bots Ruleset Log ou Deny Repère les robots d'IA connus, qu'ils servent à l'entraînement, à la recherche ou aux requêtes déclenchées par un utilisateur ; la liste est tenue à jour par Vercel

Le point décisif est dans la définition de la seconde règle : elle couvre aussi la recherche et les récupérations à la demande. La passer en Deny bloque donc, selon la description de Vercel, OAI-SearchBot, PerplexityBot ou Claude-User au même titre que GPTBot, et votre site disparaît des réponses qui citent leurs sources. La documentation ne permet pas de choisir une catégorie à l'intérieur de cette règle. Nous n'avons pas trouvé non plus la liste exacte des robots qu'elle reconnaît.

Pour suivre la recommandation de ce guide (refuser l'entraînement, garder la recherche), utilisez plutôt une règle personnalisée : Add Rule, condition User Agent avec l'opérateur Matches expression, action Deny, puis Review Changes et Publish.

Règle personnalisée « Refus entraînement IA »
  Si  User Agent    Matches expression   (?i)(GPTBot|ClaudeBot|CCBot|meta-externalagent|Bytespider)
  Et  Request Path  Does not equal       /robots.txt
  Alors  Deny

La seconde condition laisse robots.txt lisible : un robot qui ne peut pas lire le fichier ne peut pas non plus y découvrir votre refus. Les opérateurs de Vercel sont documentés comme insensibles à la casse ; le préfixe (?i) ne fait que le rendre explicite. Testez la règle d'abord avec l'action Log : les requêtes correspondantes apparaissent sur la page de synthèse du pare-feu sans être bloquées.

Si vous tenez à la règle gérée en mode Deny, Vercel indique qu'une règle personnalisée avec l'action Bypass permet de laisser passer un trafic précis. Une exception fondée sur le seul User-Agent laisse toutefois passer quiconque usurpe ce nom. Vercel précise aussi que Bot Protection fonctionne mal derrière un proxy inverse placé devant le déploiement, comme un autre CDN.

Hors de Vercel : proxy.ts

Sur un serveur Node.js ou un conteneur, la logique équivalente se place dans proxy.ts, le nom que porte le middleware depuis Next.js 16. Sans matcher, il s'exécute pour toutes les requêtes, fichiers statiques compris.

// proxy.ts (à la racine du projet)
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const REFUS = /GPTBot|ClaudeBot|CCBot|meta-externalagent|Bytespider/i

export function proxy(request: NextRequest) {
  const ua = request.headers.get('user-agent') ?? ''
  if (REFUS.test(ua)) {
    return new NextResponse('Accès refusé', { status: 403 })
  }
  return NextResponse.next()
}

export const config = {
  // robots.txt et les ressources internes ne passent pas par le proxy
  matcher: ['/((?!_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml).*)'],
}

Sur Vercel, préférez la règle de pare-feu : elle s'applique avant l'application, alors que le proxy est du code exécuté pour chaque requête.

Tester

# robots.txt servi en 200 et en texte brut
curl -I https://www.example.com/robots.txt

# Simuler un robot d'entraînement : 403 attendu si la règle Deny est publiée
curl -s -o /dev/null -w '%{http_code}\n' -A "Mozilla/5.0 (compatible; GPTBot/1.4; +https://openai.com/gptbot)" https://www.example.com/

# Simuler un robot de recherche : 200 attendu
curl -s -o /dev/null -w '%{http_code}\n' -A "Mozilla/5.0 (compatible; OAI-SearchBot/1.4; +https://openai.com/searchbot)" https://www.example.com/

# En-tête X-Robots-Tag sur un document
curl -sI https://www.example.com/documents/tarifs.pdf | grep -i x-robots-tag

Ces tests sont indicatifs : curl imite le nom du robot, pas son adresse IP, et une règle qui vérifie l'origine du trafic ne le traitera pas comme le vrai robot. Le contrôle qui compte se fait ensuite dans la vue Firewall du projet, où chaque requête bloquée ou journalisée est rattachée à la règle qui l'a traitée. Pour écrire les règles robots.txt sans faute de jeton, vous pouvez partir du générateur de robots.txt pour les IA.

Pièges à éviter

Revenir en arrière

Pour robots.txt, restaurez la version précédente du fichier et redéployez ; les robots relisent le fichier dans les 24 heures environ. Pour le pare-feu, repassez AI Bots Ruleset sur Allow ou Bot Protection sur Off, désactivez ou supprimez la règle personnalisée, puis Review Changes et Publish. Pour X-Robots-Tag et proxy.ts, retirez le bloc concerné et redéployez.

Sources officielles

À lire aussi