GEO technique · Agent-ready Apache

Site agent-ready : servir du Markdown aux crawlers IA depuis Apache, gratuitement

De 50 à 100/100 sur l'audit Cloudflare AI Compatibility, niveau Level 5 Agent-Native, en 30 lignes de configuration et zéro abonnement CDN.

Frédéric Jézégou 28 min de lecture Lire la version .md
100
Score Cloudflare AI Compatibility
+50
Gain total sur l'audit (50→100)
Level 5
Classification Agent-Native
240 $
Économie annuelle minimum
30
Lignes de configuration suffisent

Pourquoi servir du Markdown aux crawlers IA

3 min

Pendant vingt ans, les pages web ont été optimisées pour deux audiences : les humains et les robots Google. La première lit avec ses yeux, la seconde rampe avec un parser HTML qui comprend très bien la mise en page. Tout le monde y trouvait son compte.

Les LLM rebattent les cartes. Quand ClaudeBot ou GPTBot aspire une page pour répondre à un utilisateur, il ne fait pas un rendu visuel. Il convertit en interne le HTML brut en représentation textuelle exploitable, puis transmet cette représentation au modèle qui consomme des tokens à chaque caractère. Le HTML est, pour cet usage, un format bavard et bruité, alors que le Markdown a été pensé dès l'origine pour être lisible aussi bien par un humain que par une machine, sans aucune mise en page parasite.

Le coût caché du HTML pour un agent IA

Prenons un cas concret. Une page d'article rankit.fr fait environ 180 Ko de HTML avec son CSS inline, ses balises ARIA, ses scripts, ses meta og:, ses JSON-LD. La même page convertie en Markdown propre fait environ 18 Ko. Soit dix fois moins de matière à traiter.

Pour le modèle, cette différence se traduit en tokens consommés. Sur la page citée :

Format serviTailleTokens estimésBruit non-sémantique
HTML brut180 Ko≈ 45 000Élevé (CSS, scripts, ARIA)
Markdown converti18 Ko≈ 4 500Quasi nul
Réduction−90 %−90 %−
Mesure réalisée sur l'article livre-blanc-geo-greenred-2026, avril 2026.

Une réduction de quatre-vingt-dix pour cent du contexte consommé n'est pas un détail. Pour un agent qui doit synthétiser dix sources avant de répondre, cela peut être la différence entre tenir dans la fenêtre de contexte et tronquer arbitrairement les sources. Et un agent qui tronque, c'est un agent qui cite mal, ou ne cite pas du tout.

Un signal de qualité pour l'écosystème

Au-delà de l'économie de tokens, servir une version Markdown négociée envoie trois signaux convergents aux développeurs d'agents et aux moteurs IA.

Premier signal : maturité technique. Le site comprend les contraintes des consommateurs IA, sait configurer la content negotiation HTTP, et accepte de produire deux représentations cohérentes du même contenu. C'est un marqueur d'attention rare en 2026.

Deuxième signal : intention d'être lu. Servir du Markdown explicite, c'est dire aux LLM oui, vous pouvez consommer ce contenu, voici la version qui vous convient. À l'inverse, un site qui répond uniquement en HTML et qui place un robots.txt restrictif communique l'inverse. Les agents qui doivent choisir entre dix sources favoriseront mécaniquement les sites du premier groupe.

Troisième signal : durabilité. Cloudflare a publié sa spec en février 2026, OpenAI et Anthropic l'ont adoptée dans la foulée. Le mouvement est lancé. Servir du Markdown aujourd'hui, c'est se positionner avant que cela devienne un standard implicite, comme l'a été la balise <meta name="description"> il y a vingt ans.

La mécanique HTTP : Accept et Content-Type

Cette mécanique de négociation de contenu existe depuis longtemps : elle est standardisée par l'IETF dans la RFC 7231, ratifiée en 1999. Le principe : le client indique ses préférences via l'en-tête Accept, le serveur répond avec le format le plus approprié et signale son choix via Content-Type.

Concrètement, quand un navigateur visite votre site, il envoie typiquement :

Requête navigateur (humain)
GET /mon-article HTTP/1.1
Host: www.rankit.fr
Accept: text/html,application/xhtml+xml,...

Le serveur répond en HTML, business as usual. Mais quand un crawler IA moderne visite la même URL, il peut désormais envoyer :

Requête crawler IA
GET /mon-article HTTP/1.1
Host: www.rankit.fr
Accept: text/markdown
User-Agent: ClaudeBot/1.0 (+claude.ai/bot)

Si le serveur sait répondre, il sert un fichier Markdown avec Content-Type: text/markdown; charset=utf-8. Sinon, il sert du HTML par défaut, ce qui marche aussi mais consomme dix fois plus de tokens chez le crawler. La différence entre les deux comportements, c'est exactement ce que mesure l'audit Cloudflare AI Compatibility, et c'est exactement ce qu'on va activer en .htaccess dans la suite de cet article.

L'audit Cloudflare AI Compatibility : l'état initial

2 min

Cloudflare a mis en ligne au printemps 2026 un audit gratuit baptisé AI Compatibility Audit. L'outil bombarde l'URL fournie d'une série de tests calibrés pour mesurer si un site est fait pour les agents IA. Pas pour Google, pas pour les humains. Pour les agents.

Les tests couvrent dix-sept points répartis en cinq catégories : Discoverability, Content Accessibility, Bot Access Control, API, Auth, MCP, et Commerce. Tous les tests ne sont pas activés sur tous les sites, c'est ce que Cloudflare appelle un partial scan. Pour rankit.fr, qui est un site éditorial sans API publique ni paiement intégré, je n'ai sélectionné que 7 checks pertinents sur les 17 disponibles.

Les 17 checks Cloudflare et les 7 retenus pour rankit.fr

Voici la cartographie complète des checks de l'audit, qui sera utile pour comprendre la suite. Sur rankit.fr, j'ai activé les sept checks de la moitié haute du tableau (Discoverability, Content, Bot Access Control), et désactivé les dix de la moitié basse (API/Auth/MCP, Commerce) qui ne s'appliquent pas à un site éditorial.

CatégorieCheckActivéCible
DiscoverabilityrobotsTxt✓Présence et validité du fichier robots.txt
sitemap✓Présence d'un sitemap XML déclaré
linkHeaders✓En-têtes HTTP Link RFC 8288 sur la home
Content AccessibilitymarkdownNegotiation✓Réponse à Accept: text/markdown
Bot Access ControlrobotsTxtAiRules✓Règles d'accès pour les crawlers IA
contentSignals✓Directive Content-Signal dans robots.txt
webBotAuth✓Mécanisme d'authentification des bots
API / Auth / MCPapiCatalog−Fichier /.well-known/api-catalog (RFC 9727)
oauthDiscovery−Endpoints OAuth 2.0 discoverable
oauthProtectedResource−Métadonnées OAuth Protected Resource
mcpServerCard−Carte serveur MCP (Model Context Protocol)
a2aAgentCard−Carte agent A2A (Agent-to-Agent)
agentSkills−Skills agents documentés
webMcp−Endpoint WebMCP standardisé
Commercex402−Paiement HTTP 402 (Payment Required)
mpp−Machine Payment Protocol
ucp−Universal Commerce Protocol
acp−Agentic Commerce Protocol
Cartographie complète des 17 checks de l'audit Cloudflare AI Compatibility. Les 7 checks activés sur rankit.fr sont surlignés. Les 10 autres concernent des configurations API, OAuth, MCP et e-commerce hors périmètre d'un site éditorial.

Le périmètre de 7 checks que j'ai retenu correspond à ce qu'on peut appeler le « noyau dur agent-ready » : les éléments de configuration que tout site web devrait avoir, qu'il soit éditorial, marchand, ou applicatif. Pour les éditeurs SaaS qui exposent une API publique, l'extension naturelle consisterait à activer aussi apiCatalog (RFC 9727) et oauthDiscovery. Pour un site e-commerce qui implémente Stripe ou similaire, les checks Commerce deviennent pertinents le jour où ces protocoles HTTP 402-based seront supportés par les processeurs de paiement, ce qui n'est pas encore le cas en 2026.

L'URL exacte qui reproduit la mesure faite sur rankit.fr est :

URL canonique de l'audit rankit.fr (7 checks)
https://isitagentready.com/www.rankit.fr?checks=robotsTxt,
sitemap,linkHeaders,markdownNegotiation,robotsTxtAiRules,
contentSignals,webBotAuth
Testez votre propre site en 30 secondes L'audit Cloudflare est public et gratuit. Allez sur isitagentready.com, entrez votre domaine, sélectionnez les checks pertinents pour votre profil de site, et lancez le scan. Pour reproduire exactement la mesure faite ici sur rankit.fr (7 checks pertinents pour un site éditorial), le lien direct est isitagentready.com/www.rankit.fr (7 checks). Le test prend moins d'une seconde et donne un verdict immédiat sur les en-têtes HTTP servis par votre site et la conformité de votre robots.txt.

Résultat initial sur rankit.fr

Avant toute intervention, l'audit donne un verdict mitigé. Le score global est de 50/100, ce qui place rankit.fr au niveau Level 1 : Basic Web Presence. Le détail qui nous intéresse est dans la section Content, qui mesure spécifiquement la négociation Markdown :

IndicateurValeur avantVerdict Cloudflare
Score global50/100Level 1 : Basic Web Presence
Section Content0/1Markdown negotiation : échec
Test exécutéGET /Avec en-tête Accept: text/markdown
Réponse serveurtext/htmlAu lieu de text/markdown attendu
Durée du scan478 msPartial scan, profil Content
Audit Cloudflare AI Compatibility de www.rankit.fr, état du 27 avril 2026 avant déploiement de la négociation Markdown.

Le seul indicateur que je tiens pour parfaitement certain dans ces résultats, c'est le score global de 50 et l'échec Content 0/1. Cloudflare expose aussi des sous-scores par catégorie (Discoverability, Bot Access Control), mais leur calcul exact dépend de pondérations internes que la documentation publique ne détaille pas. Je préfère donc me concentrer sur ce qui est vérifiable et sur ce qui change après l'intervention, plutôt que d'inventer une décomposition par catégorie qui pourrait induire en erreur.

Le test précis : ce que fait Cloudflare

L'audit déroule un test extrêmement simple sur la racine du site :

Test exécuté par l'audit (478ms)
# Requête envoyée par Cloudflare
GET / HTTP/2
Host: www.rankit.fr
Accept: text/markdown

# Réponse attendue : text/markdown
# Réponse obtenue avant intervention :
HTTP/2 200
content-type: text/html; charset=UTF-8

# Verdict Cloudflare :
# « Site does not support Markdown for Agents »

Tout est dans la troisième ligne de la réponse. Le serveur ignore complètement la préférence du client et sert du HTML, alors qu'on lui a explicitement demandé du Markdown. Pour Cloudflare, c'est un échec sec, et le score Content tombe à zéro.

À noter L'audit Cloudflare est volontairement strict. Il ne se contente pas d'un fallback HTML : il attend explicitement un Content-Type commençant par text/markdown. C'est une bonne chose car la mécanique HTTP est claire : si le serveur ne sait pas répondre dans le format demandé, il devrait renvoyer un code 406 Not Acceptable ou se conformer. Servir du HTML en faisant semblant d'avoir compris est, techniquement, une non-conformité.

Bonne nouvelle : ce verdict, ce score 0/1, sont entièrement réversibles avec une configuration Apache élémentaire. C'est ce qu'on va voir maintenant, après un détour par la solution officielle Cloudflare et son tarif.

La solution Cloudflare officielle, et son tarif

3 min

La documentation officielle Cloudflare décrit Markdown for Agents en termes très clairs. Quand un crawler IA envoie Accept: text/markdown sur une zone éligible, le réseau Cloudflare intercepte la requête, fetch le HTML d'origine, le convertit en Markdown via un convertisseur interne, et sert directement la version Markdown au client. L'origine ne change pas, le travail est fait au niveau du CDN.

L'élégance du dispositif est réelle. Pas de conversion à programmer, pas de fichier .md à créer, pas de cohérence à maintenir entre HTML et Markdown : tout est calculé à la volée par Cloudflare. La feature ajoute même un en-tête bonus, x-markdown-tokens, qui indique au client le nombre estimé de tokens contenus dans la version Markdown servie.

Autre raffinement à connaître : Cloudflare ajoute systématiquement un en-tête de réponse content-signal: ai-train=yes, search=yes, ai-input=yes dans les conversions Markdown for Agents. Cet en-tête est cohérent avec la directive du même nom qui existe désormais dans robots.txt via le framework contentsignals.org. À retenir : la directive Content-Signal dans robots.txt est le mécanisme principal et standardisé, l'en-tête HTTP n'en est que la confirmation par requête servie. On reviendra sur ce point dans la section bonus consacrée aux signaux.

Comment activer Markdown for Agents chez Cloudflare

Trois voies sont possibles : interface dashboard, API, ou règles de configuration sur des sous-domaines spécifiques. La voie dashboard est la plus simple :

  1. Se connecter au dashboard Cloudflare avec un compte qui héberge une zone plan Pro ou supérieur
  2. Sélectionner la zone, naviguer dans AI Crawl Control
  3. Activer le toggle Markdown for Agents
  4. Attendre quelques minutes la propagation

Pour les SaaS multi-tenant qui utilisent Cloudflare for SaaS avec des hostnames personnalisés, une activation par règle de configuration est possible, mais elle requiert un advanced subscription avec accès aux custom metadata, qui est lui-même un palier Enterprise.

Le tarif réel de la fonctionnalité

C'est ici que le bât blesse. La documentation Cloudflare est limpide :

Documentation Cloudflare officielle Markdown for Agents is available to Pro, Business and Enterprise plans, and SSL for SaaS customers at no cost.

Trois mots font toute la différence : Pro, Business and Enterprise. Le plan Free, qui héberge la grande majorité des sites web protégés par Cloudflare dans le monde, est explicitement exclu. La précision at no cost en fin de phrase signifie sans surcoût par rapport au plan, pas gratuit. Pour bénéficier de Markdown for Agents, il faut basculer son site sur un plan payant.

Les tarifs publics de Cloudflare en avril 2026 (sources : documentation Cloudflare, comparatifs Vendr et CheckThat) :

Free
0 $
par mois
Markdown for Agents : indisponible. CDN, DNS, HTTPS gratuits seulement.
Pro
20 $
par mois (annuel)
240 $/an minimum. WAF, image optimization, et Markdown for Agents activable.
Business
200 $
par mois (annuel)
2 400 $/an. Custom rules WAF, support prioritaire, Markdown for Agents.
Enterprise
2 000+ $
par mois (custom)
24 000+ $/an. Solutions engineer dédié, SLA, Markdown for Agents.

Le plan Pro mensuel coûte 25 $ au lieu de 20 $ en annuel, soit 300 $/an. Pour un site éditorial moyen, qui n'a pas besoin du WAF avancé, ni des Image Resize, ni des features Pro, payer 240 à 300 $ par an pour activer une seule case à cocher est économiquement absurde. Surtout quand cette case se réplique en .htaccess.

Dans quels cas la solution Cloudflare reste pertinente

Soyons honnêtes : Cloudflare Pro a des avantages réels qui peuvent justifier le tarif si on en a besoin. Pour un site qui combine plusieurs des facteurs suivants, l'abonnement se rentabilise :

  • Trafic mondial massif avec besoin de cache CDN avancé sur 330+ POP
  • Surface d'attaque importante nécessitant WAF managé OWASP, bot management, exposed credentials check
  • Optimisation images automatique (Polish, Mirage) sur sites e-commerce ou médias
  • Volume de pages très élevé où la conversion HTML→Markdown manuelle deviendrait industriellement coûteuse
  • Architecture totalement statique où on n'a pas la main sur la couche serveur (hébergement Pages, Vercel, Netlify)

Pour un site éditorial standard hébergé sur Apache ou LiteSpeed mutualisé, qui contrôle son .htaccess et qui ajoute ses fichiers .md au fur et à mesure, ces avantages ne s'appliquent pas. Et 240 $/an, c'est le prix d'un excellent dîner ou d'un mois d'outils SEO chez un confrère. Ce n'est pas le prix d'un toggle de configuration HTTP.

Voyons donc comment activer la même fonctionnalité, à zéro euro, sur tout serveur Apache ou LiteSpeed.

La solution Apache en .htaccess, ligne par ligne

6 min

L'objectif est simple. Quand un crawler IA visite https://www.rankit.fr/mon-article avec l'en-tête Accept: text/markdown :

  1. Le serveur détecte la préférence Markdown du client
  2. Il vérifie qu'un fichier mon-article.md existe à la racine
  3. Si oui, il le sert avec Content-Type: text/markdown; charset=utf-8
  4. Sinon, il sert le HTML normal sans erreur
  5. Dans tous les cas, il ajoute Vary: Accept pour informer les caches

Cette logique s'écrit en trois blocs .htaccess distincts, qu'on va construire et expliquer un par un avant de les assembler.

Bloc 1 : la règle de réécriture mod_rewrite

C'est le cœur du dispositif. Trois cas doivent être couverts pour gérer toutes les formes d'URL d'un site moderne : la racine /, les URL propres sans extension de type /mon-slug, et les URL avec extension HTML explicite de type /mon-slug.html.

.htaccess : Bloc 1 : réécriture conditionnelle
# ================================================
# CONTENT NEGOTIATION MARKDOWN (crawlers IA)
# ================================================
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /

  # Cas 1 : racine "/" avec Accept: text/markdown
  RewriteCond %{HTTP_ACCEPT} text/markdown [NC]
  RewriteCond %{REQUEST_URI} ^/?$
  RewriteCond %{DOCUMENT_ROOT}/index.md -f
  RewriteRule ^$ /index.md [L,E=IS_MARKDOWN:1]

  # Cas 2 : URL /slug (sans extension)
  RewriteCond %{HTTP_ACCEPT} text/markdown [NC]
  RewriteCond %{REQUEST_URI} ^/([^.]+)$
  RewriteCond %{DOCUMENT_ROOT}/%1.md -f
  RewriteRule ^([^.]+)$ /$1.md [L,E=IS_MARKDOWN:1]

  # Cas 3 : URL /slug.html avec extension
  RewriteCond %{HTTP_ACCEPT} text/markdown [NC]
  RewriteCond %{REQUEST_URI} ^/(.+)\.html$
  RewriteCond %{DOCUMENT_ROOT}/%1.md -f
  RewriteRule ^(.+)\.html$ /$1.md [L,E=IS_MARKDOWN:1]
</IfModule>

Décortiquons les directives utilisées, parce que chacune compte.

RewriteCond %{HTTP_ACCEPT} text/markdown [NC] : c'est la condition sentinelle. Le flag [NC] rend la comparaison insensible à la casse, ce qui est conforme à la RFC 7231 sur les types de média. La condition se déclenche dès que la chaîne text/markdown apparaît dans l'en-tête Accept du client, même au milieu d'une liste comme text/html, text/markdown, */*.

RewriteCond %{REQUEST_URI} ^/?$ : on cible la racine. Le pattern accepte aussi bien / que /index.html selon la configuration du serveur. Pour les Cas 2 et 3, la regex capture le slug dans une backreference numérotée %1 exploitée à la condition suivante.

RewriteCond %{DOCUMENT_ROOT}/%1.md -f : c'est la condition critique. On vérifie l'existence physique du fichier .md jumeau sur le disque, via le test -f (regular file). Si le fichier n'existe pas, la règle ne s'applique pas, et le serveur tombe en fallback sur le HTML standard. Aucune erreur 404, aucune cassure : la dégradation est gracieuse.

RewriteRule ... [L,E=IS_MARKDOWN:1] : l'action. Le flag [L] stoppe le traitement après cette règle (Last). Le flag [E=IS_MARKDOWN:1] définit une variable d'environnement qui sera lue par le bloc 2 pour appliquer le bon Content-Type. Cette variable est le pont entre mod_rewrite et mod_headers.

Bloc 2 : les en-têtes HTTP de réponse

Une fois la réécriture faite, il faut s'assurer que la réponse part avec les bons en-têtes. Trois en-têtes sont indispensables, plus un en-tête optionnel mais recommandé.

.htaccess : Bloc 2 : en-têtes HTTP
# Headers pour les .md servis via négociation
<IfModule mod_headers.c>
  Header set Content-Type "text/markdown; charset=utf-8" env=IS_MARKDOWN
  Header set X-Content-Negotiation "markdown-served" env=IS_MARKDOWN

  # Vary: Accept critique pour caches CDN (LiteSpeed, Cloudflare)
  <FilesMatch "\.(html|md)$">
    Header append Vary "Accept"
  </FilesMatch>
</IfModule>

Header set Content-Type "text/markdown; charset=utf-8" env=IS_MARKDOWN : on force le bon type MIME quand la variable IS_MARKDOWN a été définie par le bloc 1. La précision charset=utf-8 est importante pour les contenus français qui contiennent des caractères accentués.

Header set X-Content-Negotiation "markdown-served" env=IS_MARKDOWN : un en-tête de debug très pratique. Il permet de vérifier en un coup d'œil, via les outils de développement du navigateur ou via curl -I, que le serveur a bien servi du Markdown via la négociation. Ce n'est pas un en-tête standard, mais le préfixe X- reste autorisé pour les en-têtes spécifiques.

Le piège du Vary: Accept Si vous oubliez ce dernier en-tête, vous prenez un risque réel : LiteSpeed Cache, Cloudflare CDN ou tout proxy intermédiaire peuvent servir la version Markdown à un visiteur humain qui arriverait sur la même URL après un crawler IA. Le résultat : un texte brut Markdown affiché au visiteur au lieu de votre belle page HTML. Vary: Accept indique aux caches que la réponse dépend de l'en-tête Accept, donc qu'il faut deux entrées de cache distinctes par URL. C'est non négociable.

Bloc 3 : le type MIME des fichiers .md

Par défaut, Apache ne sait pas qu'un fichier .md est du Markdown. Il le traite comme un fichier inconnu, voire le sert en application/octet-stream ce qui force un téléchargement. Il faut donc lui apprendre.

.htaccess : Bloc 3 : type MIME
# Configuration MIME pour les .md (text/markdown, pas text/plain)
<IfModule mod_mime.c>
  AddType text/markdown .md
  AddCharset UTF-8 .md
</IfModule>

# Lecture inline (pas de download forcé)
<FilesMatch "\.md$">
  Header set Content-Disposition "inline"
</FilesMatch>

# Empêcher l'indexation Google (les crawlers IA ignorent ce header)
<FilesMatch "\.md$">
  Header set X-Robots-Tag "noindex, nofollow"
</FilesMatch>

AddType text/markdown .md : la directive clé. C'est elle qui aligne le type MIME sur ce qu'attend Cloudflare et tous les crawlers IA. Beaucoup de tutoriels en ligne préconisent text/plain à tort. Le test Cloudflare ne valide que text/markdown. Ne vous trompez pas.

Content-Disposition: inline : empêche le navigateur de proposer le téléchargement quand un humain accède à une URL .md directement. La page s'affiche alors comme du texte brut, ce qui est cohérent avec un Markdown source.

X-Robots-Tag: noindex, nofollow : c'est le détail astucieux. Google indexe les .md s'il les trouve, et cela créerait du contenu dupliqué. On lui demande donc de ne pas indexer. Mais les crawlers IA ignorent X-Robots-Tag : ils n'indexent pas au sens Google, ils consomment du contenu. La directive noindex ne les empêche pas d'utiliser les pages Markdown servies. Résultat : Google reste sur le HTML, les agents IA gagnent leur version Markdown, tout le monde est content.

La règle des fichiers jumeaux : comment ça s'articule

Avant d'assembler le tout, un point pédagogique important. Le .htaccess ne convertit rien à la volée : il joue le rôle d'aiguilleur entre deux fichiers qui doivent coexister à la racine du site. Pour chaque page publiée, il faut donc maintenir deux versions du même contenu, partageant le même slug.

Prenons l'exemple de l'URL https://www.rankit.fr/livre-blanc-geo-greenred-2026. Pour que la négociation de contenu fonctionne, deux fichiers doivent être physiquement présents sur le serveur :

Fichier sur le serveurSert quiSert quoi
livre-blanc-geo-greenred-2026.htmlHumains et GooglebotPage complète avec mise en page, CSS, scripts
livre-blanc-geo-greenred-2026.mdCrawlers IA (ClaudeBot, GPTBot, etc.)Markdown propre, sans bruit, économe en tokens
Les deux fichiers partagent le même slug livre-blanc-geo-greenred-2026. C'est cette identité de slug qui permet au .htaccess de basculer de l'un à l'autre selon l'en-tête Accept reçu.

Le mécanisme du .htaccess peut alors se décrire en une phrase : si la requête arrive avec Accept: text/markdown et que le fichier .md existe, le serveur sert le .md ; dans tous les autres cas, le serveur sert le .html. La dégradation est gracieuse : un .md manquant n'empêche jamais le HTML d'être servi.

Cas particulier de la racine du site : pour que https://www.rankit.fr/ renvoie du Markdown aux IA, il faut un fichier index.md à côté du index.html. Le .htaccess traite cette racine comme un cas dédié dans sa première règle de réécriture.

Concrètement, sur rankit.fr, l'arborescence ressemble à cela :

Arborescence des fichiers à la racine du site
www.rankit.fr/
├── .htaccess
├── index.html              # humains + Googlebot
├── index.md                # crawlers IA
├── livre-blanc-geo-greenred-2026.html
├── livre-blanc-geo-greenred-2026.md
├── geo-markdown-content-negotiation-apache-gratuit.html
├── geo-markdown-content-negotiation-apache-gratuit.md
├── brave-search-grail-22-signaux-moteur-ia.html
├── brave-search-grail-22-signaux-moteur-ia.md
└── ...

Cette correspondance un-à-un peut sembler lourde au premier abord, mais elle a une vertu : la version Markdown reste sous votre contrôle. Vous pouvez l'enrichir, l'épurer, l'adapter au contexte des LLM, là où Cloudflare convertit automatiquement le HTML sans que vous puissiez intervenir. Pour les sites à fort volume éditorial, la conversion peut être automatisée par script (voir la section dédiée plus bas).

Assemblage final : les 30 lignes ultimes

Voici les trois blocs assemblés, au format prêt à coller dans votre .htaccess existant. À placer après vos règles HTTPS/www mais avant vos règles applicatives.

.htaccess : Configuration complète Markdown for Agents
# ================================================================
# CONTENT NEGOTIATION MARKDOWN : Markdown for Agents en pur Apache
# Compatible Apache 2.4+, LiteSpeed, O2switch, Hostinger, OVH
# ================================================================

# --- Bloc 1 : réécriture si Accept: text/markdown ---
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /

  # Cas 1 : racine "/"
  RewriteCond %{HTTP_ACCEPT} text/markdown [NC]
  RewriteCond %{REQUEST_URI} ^/?$
  RewriteCond %{DOCUMENT_ROOT}/index.md -f
  RewriteRule ^$ /index.md [L,E=IS_MARKDOWN:1]

  # Cas 2 : URL /slug
  RewriteCond %{HTTP_ACCEPT} text/markdown [NC]
  RewriteCond %{REQUEST_URI} ^/([^.]+)$
  RewriteCond %{DOCUMENT_ROOT}/%1.md -f
  RewriteRule ^([^.]+)$ /$1.md [L,E=IS_MARKDOWN:1]

  # Cas 3 : URL /slug.html
  RewriteCond %{HTTP_ACCEPT} text/markdown [NC]
  RewriteCond %{REQUEST_URI} ^/(.+)\.html$
  RewriteCond %{DOCUMENT_ROOT}/%1.md -f
  RewriteRule ^(.+)\.html$ /$1.md [L,E=IS_MARKDOWN:1]
</IfModule>

# --- Bloc 2 : en-têtes HTTP ---
<IfModule mod_headers.c>
  Header set Content-Type "text/markdown; charset=utf-8" env=IS_MARKDOWN
  Header set X-Content-Negotiation "markdown-served" env=IS_MARKDOWN
  <FilesMatch "\.(html|md)$">
    Header append Vary "Accept"
  </FilesMatch>
</IfModule>

# --- Bloc 3 : MIME et configuration .md ---
<IfModule mod_mime.c>
  AddType text/markdown .md
  AddCharset UTF-8 .md
</IfModule>

<FilesMatch "\.md$">
  Header set Content-Disposition "inline"
  Header set X-Robots-Tag "noindex, nofollow"
</FilesMatch>

Trente lignes utiles, hors commentaires. Trente lignes qui font exactement le travail que Cloudflare facture 240 $/an. Trente lignes qui passent l'audit AI Compatibility avec un score parfait sur le critère Content. Et trente lignes que vous gardez sous votre contrôle, modifiables à volonté, sans dépendance externe.

Vérification immédiate avec quatre commandes curl

2 min

Le test ne prend pas plus d'une minute. Ouvrez un terminal sur votre machine, et lancez les quatre commandes suivantes l'une après l'autre.

Test 1 : un humain doit toujours recevoir du HTML

Première vérification cruciale : la content negotiation ne doit rien casser pour les visiteurs normaux. Un navigateur classique envoie Accept: text/html, ... et doit recevoir du HTML.

Test 1 : visite humaine standard
# Commande à lancer
curl -I https://www.rankit.fr/

# Réponse attendue (extraits importants)
HTTP/2 200
content-type: text/html; charset=UTF-8
vary: Accept

Le content-type doit rester text/html, et l'en-tête vary: Accept doit être présent. Si ce n'est pas le cas, le bloc 2 du .htaccess n'est pas appliqué correctement.

Test 2 : un crawler IA doit recevoir du Markdown

C'est le test critique, celui que Cloudflare exécute dans son audit. On simule un crawler IA en forçant l'en-tête Accept.

Test 2 : simulation crawler IA
# Commande à lancer
curl -I -H "Accept: text/markdown" https://www.rankit.fr/

# Réponse attendue
HTTP/2 200
content-type: text/markdown; charset=utf-8
vary: Accept
x-content-negotiation: markdown-served

Trois éléments à valider : content-type: text/markdown; charset=utf-8 (le critère Cloudflare), vary: Accept (la sécurité cache), et l'en-tête de debug x-content-negotiation: markdown-served qui confirme que le bloc 2 a bien été déclenché par la variable d'environnement.

Test 3 : un article spécifique en mode IA

Si la racine fonctionne, on vérifie qu'un article spécifique fonctionne aussi. C'est le cas le plus fréquent en production.

Test 3 : article en Markdown
# URL propre sans extension
curl -I -H "Accept: text/markdown" \
  https://www.rankit.fr/livre-blanc-geo-greenred-2026

# URL avec extension .html (doit aussi fonctionner)
curl -I -H "Accept: text/markdown" \
  https://www.rankit.fr/livre-blanc-geo-greenred-2026.html

Les deux variantes d'URL doivent renvoyer text/markdown. Si l'une des deux échoue, vérifiez que les Cas 2 et 3 du bloc 1 sont bien tous deux présents dans votre .htaccess.

Test 4 : inspection du contenu Markdown servi

Dernière vérification : on récupère le contenu réel pour confirmer qu'il s'agit bien de Markdown lisible et bien formé.

Test 4 : aperçu du Markdown servi
# Récupère le corps de la réponse en mode Markdown
curl -H "Accept: text/markdown" https://www.rankit.fr/ | head -30

# Sortie attendue : du Markdown propre
# Rankit.fr : Consultant SEO et GEO

Frédéric Jézégou, freelance SEO senior basé à Pornichet...

## Services proposés

- **Audit SEO technique**
- **Stratégie GEO**
- **Présence dans les IA**
...

Vous devez voir du Markdown lisible : titres avec dièses, listes avec tirets, gras avec doubles astérisques. Si vous voyez du HTML brut avec balises, le bloc 1 n'a pas réécrit l'URL et la requête est partie sur le fichier HTML par défaut. Si vous voyez du texte sans aucune mise en forme, votre fichier .md est probablement vide ou mal formé.

Astuce dépannage Si après déploiement les tests échouent encore, la cause la plus fréquente est un cache LiteSpeed actif qui sert l'ancienne version. Sur O2switch, une purge du cache via cPanel > LiteSpeed Cache Manager résout 80 % des cas. Sur Cloudflare en façade, une purge des en-têtes Vary peut être nécessaire via le dashboard. Sinon, vérifiez l'ordre des règles dans votre .htaccess : la content negotiation doit être placée avant les redirections HTTP applicatives.

L'audit Cloudflare après déploiement : la preuve par l'image, en 4 paliers

4 min

Quelques minutes après avoir poussé la nouvelle version du .htaccess sur la production O2switch, j'ai relancé l'audit Cloudflare AI Compatibility sur la même URL. Et puis encore deux fois après chaque amélioration. Le résultat se raconte en quatre paliers chiffrés.

Palier 2 : passage de 50 à 67 avec le .htaccess

Avant
50
Score global · Level 1
Content : 0/1
Markdown Negotiation : Échec. Réponse text/html au lieu de text/markdown. Verdict : Site does not support Markdown for Agents.
Après
67
Score global · Level 1+
Content : 1/1
Markdown Negotiation : Succès. Réponse text/markdown; charset=utf-8. Verdict Cloudflare : Site supports markdown negotiation.

Le détail technique du test, capturé après modification, montre que la requête GET / Accept: text/markdown reçoit désormais en réponse content-type: text/markdown; charset=utf-8, avec le verdict positif de Cloudflare : « Response content-type is text/markdown; charset=utf-8, site supports markdown negotiation ». Section Content passée de 0/1 à 1/1, soit un gain de +17 points sur le score global.

Palier 3 : 83 et Level 5 Agent-Native après ajout du robots.txt

L'histoire ne s'arrête pas à 67. Quelques minutes après ce premier audit, j'ai poussé en production le robots.txt enrichi avec la directive Content-Signal: search=yes, ai-train=yes, ai-input=yes décrite dans la section 8 de cet article. Nouveau scan de l'audit Cloudflare, et le résultat fait un saut spectaculaire : 67 → 83, et le niveau passe directement de Basic Web Presence à Agent-Native, la classification la plus élevée du système Cloudflare.

Le saut s'explique simplement. La directive Content-Signal dans robots.txt, combinée à la mention Allow: / et à l'absence de blocage explicite des crawlers IA légitimes, fait passer la section Bot Access Control de partiel à 2/2. Le calcul global remonte à 83.

L'enseignement méthodologique est intéressant. Le .htaccess seul, malgré toute son ingéniosité technique, plafonnait à 67 parce qu'il ne traite qu'un seul critère : la négociation de contenu Markdown. C'est l'ajout du robots.txt déclaratif, qui parle au cerveau des crawlers et pas seulement à leur client HTTP, qui débloque le palier supérieur. Les deux leviers sont complémentaires, pas redondants.

Palier 4 : 100/100 après ajout des Link headers RFC 8288

Reste un dernier critère à satisfaire pour viser le maximum. La section Discoverability plafonne à 2/3 parce qu'il manque un en-tête HTTP Link: conforme à la RFC 8288 (Web Linking) sur la page d'accueil. C'est un mécanisme standardisé qui permet aux agents IA de découvrir, dès le premier appel HTTP, les ressources machine-lisibles qui décrivent le site : sitemap, robots.txt, documentation API, llms.txt, etc.

Sur rankit.fr, j'ai ajouté trois lignes dans le bloc mod_headers du .htaccess qui pointent vers les deux ressources machine-lisibles que mon site expose déjà : /sitemap.xml et /robots.txt. La sous-section consacrée aux Link headers en section 9 détaille la syntaxe correcte et le piège technique à éviter sous LiteSpeed. Une fois déployé, nouveau scan : 83 → 100, sections Discoverability, Content et Bot Access Control toutes à leur maximum.

IndicateurÉtat initial+ .htaccess Markdown+ robots.txt Content-Signal+ Link headersGain total
Score global506783100+50
Section Content0/11/11/11/1−
Section Bot Access Control−partiel2/22/2−
Section Discoverability−2/32/33/3+1
Niveau CloudflareLevel 1Level 1+Level 5Level 5−
Label associéBasic Web PresenceBasic Web PresenceAgent-NativeAgent-Native−
Progression complète du score Cloudflare AI Compatibility de www.rankit.fr en quatre paliers cumulatifs. Mesures effectuées le 27 avril 2026.
Trois fichiers, trois leviers, score parfait Le récit complet tient en trois fichiers de configuration totalisant moins de quarante lignes : un .htaccess avec les règles de négociation Markdown et les Link headers, un robots.txt avec la directive Content-Signal, et les fichiers Markdown jumeaux des pages HTML. Aucun service externe, aucune dépendance technique, aucun abonnement CDN. C'est exactement la configuration qu'un consultant SEO peut déployer sur le site de n'importe quelle PME hébergée chez O2switch, OVH mutualisé ou Infomaniak en moins d'une heure de travail effectif.

Et après le 100/100 ?

Une fois le score à 100 sur le profil Content, il reste deux pistes pour aller plus loin sur d'autres profils d'audit. Cloudflare propose en effet des profils plus exigeants comme full, qui activent les 17 checks au lieu de 7, et qui testent des critères additionnels comme API, Auth, MCP & Skill Discovery et Commerce. Pour un site éditorial comme rankit.fr, ces critères sont hors périmètre. Mais pour un éditeur SaaS, un site e-commerce ou une plateforme proposant une API publique, ce sont des leviers concrets.

  • API catalog (rel="api-catalog") : ajouter un fichier /.well-known/api-catalog conforme RFC 9727 et un Link header pointant vers lui. C'est le mécanisme officiel pour annoncer aux agents l'existence d'une API publique.
  • llms.txt : ajouter un fichier llms.txt à la racine du site, conforme à la norme émergente, qui agrège la documentation principale du site sous forme Markdown agent-friendly.

Mais ce qui vient de se passer sur rankit.fr est déjà l'aboutissement complet de la promesse « reproduire gratuitement ce que Cloudflare facture 240 $/an ». Score parfait, niveau maximal, sans aucune dépendance externe, en moins d'une heure de configuration. Soyons clairs sur l'enjeu économique de ce résultat.

L'économie réelle, chiffrée sur 5 ans

2 min

Faisons les comptes calmement. La question n'est pas de cracher sur Cloudflare, qui propose un produit techniquement excellent et utile à de nombreux usages. La question est de mesurer ce qu'on dépense pour activer cette case spécifique, et ce qu'on en retire en retour.

Cas mono-site : un site éditorial standard

Hypothèse : un site éditorial classique (blog professionnel, vitrine consultant, site média), hébergé sur Apache ou LiteSpeed mutualisé, qui veut activer Markdown for Agents pour rester compétitif sur le GEO.

SolutionCoût annuelCoût 5 ansEffort initial
Cloudflare Free + .htaccess Apache0 $0 $30 min de config
Cloudflare Pro annuel240 $1 200 $Toggle + migration DNS
Cloudflare Pro mensuel300 $1 500 $Toggle + migration DNS
Cloudflare Business annuel2 400 $12 000 $Toggle + migration DNS
Économie .htaccess vs Pro annuel240 $1 200 $−
Comparaison des solutions pour activer la content negotiation Markdown sur un site mono-domaine. Tarifs Cloudflare avril 2026.

Sur 5 ans, l'écart minimum est de 1 200 $. Au cours de change actuel, c'est environ 1 100 € qui restent dans la trésorerie de l'entreprise au lieu de partir vers un abonnement CDN. Pour un freelance ou une PME, c'est trois bons écrans, ou un séjour en formation Formaseo, ou le budget d'une refonte graphique.

Cas multi-sites : un consultant ou une agence

Pour un freelance comme moi qui gère plusieurs domaines, ou pour une agence qui héberge les sites de ses clients, l'écart se multiplie linéairement. Mes propres propriétés web : rankit.fr, dicocitations.com, vultifrine.com, greenred.fr, soit quatre domaines actifs.

.htaccess Apache
0 €
total / 5 ans / 4 sites
Une seule config .htaccess dupliquée. Maintenance : 0. Score Cloudflare AI Audit : 67, identique au Pro.
Cloudflare Pro
4 800 $
total / 5 ans / 4 sites
240 $/an × 4 sites × 5 ans. Inclut WAF et image opt., dont vous n'avez peut-être pas l'usage.

Pour un consultant SEO/GEO indépendant ou une petite agence avec quatre clients en mutualisé, on parle de 4 800 $ environ 4 400 € qui peuvent être réinvestis ailleurs : outil de suivi GEO, abonnement IA premium pour la production, ou simplement une trésorerie plus saine.

Le coût caché de la dépendance Cloudflare

Au-delà du chèque mensuel, il y a un coût qui ne se voit pas dans les tableaux de tarifs : la dépendance à un fournisseur tiers pour des fonctionnalités HTTP basiques.

Si demain Cloudflare change ses conditions tarifaires, ajoute un quota d'usage, ou décide de monter Markdown for Agents en feature Premium uniquement, vous subissez la décision sans recours. Si Cloudflare a une panne mondiale (ce qui est arrivé en 2020, 2022 et 2024), votre content negotiation tombe avec eux. Si vous voulez ajouter une logique métier sur la conversion (cache personnalisé, transformation de contenu, contrôle d'accès), vous êtes limité par leur API.

En .htaccess, le code vous appartient. Il s'exécute dans votre stack, sous votre contrôle, modifiable à la milliseconde. Vous pouvez y ajouter ce que vous voulez, comme nous allons le voir maintenant avec deux en-têtes bonus que Cloudflare ne propose pas par défaut.

Aller plus loin que Cloudflare : x-markdown-tokens et Content-Signal dans robots.txt

3 min

Markdown for Agents, c'est la base. Mais une fois la mécanique en place, on peut enrichir le dispositif avec deux signaux complémentaires qui parlent un langage encore plus précis aux agents IA modernes. Le premier, x-markdown-tokens, est un en-tête HTTP que vous pouvez calculer en PHP au moment de servir le .md. Le second, Content-Signal, est une directive de votre robots.txt qui n'a rien à voir avec le .htaccess mais qui complète parfaitement le tableau. Voyons les deux.

L'en-tête x-markdown-tokens

Cet en-tête, popularisé par Cloudflare, indique au client le nombre estimé de tokens contenus dans la réponse Markdown. Pour un agent IA qui doit gérer une fenêtre de contexte limitée (par exemple 200 000 tokens chez Claude), savoir qu'une page consommera 4 500 tokens permet de décider à l'avance combien de sources fetcher avant de faire l'inférence.

Le calcul du nombre de tokens est une approximation. La règle empirique communément admise pour le français est environ 1 token pour 3,5 caractères. On peut donc l'estimer côté serveur avec un script PHP léger.

md-router.php : calcul x-markdown-tokens
<?php
// Routeur PHP qui sert un .md avec calcul de tokens
// À utiliser à la place du Bloc 1 si vous voulez x-markdown-tokens
$accept = $_SERVER['HTTP_ACCEPT'] ?? '';
$uri    = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$slug   = trim(preg_replace('#\.html$#', '', $uri), '/');

if (stripos($accept, 'text/markdown') !== false) {
    $md_path = __DIR__ . '/' . $slug . '.md';

    if (is_file($md_path)) {
        $content = file_get_contents($md_path);
        // Estimation : ~1 token pour 3.5 caractères en français
        $tokens  = (int) round(mb_strlen($content, 'UTF-8') / 3.5);

        header('Content-Type: text/markdown; charset=utf-8');
        header('Vary: Accept');
        header('X-Markdown-Tokens: ' . $tokens);
        header('X-Content-Negotiation: markdown-served');
        header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($md_path)) . ' GMT');
        echo $content;
        exit;
    }
}

// Fallback HTML normal
$html_path = __DIR__ . '/' . $slug . '.html';
if (is_file($html_path)) {
    header('Vary: Accept');
    readfile($html_path);
    exit;
}

http_response_code(404);

Ce script remplace le Bloc 1 quand on veut un calcul précis. La règle .htaccess qui route vers ce fichier devient :

.htaccess : Routage vers md-router.php
# Si Accept: text/markdown, router vers md-router.php
RewriteCond %{HTTP_ACCEPT} text/markdown [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^.]+)$ /md-router.php [L]

La directive Content-Signal dans robots.txt

Le framework Content Signals a été initié par Cloudflare en septembre 2025 pour permettre aux éditeurs d'exprimer machine-readable leurs préférences sur l'usage de leur contenu par les IA. Premier point essentiel à comprendre : Content-Signal n'est pas un en-tête HTTP, c'est une directive qui s'ajoute à votre fichier robots.txt. C'est une extension du Robots Exclusion Protocol, pas un en-tête de réponse HTTP. Les crawlers IA conformes vont chercher cette information dans robots.txt, là où ils ont l'habitude de lire User-agent, Allow et Disallow.

Le framework définit trois signaux principaux :

  • search : autoriser l'indexation et l'affichage dans les résultats de recherche IA (valeurs yes, no)
  • ai-input : autoriser l'utilisation comme contexte agentique en temps réel, RAG ou grounding (valeurs yes, no)
  • ai-train : autoriser l'utilisation pour entraîner ou affiner des modèles (valeurs yes, no)

Sur les domaines Free chez Cloudflare qui n'ont pas de robots.txt existant, Cloudflare insère automatiquement la directive Content-Signal: search=yes, ai-train=no par défaut. Les domaines qui ont déjà un robots.txt, comme rankit.fr, ne sont pas modifiés. Il faut donc ajouter la directive soi-même, et c'est précisément ce qui me convient mieux : je décide de ma politique, pas Cloudflare à ma place.

La politique permissive de rankit.fr

Sur rankit.fr, j'ai opté pour la politique permissive maximale. Mon contenu existe pour être lu, indexé, cité, et accessoirement contribuer à l'amélioration des modèles qui me citent. C'est une décision éditoriale qui m'appartient, et qui peut différer pour un site média payant ou une publication scientifique soumise à embargo. Voici l'extrait à coller dans robots.txt.

robots.txt : Content-Signal politique permissive maximale (rankit.fr)
# Politique Content Signals (contentsignals.org)
# En tant que condition d'accès à ce site, vous acceptez les signaux suivants :
# (a) si content-signal = yes, vous pouvez collecter le contenu pour l'usage correspondant
# (b) si content-signal = no, vous ne pouvez pas collecter le contenu pour l'usage correspondant
# (c) si le signal n'est pas fourni, l'opérateur du site n'accorde ni ne restreint l'usage
#
# Signification des trois signaux :
# search : indexation et affichage dans des résultats de recherche
# ai-input : utilisation comme contexte temps réel pour modèles IA (RAG, grounding)
# ai-train : utilisation pour entraîner ou affiner des modèles IA

User-agent: *
Content-Signal: search=yes, ai-train=yes, ai-input=yes
Allow: /

Sitemap: https://www.rankit.fr/sitemap.xml

Les commentaires ne sont pas obligatoires mais recommandés par contentsignals.org : ils rendent la politique lisible par un humain qui auditerait votre fichier, et explicitent la sémantique des trois signaux pour quiconque ne connaît pas encore le framework. Vous pouvez aussi générer une version personnalisée plus complète sur contentsignals.org avec leur outil officiel.

Pour les politiques plus restrictives, voici trois exemples qui correspondent à des cas d'usage courants :

Trois alternatives selon votre stratégie éditoriale
# Cas A : permissive maximale (choix rankit.fr)
# Pour les sites qui veulent maximiser leur visibilité dans les IA
Content-Signal: search=yes, ai-train=yes, ai-input=yes

# Cas B : refuser uniquement l'entraînement (choix par défaut Cloudflare managé)
# Pour les sites qui acceptent la citation mais refusent que leur contenu
# alimente l'entraînement de modèles fondationnels
Content-Signal: search=yes, ai-train=no, ai-input=yes

# Cas C : recherche classique uniquement
# Pour les sites média sur abonnement, publications scientifiques sous embargo,
# contenus juridiques sensibles
Content-Signal: search=yes, ai-train=no, ai-input=no

Note importante : l'en-tête HTTP content-signal n'est pas le mécanisme principal

Précision technique pour ne pas se tromper. Quand Cloudflare convertit du HTML en Markdown via Markdown for Agents, sa réponse HTTP inclut effectivement un en-tête content-signal: ai-train=yes, search=yes, ai-input=yes. Cet en-tête HTTP est cohérent avec ce qui est déclaré dans le robots.txt, mais ce n'est pas ce que les crawlers consultent en premier. Les crawlers conformes vont d'abord lire robots.txt, et l'en-tête HTTP n'est que la confirmation par requête de la politique globale.

Concrètement : si vous ajoutez Header set Content-Signal dans votre .htaccess mais pas la directive dans robots.txt, les crawlers ne verront pas votre politique au moment où ils décident de collecter votre contenu. La règle est donc : la déclaration vit dans robots.txt, l'en-tête HTTP est secondaire et plutôt destiné aux clients qui ont déjà fetché le contenu.

L'avantage de la voie robots.txt Cette voie est techniquement plus propre que l'en-tête HTTP pour deux raisons. D'abord, c'est indépendant de votre stack serveur : que vous soyez sur Apache, Nginx, IIS, ou même un site statique, vous avez un robots.txt. Ensuite, c'est indépendant de Cloudflare : votre politique vous appartient et reste valable même si vous changez de CDN demain. Enfin, c'est conforme au framework standardisé contentsignals.org, ratifié par l'IETF dans le groupe de travail AI Preferences (AIPREF).

Le piège Lighthouse : pourquoi votre score SEO peut tomber à 92

Soyons honnêtes sur un effet secondaire que vous allez rencontrer en déployant cette directive. Au moment où vous ajoutez Content-Signal: à votre robots.txt, votre score Lighthouse SEO va probablement chuter de 100 à 92. C'est exactement ce qui s'est passé sur rankit.fr. Search Console peut aussi afficher un avertissement « Syntax not understood » sur cette ligne.

La raison est mécanique. Lighthouse fait huit audits SEO automatisés, chacun pèse environ 12,5 points sur le score final. L'un de ces audits est « robots.txt is valid » qui vérifie que toutes les directives appartiennent à une liste blanche connue (User-agent, Allow, Disallow, Sitemap, Crawl-delay, etc.). Comme Content-Signal n'est pas encore dans cette liste blanche au moment où j'écris ces lignes, Lighthouse signale la directive comme « Unknown directive », l'audit échoue, et le score tombe d'environ huit points.

Cloudflare reconnaît explicitement le problème dans sa propre documentation :

Documentation Cloudflare officielle Google Search Console may occasionally report Syntax not understood for Content Signals and newer directives in the robots.txt standard. However, we have observed no impact on crawling rates or SEO as a result of these reports.

Et la communauté Chrome a effectivement ouvert le Pull Request #16767 sur le repository Lighthouse pour ajouter Content-Signal à la safelist des directives reconnues. Le PR a été ouvert en octobre 2025 et reste en review au moment de cette publication. Une fois mergé, le warning disparaîtra automatiquement et votre score remontera à 100 sans que vous ayez à toucher à votre robots.txt.

Mais le score Lighthouse SEO n'est pas un signal de ranking Google

Avant de paniquer et de retirer la directive pour récupérer votre 100, lisez ce que John Mueller de l'équipe Search Relations de Google dit régulièrement à ce sujet : le score Lighthouse SEO n'est pas un facteur de classement. Google n'utilise pas ce score pour décider du positionnement de vos pages. C'est un outil de diagnostic destiné aux développeurs, pas un signal de ranking.

L'unité de mesure qui compte vraiment, c'est le comportement réel de Googlebot et l'indexation effective dans Search Console, pas un score agrégé calculé par un outil tiers. La preuve : sur rankit.fr, malgré le score Lighthouse à 92, l'indexation Google fonctionne parfaitement, le crawl est stable, et aucune URL n'a été déréférencée à cause de cette directive.

L'arbitrage devient alors lisible. D'un côté, un score Lighthouse cosmétique qui passe de 100 à 92, sans aucun impact sur le ranking Google réel. De l'autre, un signal GEO standardisé qui exprime à 3,8 millions de domaines Cloudflare et à tous les agents IA conformes votre politique d'usage de contenu. Le choix me semble clair : le vrai gain GEO l'emporte sur le score cosmétique, surtout que ce dernier est une régression temporaire dont la résolution est déjà engagée côté Lighthouse.

Vos trois stratégies face à ce dilemme

Pour les sites où le score Lighthouse a une importance contractuelle ou commerciale (présentations clients, audits d'agence, monitoring automatisé), voici les options possibles, classées de la plus pédagogique à la plus défensive :

  • Garder la directive et expliquer. C'est le choix de rankit.fr. Si quelqu'un vous interpelle sur le score 92, vous lui montrez le PR Lighthouse en attente, vous citez John Mueller, et vous expliquez que vous êtes en avance d'environ six mois sur la safelist. C'est un argument de maturité technique, pas une faute.
  • Garder la directive et auto-documenter le robots.txt. Vous ajoutez en commentaires de votre robots.txt une note explicative qui contextualise la directive et anticipe la critique. Quiconque lit le fichier comprend tout de suite pourquoi elle est là, et où on en est dans le cycle de standardisation.
  • Retirer temporairement la directive. Si vous gérez un site institutionnel à fort enjeu commercial sur les scores agrégés, vous pouvez décider d'attendre que Lighthouse merge le PR avant de déployer Content-Signal. Vous perdez le signal GEO marginal, mais vous préservez votre 100. C'est un choix défensif, pas une erreur.

Sur rankit.fr, j'ai opté pour la stratégie hybride entre les deux premières options : la directive est en place, et le robots.txt contient une note explicative en commentaires. Le score Lighthouse SEO est à 92, mais le score Cloudflare AI Compatibility est à 100/100, et le ranking Google est intact. Trois mesures qui pointent dans des directions différentes, et qui ensemble racontent l'histoire d'un site qui assume d'être en avance sur la transition GEO.

3 min

Le principe : annoncer ses ressources machine-lisibles

Les Link headers existent depuis la RFC 5988 de 2010, mise à jour par la RFC 8288 en 2017. Le concept est simple : ajouter un ou plusieurs en-têtes HTTP Link: à la réponse de la page d'accueil, qui pointent vers des ressources liées avec une relation typée. Le client lit ces en-têtes avant même de parser le HTML, et sait immédiatement quelles autres ressources consulter.

Pour les agents IA, c'est un mécanisme de découverte central. Au lieu de scraper toute votre arborescence pour trouver le sitemap ou le robots.txt, ils lisent les Link headers de votre racine et savent où aller. C'est la version industrialisée du « voici la carte du site, servez-vous ».

L'audit Cloudflare AI Compatibility teste précisément ce critère sous le nom linkHeaders, dans la section Discoverability. Pour le valider, il faut que la réponse à GET / contienne au moins un en-tête Link: avec une relation reconnue par l'IANA Link Relations Registry et identifiée comme agent-useful. Les quatre relations principales sont :

RelationCible typiqueCas d'usage
describedbysitemap.xml, robots.txt, llms.txtDescription machine-lisible du site (la plus polyvalente)
api-catalog/.well-known/api-catalogCatalogue d'API publique conforme RFC 9727
service-descopenapi.yaml, swagger.jsonSpécification OpenAPI ou similaire
service-doc/docs/api, /api/helpDocumentation humaine de l'API
Relations Link header reconnues comme agent-useful par l'audit Cloudflare AI Compatibility, conformes à la RFC 8288 et au registre IANA.

Pour un site éditorial comme rankit.fr qui n'expose pas d'API publique, la relation à utiliser est describedby. Elle pointe vers les ressources qui décrivent le site, à savoir le sitemap XML et le robots.txt. Pour un site SaaS ou un éditeur d'API, vous combineriez describedby avec api-catalog ou service-desc.

Le code .htaccess à ajouter

Trois lignes à insérer dans le bloc mod_headers du .htaccess, après les blocs Markdown for Agents existants :

.htaccess : Link headers RFC 8288 (version qui marche sous LiteSpeed)
# Bloc 4 : Link headers RFC 8288 pour la découverte agent
# (critère linkHeaders de l'audit Cloudflare AI Compatibility)
<IfModule mod_headers.c>
    Header set Link '</sitemap.xml>; rel="describedby"; type="application/xml"'
    Header append Link '</robots.txt>; rel="describedby"; type="text/plain"'
</IfModule>

La directive Header set Link définit le premier en-tête, la directive Header append Link en ajoute un second séparé par une virgule (équivalent à deux en-têtes Link: distincts dans la réponse HTTP, c'est conforme à la RFC). Le résultat servi sur la racine donne :

Réponse HTTP attendue (curl -I https://www.rankit.fr/)
HTTP/2 200
content-type: text/html; charset=UTF-8
link: </sitemap.xml>; rel="describedby"; type="application/xml"
link: </robots.txt>; rel="describedby"; type="text/plain"

Le piège LiteSpeed : doubles quotes échappées par backslash

Voici un piège qui m'a coûté un audit raté lors du premier essai. La syntaxe Apache classique pour les valeurs avec espaces utilise des doubles quotes externes et des doubles quotes internes échappées par backslash :

Version qui ne marche pas sous LiteSpeed
# Cette syntaxe est valide Apache mais cassée sur LiteSpeed
Header set Link "</sitemap.xml>; rel=\"describedby\"; type=\"application/xml\""

Sous Apache 2.4 standard, cette ligne fonctionne et l'en-tête HTTP servi est correctement décodé. Mais sous LiteSpeed (le serveur utilisé par défaut sur la plupart des hébergements mutualisés français comme O2switch, Infomaniak ou OVH Performance), le parser interprète littéralement les \" et les inclut dans la valeur servie. Résultat dans la réponse HTTP :

Sortie HTTP cassée par LiteSpeed
link: </sitemap.xml>; rel=\"describedby\"; type=\"application/xml\"

Les backslashes sont préservés littéralement, et les parsers RFC 8288 (dont celui de l'audit Cloudflare) échouent à reconnaître la relation describedby parce qu'elle est entourée de \" au lieu de ". L'audit a renvoyé le verdict « Link headers present but no agent-useful relation types found », ce qui était techniquement exact : aucune relation n'était parsable.

La solution est élégante. Apache mod_headers accepte les simples quotes comme délimiteurs de valeur, exactement comme un shell Unix. À l'intérieur de simples quotes, les doubles quotes sont préservées telles quelles sans aucun échappement nécessaire. C'est la syntaxe officielle utilisée dans la documentation Apache de référence du framework IIIF.

Règle à retenir pour mod_headers sous LiteSpeed Quand votre valeur contient des doubles quotes, entourez-la de simples quotes. Quand elle contient des simples quotes, entourez-la de doubles quotes. Ne mélangez jamais les deux dans la même valeur, et n'utilisez les backslashes d'échappement qu'en dernier recours sur Apache stock, jamais sur LiteSpeed.

Validation : passer Discoverability de 2/3 à 3/3

Une fois le snippet déployé, le test se fait en deux étapes. D'abord, vérification curl pour s'assurer que les en-têtes HTTP servis sont propres :

Test curl de validation
# La sortie ne doit contenir aucun backslash
curl -I https://www.rankit.fr/ | grep -i "link:"

Puis relance de l'audit Cloudflare avec le profil complet pour que la section Discoverability soit testée intégralement :

Lancer l'audit Rankit (7 checks) sur isitagentready.com

Sur rankit.fr, le résultat de cet audit final est sans appel : score global 100/100, Discoverability 3/3, Content 1/1, Bot Access Control 2/2, niveau Level 5 Agent-Native confirmé. C'est l'aboutissement complet de la promesse de cet article. Trois fichiers de configuration, moins de quarante lignes au total, zéro service externe, et le site décroche le score maximal de l'audit Cloudflare AI Compatibility sur les sept critères testés.

Et pour les sites sous Nginx ?

2 min

Tout le monde n'est pas sous Apache. Pour les sites Nginx (souvent utilisés sur VPS Hetzner, OVH Cloud, AWS Lightsail, ou en stack moderne avec PHP-FPM), la même logique se transpose élégamment. Nginx n'a pas l'équivalent direct de .htaccess, la configuration se fait dans le fichier de site (typiquement /etc/nginx/sites-available/votre-site.conf).

nginx.conf : Content negotiation Markdown
# =============================================================
# Au niveau http {} : map qui détecte Accept: text/markdown
# =============================================================
map $http_accept $wants_markdown {
    default            0;
    "~*text/markdown" 1;
}

# =============================================================
# Au niveau server {} : déclaration MIME et logique de service
# =============================================================
server {
    listen 443 ssl http2;
    server_name www.example.com;
    root /var/www/html;

    # Type MIME pour les .md
    types {
        text/markdown md;
    }
    charset utf-8;

    location / {
        # Si Accept: text/markdown ET .md existe : on le sert
        if ($wants_markdown) {
            rewrite ^/$ /index.md break;
            rewrite ^/(.+?)(\.html)?$ /$1.md break;
        }

        # Fallback HTML normal
        try_files $uri $uri.html $uri/ =404;
    }

    # En-têtes pour les .md servis
    location ~ \.md$ {
        add_header Content-Type "text/markdown; charset=utf-8" always;
        add_header Vary "Accept" always;
        add_header X-Content-Negotiation "markdown-served" always;
        add_header X-Robots-Tag "noindex, nofollow" always;
        add_header Content-Disposition "inline" always;
    }

    # Vary: Accept aussi sur les .html (cohérence cache)
    location ~ \.html$ {
        add_header Vary "Accept" always;
    }
}

Trois points d'attention spécifiques à Nginx.

Le bloc map doit être placé au niveau http {} dans nginx.conf, pas dans le bloc server. C'est une particularité Nginx : les directives map ne sont pas autorisées dans le contexte server.

La directive if dans location est connue pour ses pièges (if is evil), mais ici l'usage avec rewrite ... break est sûr et idiomatique. Pas de risque de boucle.

Recharger la configuration après modification : sudo nginx -t pour valider la syntaxe, puis sudo systemctl reload nginx. Sur un VPS bien configuré, l'opération est sans coupure.

Comment produire les fichiers Markdown jumeaux

2 min

Tout le mécanisme .htaccess repose sur l'existence de fichiers .md jumeaux à côté des fichiers .html. Si un article s'appelle livre-blanc-geo-greenred-2026.html, il faut un fichier livre-blanc-geo-greenred-2026.md à la racine pour que la content negotiation fonctionne sur cette URL.

La question logique devient donc : comment produire ces fichiers Markdown ? Trois approches existent, chacune avec ses avantages.

Approche manuelle au moment de la rédaction

C'est la voie que j'ai privilégiée pour mes articles importants. Au moment où je rédige le HTML d'un article, je rédige aussi sa version Markdown source. Le Markdown devient en réalité le format maître, et le HTML est généré à partir de lui (par moi, manuellement, ou via un convertisseur).

Avantages : qualité maximale du Markdown servi (pas de bruit, structure fidèle aux intentions de l'auteur). Inconvénients : effort double à la rédaction, et risque de désynchronisation HTML/MD si on modifie l'un sans l'autre.

Approche script PHP automatique

Pour les sites avec un grand nombre de pages générées dynamiquement (CMS, e-commerce, sites éditoriaux à fort volume), une conversion automatique au moment du build ou en cron quotidien est plus réaliste. Plusieurs bibliothèques PHP font le travail :

  • league/html-to-markdown (Composer) : robuste, bien maintenu, supporte les principales extensions Markdown
  • turndown (Node.js, si vous avez un build pipeline) : très configurable, populaire dans l'écosystème JS
  • pandoc en CLI : la référence absolue, à invoquer en post-build via pandoc -f html -t markdown_strict

L'idée : à chaque déploiement, un script parcourt le dossier, lit chaque .html, en extrait le contenu principal (sans nav, sans sidebar, sans footer), le convertit en Markdown et écrit le .md jumeau. Le résultat est mécaniquement à jour.

Approche convertisseur en ligne : rankit.fr/md/

Pour les sites éditoriaux qui n'ont pas envie d'installer Composer ou Pandoc, la voie la plus simple est de passer par un convertisseur qui prend une URL HTML en entrée et retourne du Markdown propre en sortie. C'est exactement ce que j'ai développé sur rankit.fr/md/, après avoir constaté que les outils existants étaient soit verbeux (gardaient trop de balises), soit trop décharnés (perdaient les liens internes).

Le convertisseur Rankit fait trois choses qu'on peut difficilement obtenir ailleurs :

  • Il nettoie agressivement les éléments non-sémantiques (CSS inline, scripts, ARIA, blocs de navigation)
  • Il préserve la structure sémantique : titres, listes, citations, code, liens et tableaux dans leur forme Markdown standard
  • Il respecte l'encodage UTF-8 français (un point souvent défaillant chez les convertisseurs anglophones avec les apostrophes typographiques et les caractères accentués)

L'outil est gratuit, sans inscription, conçu pour être l'outil que j'aurais aimé trouver quand j'ai commencé ce projet sur rankit.fr. Vous collez l'URL de votre page HTML, vous récupérez le Markdown formaté, vous l'enregistrez à côté du .html. Trois clics, et votre page est prête pour la content negotiation.

Outil gratuit
Convertisseur HTML→Markdown · Rankit

Convertissez vos pages HTML en .md propres en 3 clics

Vous avez activé la content negotiation Markdown sur votre .htaccess ? Il vous manque les fichiers .md jumeaux pour que ça fonctionne. Le convertisseur Rankit transforme n'importe quelle URL HTML en Markdown propre, sans bruit, encodage UTF-8 préservé, structure sémantique respectée. Sans inscription, sans limite raisonnable.

Lancer le convertisseur
URL → .md · Encodage UTF-8 · Sans inscription · Code Open Source à terme

Le .htaccess final : bloc complet à copier-coller

2 min

Voici la version consolidée et prête à déployer. Chacun des quatre blocs a été construit et expliqué un par un dans les sections précédentes. Les voici assemblés dans le bon ordre, avec commentaires détaillés, prêts à être collés tels quels dans un .htaccess vierge à la racine du site.

À placer après vos règles HTTPS/www mais avant toute règle applicative (WordPress, Laravel, routing custom). Compatible Apache 2.4+, LiteSpeed (O2switch, Hostinger, Infomaniak, OVH Performance), et tout serveur supportant les .htaccess.

.htaccess : configuration complète (4 blocs → 100/100)
# ================================================================
# .HTACCESS COMPLET · MARKDOWN FOR AGENTS · CLOUDFLARE 100/100
# ================================================================
# Testé en production sur rankit.fr (O2switch / LiteSpeed)
# Audit Cloudflare AI Compatibility : 100/100 · Level 5 Agent-Native
# Compatible Apache 2.4+, LiteSpeed, Nginx via traduction séparée
# ================================================================
#
# BLOC 1 · mod_rewrite : Accept: text/markdown -> fichier .md jumeau
# BLOC 2 · mod_headers : Content-Type, Vary, X-Content-Negotiation
# BLOC 3 · mod_mime    : type MIME, charset, inline, noindex
# BLOC 4 · mod_headers : Link headers RFC 8288 (Discoverability)
# ================================================================

# --- BLOC 1 : réécriture si Accept: text/markdown ---
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /

  # Cas 1 : racine "/" avec Accept: text/markdown
  RewriteCond %{HTTP_ACCEPT} text/markdown [NC]
  RewriteCond %{REQUEST_URI} ^/?$
  RewriteCond %{DOCUMENT_ROOT}/index.md -f
  RewriteRule ^$ /index.md [L,E=IS_MARKDOWN:1]

  # Cas 2 : URL propre /slug (sans extension)
  RewriteCond %{HTTP_ACCEPT} text/markdown [NC]
  RewriteCond %{REQUEST_URI} ^/([^.]+)$
  RewriteCond %{DOCUMENT_ROOT}/%1.md -f
  RewriteRule ^([^.]+)$ /$1.md [L,E=IS_MARKDOWN:1]

  # Cas 3 : URL legacy /slug.html avec extension explicite
  RewriteCond %{HTTP_ACCEPT} text/markdown [NC]
  RewriteCond %{REQUEST_URI} ^/(.+)\.html$
  RewriteCond %{DOCUMENT_ROOT}/%1.md -f
  RewriteRule ^(.+)\.html$ /$1.md [L,E=IS_MARKDOWN:1]
</IfModule>

# --- BLOC 2 : en-têtes HTTP de réponse ---
<IfModule mod_headers.c>
  Header set Content-Type "text/markdown; charset=utf-8" env=IS_MARKDOWN
  Header set X-Content-Negotiation "markdown-served" env=IS_MARKDOWN

  # Vary: Accept (obligatoire) pour les caches CDN (LiteSpeed, Cloudflare)
  <FilesMatch "\.(html|md)$">
    Header append Vary "Accept"
  </FilesMatch>
</IfModule>

# --- BLOC 3 : MIME et configuration des .md ---
<IfModule mod_mime.c>
  AddType text/markdown .md
  AddCharset UTF-8 .md
</IfModule>

<FilesMatch "\.md$">
  Header set Content-Disposition "inline"
  Header set X-Robots-Tag "noindex, nofollow"
</FilesMatch>

# --- BLOC 4 : Link headers RFC 8288 (Discoverability 3/3) ---
# IMPORTANT : simples quotes obligatoires sous LiteSpeed
# (l'échappement avec \" casse le parsing)
<IfModule mod_headers.c>
  Header set Link '</sitemap.xml>; rel="describedby"; type="application/xml"'
  Header append Link '</robots.txt>; rel="describedby"; type="text/plain"'
</IfModule>

Une fois ce .htaccess déployé à la racine du site et le robots.txt contenant la directive Content-Signal, l'audit Cloudflare AI Compatibility renvoie :

  • Score global : 100/100 · Level 5 Agent-Native
  • Discoverability : 3/3 · sitemap, robots.txt et Link headers détectés
  • Content : 1/1 · négociation Markdown supportée
  • Bot Access Control : 2/2 · politique Content-Signal déclarée

Trois fichiers, une quarantaine de lignes de code, zéro abonnement CDN. Le même résultat que Cloudflare facture 240 $/an minimum sur le plan Pro, ici en Apache pur, sous votre contrôle total, avec la liberté d'étendre la politique à votre rythme.

Checklist de déploiement 1. Coller le bloc ci-dessus dans votre .htaccess à la racine du site. 2. S'assurer que chaque page HTML dispose d'un fichier .md jumeau (manuellement, par script PHP, ou via le convertisseur rankit.fr/md/). 3. Ajouter la directive Content-Signal dans votre robots.txt (voir section 8). 4. Purger le cache LSCache via cPanel le cas échéant. 5. Lancer les quatre tests curl de la section 5 pour valider. 6. Relancer l'audit Cloudflare pour confirmer le 100/100.

Palier bonus : /.well-known/api-catalog (RFC 9727)

2 min

Nos sections précédentes ont couvert trois formats servis via content negotiation : HTML pour les humains, Markdown pour les crawlers IA, et JSON via l'endpoint /api/post/{slug}.json. La question naturelle qui se pose ensuite : comment un agent découvre-t-il que l'endpoint JSON existe ?

Trois voies de découverte coexistent aujourd'hui, avec des niveaux d'agent-readiness différents :

  • Le <link rel="alternate" type="application/json"> dans le <head> HTML : l'agent doit télécharger et parser le HTML d'abord.
  • Le champ alternateFormat à l'intérieur du JSON lui-même : l'agent doit déjà avoir fetché le JSON pour le trouver.
  • La voie well-known RFC 9727 : l'agent fetche une URL prédictible unique et obtient le catalogue complet, sans parsing HTML.

La troisième voie est la plus agent-native. C'est aussi la plus jeune : la norme est la RFC 9727, ratifiée par l'IETF en juin 2025 par Kevin Smith (Vodafone) après treize révisions et deux ans de travail.

Le principe : un well-known URI prédictible

La RFC définit un well-known URI unique : /.well-known/api-catalog. Tout client conforme peut faire un GET sur cette URL sur n'importe quel domaine et savoir immédiatement quelles APIs sont exposées et où les trouver. Le mécanisme s'appuie sur la RFC 8615 (Well-Known URIs) et le format Linkset JSON de la RFC 9264.

Trois points techniques obligatoires pour satisfaire la RFC :

  • L'endpoint DOIT être servi en HTTPS (recommandation de sécurité §8 de la RFC).
  • La réponse DOIT être disponible au format application/linkset+json défini par la RFC 9264.
  • Le Content-Type DEVRAIT inclure le paramètre profile profile="https://www.rfc-editor.org/info/rfc9727" pour que les consommateurs puissent identifier le document comme un api-catalog valide.

Le fichier linkset.json

Le format Linkset (RFC 9264) est simple : un document JSON avec un tableau linkset à la racine, chaque entrée contenant une URL anchor et un ensemble de relations de lien typées. Pour rankit.fr, le fichier est placé à la racine du site sous /linkset.json.

linkset.json : Linkset RFC 9264 + profile RFC 9727
{
  "linkset": [
    {
      "anchor": "https://www.rankit.fr/api/post/",
      "service-doc": [
        {
          "href": "https://www.rankit.fr/geo-markdown-content-negotiation-apache-gratuit",
          "type": "text/html",
          "hreflang": ["fr"],
          "title": "Documentation API (français)"
        }
      ],
      "service-meta": [
        {
          "href": "https://www.rankit.fr/api/post/geo-markdown-content-negotiation-apache-gratuit.json",
          "type": "application/json",
          "title": "Représentation JSON d'article (Schema.org TechArticle)"
        }
      ],
      "license": [
        { "href": "https://creativecommons.org/licenses/by-sa/4.0/" }
      ]
    }
  ]
}

Les relations de lien utilisées (service-doc, service-meta, license) viennent du registre IANA des Link Relations. Leur sémantique est standardisée : service-doc pointe vers la documentation API lisible par un humain, service-meta vers les métadonnées additionnelles sur les endpoints API (ici, la voie de content negotiation JSON).

Le code .htaccess (Bloc 6)

Trois choses à faire côté serveur : router /.well-known/api-catalog vers le fichier physique, servir le bon Content-Type, et annoncer le catalogue via un Link header sur chaque page HTML.

.htaccess : Bloc 6 (api-catalog RFC 9727)
# --- Bloc 6 : api-catalog (RFC 9727) ---
<IfModule mod_rewrite.c>
  RewriteCond %{REQUEST_URI} ^/\.well-known/api-catalog/?$ [NC]
  RewriteRule ^\.well-known/api-catalog/?$ /linkset.json [L,E=IS_LINKSET:1]
</IfModule>

<IfModule mod_headers.c>
  Header set Content-Type "application/linkset+json; profile=\"https://www.rfc-editor.org/info/rfc9727\"" env=IS_LINKSET
  Header set Cache-Control "public, max-age=3600" env=IS_LINKSET

  # Annonce du catalogue via Link header sur chaque page HTML
  <FilesMatch "\.html$|/$">
    Header append Link '</.well-known/api-catalog>; rel="api-catalog"; type="application/linkset+json"'
  </FilesMatch>
</IfModule>

Validation curl

Trois vérifications pour confirmer le bon déploiement :

Trois tests curl pour l'api-catalog
# Test 1 : l'endpoint well-known sert le bon Content-Type
curl -I https://www.rankit.fr/.well-known/api-catalog
# Attendu : content-type: application/linkset+json; profile="..."

# Test 2 : le contenu JSON est valide et parsable
curl https://www.rankit.fr/.well-known/api-catalog | jq .

# Test 3 : la page d'accueil annonce le catalogue via Link header
curl -I https://www.rankit.fr/ | grep -i "link:"
# Attendu, parmi d'autres : link: </.well-known/api-catalog>; rel="api-catalog"; ...

Avis honnête sur le gain à l'audit

En toute transparence : déployer l'api-catalog ne fait pas monter le score global au-dessus de 100/100. Vous êtes déjà au maximum. Ce que cela change, c'est le périmètre de l'audit, qui s'étend de trois catégories (Discoverability, Content, Bot Access Control) à quatre, avec la nouvelle catégorie API/Auth/MCP/Skill Discovery également à 100/100.

Quand vous activez la catégorie API/Auth/MCP & Skill Discovery dans l'audit, Cloudflare exécute le check apiCatalog (cette section, Bloc 6) et le check agentSkills (Bloc 7, voir section suivante). Les deux passent pour rankit.fr, ce qui donne un 2/2 sur cette catégorie et confirme le déploiement. Les autres checks potentiels documentés dans les premières specs (oauthDiscovery, oauthProtectedResource, mcpServerCard, a2aAgentCard, webMcp) ne font pas partie du scope d'audit actuel à avril 2026.

La question stratégique devient donc : en ai-je besoin pour un site éditorial ? Pour rankit.fr, la réponse est nuancée. Ce n'est pas un SaaS avec une API publique OAuth, pas de serveur MCP, pas d'agent skills exposées au sens strict du terme. Mais la sortie JSON de chaque article via /api/post/{slug}.json est une API publique au sens architectural. La déclarer via la voie standardisée RFC 9727 est techniquement rigoureux, ne coûte rien, et signale une maturité technique aux agents discriminants.

Mon choix pragmatique Je déploie le Bloc 6 sur rankit.fr et j'active la catégorie API/Auth/MCP/Skill Discovery dans l'audit. L'audit retourne 100/100 sur quatre catégories (Discoverability, Content, Bot Access Control, API/Auth/MCP/Skill Discovery), avec apiCatalog validé. L'api-catalog est exposé conformément au standard IETF RFC 9727, et rankit.fr reste en avance sur la courbe des normes au fur et à mesure que de nouveaux checks sont ajoutés. Anticiper les normes fait partie du métier.

Palier bonus 2 : /.well-known/skills (Agent Skills Discovery)

3 min

Le format Agent Skills vient d'Anthropic et a été adopté par OpenAI Codex, OpenCode et désormais Cloudflare. Un skill est un dossier qui contient un fichier SKILL.md avec un frontmatter YAML (champs name et description) et des instructions Markdown. Les agents IA peuvent récupérer l'index, découvrir les skills disponibles, et charger progressivement ceux qui sont pertinents pour la tâche en cours.

Pour un site éditorial comme rankit.fr, exposer des skills revient à publier de l'expertise structurée et lisible par machine que n'importe quel agent conforme peut consommer directement. C'est un pas au-delà des articles destinés à la lecture humaine : les skills sont du savoir opérationnel qu'un agent peut appliquer immédiatement.

Structure de dossiers

Le draft Cloudflare spécifie une hiérarchie prédictible sous /.well-known/skills/ :

Structure de dossiers sur le serveur
/.well-known/skills/
├── index.json                              # Obligatoire : index des skills
├── htaccess-markdown-negotiation/
│   └── SKILL.md                            # Skill 1 : content negotiation Markdown
├── content-signal-robots-txt/
│   └── SKILL.md                            # Skill 2 : Content-Signal robots.txt
└── rfc8288-link-headers-litespeed/
    └── SKILL.md                            # Skill 3 : Link headers sous LiteSpeed

Le fichier index.json

L'index liste chaque skill disponible avec son nom, sa description et les fichiers présents dans son dossier. Chaque nom de skill doit respecter la spec de nommage Agent Skills (1 à 64 caractères alphanumériques minuscules et tirets, sans tiret de début ou de fin).

/.well-known/skills/index.json
{
  "skills": [
    {
      "name": "htaccess-markdown-negotiation",
      "description": "Configurer un .htaccess Apache (ou LiteSpeed) pour servir du Markdown aux crawlers IA via la content negotiation HTTP quand ils envoient l'en-tête Accept text/markdown...",
      "files": ["SKILL.md"]
    },
    {
      "name": "content-signal-robots-txt",
      "description": "Ajouter la directive Content-Signal dans un robots.txt pour déclarer les préférences d'usage IA...",
      "files": ["SKILL.md"]
    },
    {
      "name": "rfc8288-link-headers-litespeed",
      "description": "Configurer des Link headers RFC 8288 dans un .htaccess Apache (ou LiteSpeed)...",
      "files": ["SKILL.md"]
    }
  ]
}

Un fichier SKILL.md

Chaque SKILL.md commence par un frontmatter YAML (entre deux lignes ---) suivi d'instructions Markdown. Le frontmatter doit déclarer name et description ; tout ce qui suit est du Markdown libre lu par l'agent quand le skill s'active.

SKILL.md (extrait : htaccess-markdown-negotiation)
---
name: htaccess-markdown-negotiation
description: Configure an Apache .htaccess (or LiteSpeed) to serve
  Markdown to AI crawlers via HTTP content negotiation when they
  send the Accept text/markdown header. Use when setting up a
  website to be agent-ready, when implementing Markdown for Agents
  on shared hosting, or when targeting the Cloudflare AI Compatibility
  audit Content score (markdownNegotiation check).
---

# Apache Markdown Content Negotiation for AI Crawlers

## What this skill does

Configures an Apache HTTP server (or LiteSpeed, the default on most
French shared hosting like O2switch, Infomaniak, OVH Performance) to
serve Markdown content to AI crawlers when they send the HTTP header
`Accept: text/markdown`...

## When to use

- Setting up a website to be readable by AI crawlers
- Implementing GEO on Apache or LiteSpeed
- Reducing token consumption for AI agents fetching the site
- Passing the Content score on Cloudflare's `isitagentready.com` audit

## Implementation

[Bloc Apache .htaccess complet, exactement celui de la Section 4 de cet article]

Note : les SKILL.md sont publiés en anglais pour maximiser la lisibilité par les agents IA, qui consomment du contexte multilingue mais favorisent l'anglais pour les instructions techniques. La description et le contenu pédagogique restent traduits dans cet article en français.

Le code .htaccess (Bloc 7)

Même approche que pour le Bloc 6 : servir le bon Content-Type pour l'index et pour les fichiers SKILL.md, et fournir un alias depuis l'ancienne URL /.well-known/agent-skills/ vers le chemin officiel /.well-known/skills/, pour que l'audit et les futurs scanners trouvent les ressources au même emplacement.

.htaccess : Bloc 7 (Agent Skills Discovery)
# --- Bloc 7 : Agent Skills Discovery ---
<IfModule mod_rewrite.c>
  # Alias : /.well-known/agent-skills/ -> /.well-known/skills/
  RewriteCond %{REQUEST_URI} ^/\.well-known/agent-skills/(.+)$ [NC]
  RewriteRule ^\.well-known/agent-skills/(.+)$ /.well-known/skills/$1 [L]
</IfModule>

<IfModule mod_headers.c>
  # Content-Type pour l'index
  <Files "index.json">
    Header set Content-Type "application/json; charset=utf-8"
    Header set Cache-Control "public, max-age=3600"
    Header set Access-Control-Allow-Origin "*"
  </Files>

  # Content-Type pour les fichiers SKILL.md
  <FilesMatch "^SKILL\.md$">
    Header set Content-Type "text/markdown; charset=utf-8"
    Header set Cache-Control "public, max-age=3600"
    Header set Access-Control-Allow-Origin "*"
  </FilesMatch>
</IfModule>

Validation curl

Trois tests curl pour Agent Skills
# Test 1 : index.json est servi en application/json
curl -I https://www.rankit.fr/.well-known/skills/index.json
# Attendu : content-type: application/json; charset=utf-8

# Test 2 : l'alias /.well-known/agent-skills/ renvoie le même contenu
curl -I https://www.rankit.fr/.well-known/agent-skills/index.json
# Attendu : même réponse que le test 1

# Test 3 : SKILL.md est servi en text/markdown
curl -I https://www.rankit.fr/.well-known/skills/htaccess-markdown-negotiation/SKILL.md
# Attendu : content-type: text/markdown; charset=utf-8

Résultat de l'audit avec les quatre catégories

Avec le Bloc 6 (api-catalog) et le Bloc 7 (Agent Skills) déployés, l'audit Cloudflare peut exécuter neuf checks répartis sur quatre catégories. Lien direct pour reproduire le scan rankit.fr :

Lancer l'audit Rankit (9 checks) sur isitagentready.com

Résultat attendu :

  • Score global : 100/100 · Level 5 Agent-Native
  • Discoverability : 3/3 · sitemap, robots.txt, Link headers
  • Content : 1/1 · négociation Markdown
  • Bot Access Control : 2/2 · politique Content-Signal
  • API/Auth/MCP/Skill Discovery : 2/2 · API Catalog + Agent Skills index
Pourquoi trois vrais skills Publier un index vide juste pour valider un check produit un cercle vert et zéro valeur. Les trois skills exposés par rankit.fr (htaccess-markdown-negotiation, content-signal-robots-txt, rfc8288-link-headers-litespeed) sont extraits du contenu réel de cet article. C'est de la véritable expertise que tout agent conforme peut appliquer directement. L'audit passe et les skills aident réellement les agents downstream qui les consomment.

Questions fréquentes

3 min

Le Markdown est aujourd'hui le format que les LLM préfèrent traiter. Sa structure explicite (titres, listes, liens) est lisible directement par les modèles, sans rendu visuel ni parsing complexe, et elle réduit drastiquement la consommation de tokens par rapport au HTML, qui contient beaucoup de bruit (CSS inline, balises de mise en page, scripts, attributs ARIA). Servir une version Markdown améliore la qualité des extractions par les LLM tout en réduisant le coût d'inférence pour les agents qui consomment vos pages.

La fonctionnalité Markdown for Agents est disponible sur les plans Cloudflare Pro (240 $/an en facturation annuelle, soit 300 $/an en mensuel), Business (2 400 $/an annuel ou 3 000 $/an mensuel) et Enterprise (à partir de 24 000 $/an). Elle est explicitement exclue du plan Free dans la documentation officielle Cloudflare.

Oui. LiteSpeed est compatible avec la syntaxe Apache mod_rewrite et mod_headers. Le .htaccess décrit dans cet article a été testé en production sur rankit.fr hébergé chez O2switch (LiteSpeed mutualisé) et passe l'audit Cloudflare AI Compatibility avec succès. Aucune adaptation spécifique à LiteSpeed n'est nécessaire pour la content negotiation Markdown. Si vous utilisez LSCache, pensez à purger le cache après modification du .htaccess.

Oui. La logique du .htaccess est de servir un fichier .md jumeau quand le client envoie Accept: text/markdown. Si le .md n'existe pas, le serveur sert le HTML par défaut, sans erreur. La conversion HTML vers Markdown peut se faire manuellement, par script PHP au déploiement, ou avec des outils en ligne comme le convertisseur Rankit (rankit.fr/md/) qui transforme une URL HTML en .md propre.

Oui, sans aucune exception. Vary: Accept indique aux caches CDN, navigateurs et proxies que la réponse dépend de l'en-tête Accept envoyé par le client. Sans ce Vary, un cache peut servir la version Markdown à un visiteur humain qui arriverait sur la même URL après un crawler IA, ou inversement. C'est une faute de configuration majeure qui casse le site pour la moitié des utilisateurs. Sur LiteSpeed et Cloudflare en façade, l'oubli est immédiatement visible.

Aucun impact négatif si la configuration est correcte. Googlebot envoie Accept: text/html par défaut, donc il continue de recevoir le HTML normal. Les fichiers .md sont marqués X-Robots-Tag: noindex, nofollow pour éviter une indexation parasite. Les crawlers IA (GPTBot, ClaudeBot, PerplexityBot) ignorent ces directives et continuent de recevoir le Markdown qu'ils préfèrent. Le SEO Google reste intact, le GEO progresse.

À avril 2026 : ClaudeBot d'Anthropic envoie Accept: text/markdown depuis fin 2025, GPTBot d'OpenAI le supporte sur certains contextes agentiques, PerplexityBot et BraveBot le testent. Au-delà de la mécanique strictement automatique, l'avantage est qu'un développeur d'agent qui pointe son code sur votre site préférera explicitement votre version Markdown : c'est un signal de qualité technique pour l'écosystème entier.

La méthode la plus simple est curl. Lancez curl -I -H "Accept: text/markdown" https://votre-site.fr/ et vérifiez que l'en-tête de réponse contient content-type: text/markdown; charset=utf-8 ainsi que vary: Accept. Vous pouvez aussi utiliser l'audit gratuit Cloudflare AI Compatibility qui teste cette fonctionnalité dans la section Content. Un score 1/1 confirme que tout fonctionne. La section Vérification immédiate avec quatre commandes curl de cet article détaille la procédure complète.

La logique reste identique mais la syntaxe change. Sur Nginx, on utilise un bloc map dans http {} pour détecter Accept: text/markdown, suivi d'une directive try_files dans le bloc location. Le principe : si l'en-tête contient text/markdown et que le fichier .md existe, on le sert avec Content-Type text/markdown. La section Nginx de cet article fournit la configuration complète prête à coller.

Trois raisons. D'abord, le coût : 240 $/an pour activer une case à cocher quand 30 lignes de .htaccess font le même travail, ça n'a pas de sens budgétaire. Ensuite, la pédagogie : démontrer que le GEO n'est pas réservé aux infrastructures CDN haut de gamme, c'est aussi un message porté à mes clients PME. Enfin, le contrôle : maîtriser sa stack permet d'aller plus loin que ce que propose Cloudflare en activant x-markdown-tokens calculé sur mesure et la directive Content-Signal dans le robots.txt avec la politique éditoriale de mon choix.

Conclusion : démocratiser le GEO technique

3 min

Cet article démontre une chose simple. La négociation de contenu en HTTP, mécanisme que l'IETF a standardisé en 1999 dans la RFC 7231, suffit à faire tout ce que Cloudflare baptise Markdown for Agents. Trente lignes de configuration Apache. Aucune dépendance externe. Aucun abonnement.

Pourtant, autour de cette mécanique élémentaire, s'est construit en quelques mois un récit où le GEO technique deviendrait l'apanage de ceux qui paient un CDN premium. C'est la première chose à corriger.

Le GEO ne doit pas se transformer en marché captif. La promesse des moteurs IA, c'est précisément qu'un site éditorial bien construit, bien structuré, bien documenté, peut être cité par Claude, ChatGPT ou Perplexity à égalité avec les grands sites institutionnels. Cette promesse tombe si on conditionne la visibilité IA à un abonnement à 240 $ par an que beaucoup de TPE et freelances ne peuvent pas justifier.

La deuxième chose à corriger, c'est l'idée que le GEO serait une discipline radicalement nouvelle. Le GEO repose à 80 % sur des fondamentaux techniques anciens : un HTML sémantique propre, une structure de titres cohérente, des liens internes pensés, une vitesse de chargement raisonnable, et donc, désormais, une content negotiation correctement configurée. Tout cela se fait avec les outils standards du web, dans le serveur que vous avez déjà, en quelques heures de travail bien dirigé.

Sur rankit.fr, l'audit Cloudflare AI Compatibility est passé de 50 à 100/100 en moins d'une heure de travail effectif, en quatre paliers progressifs : .htaccess Markdown for Agents (50 → 67), robots.txt avec Content-Signal (67 → 83), Link headers RFC 8288 (83 → 100). Niveau atteint : Level 5 Agent-Native, la classification la plus élevée du système Cloudflare. C'est l'équivalent d'un signal lumineux qui s'allume sur le tableau de bord d'un site et qui dit aux agents IA : oui, vous pouvez me consommer, et dans le format que vous préférez. Pour un consultant SEO/GEO indépendant, c'est exactement le genre d'amélioration que je veux pouvoir reproduire chez chacun de mes clients PME, en facturant le temps et l'expertise, pas l'abonnement à un fournisseur tiers.

Trente lignes de .htaccess. Zéro euro de surcoût annuel. Plus 17 points d'audit Cloudflare en moins d'une heure. Cette équation me satisfait, et je pense qu'elle satisfera la grande majorité des sites Apache et LiteSpeed du monde. Le code est dans cet article. Le convertisseur Markdown est en ligne. Tout le reste, c'est juste du temps de configuration que vous économisez en suivant ce mode d'emploi.

Et le jour où vous voudrez aller plus loin avec une politique Content-Signal sur mesure dans votre robots.txt, un calcul de tokens personnalisé en PHP, ou une politique d'accès différenciée par chemin, vous serez heureux de maîtriser votre stack au lieu de dépendre des conditions générales d'un CDN. Le contrôle technique, dans ce métier, c'est aussi une forme d'indépendance professionnelle.

Frédéric Jézégou, Consultant SEO & GEO senior
Frédéric Jézégou
Freelance SEO Senior · Consultant GEO · Rankit.fr
Freelance SEO Senior, 20 ans d'expérience. J'aide les PME à renforcer leur visibilité sur Google tout en intégrant les nouveaux enjeux liés aux moteurs IA. Mon expertise couvre à la fois le SEO classique et les stratégies émergentes de présence dans les IA (GEO), que j'exploite de manière complémentaire selon les objectifs. Approche basée sur des données réelles : logs serveur, tests en compétition, audits de sites.

Pour situer ces enjeux dans l'écosystème français, consultez le classement des consultants SEO senior reconnus en France, mis à jour chaque mois selon des critères factuels (ancienneté, spécialisation sectorielle, présence dans les moteurs IA, votes mensuels de la profession).