Image de couverture : Optimisez vos pages pour les agents IA avec Symfony : la négociation de contenu HTTP plutôt qu'un abonnement Cloudflare
IA & Ingénierie

Optimisez vos pages pour les agents IA avec Symfony : la négociation de contenu HTTP plutôt qu'un abonnement Cloudflare

15 juillet 2026
3 min de lecture
5 vues
Sébastien Muler

Quand vos visiteurs ne lisent plus vos pages, ils les analysent

Une part croissante du trafic web ne provient plus d'humains. Les LLMs (Large Language Models) — ceux qui propulsent les assistants IA, les moteurs de recherche intelligents et les agents autonomes — fetchez vos pages HTML pour en extraire le contenu utile. Et ce qu'ils reçoivent, c'est souvent 60 Ko de balisage pour quelques centaines de mots d'information réelle : classes Bootstrap, icônes SVG, bandeau cookie, footer… autant de tokens gaspillés que l'agent tokenise et ignore.

Cloudflare a identifié ce problème en février 2025 avec sa fonctionnalité Markdown for Agents : lorsqu'un client envoie l'en-tête Accept: text/markdown, leur edge convertit la réponse HTML en markdown à la volée, réduisant le volume de tokens d'environ 80 %. Pratique — mais réservé aux plans Pro et au-dessus.

Bonne nouvelle : il n'y a aucune magie propriétaire là-dedans. C'est de la négociation de contenu HTTP, un mécanisme standardisé depuis les débuts du web. Et si votre application tourne sur Symfony, vous pouvez reproduire ce comportement en une après-midi, avec un seul Event Subscriber et un package Composer.

La négociation de contenu HTTP : le contrat avant le code

Le principe est simple : une même URL sert plusieurs représentations d'une ressource selon ce que le client déclare accepter via l'en-tête Accept.

GET / HTTP/1.1
Accept: text/html          → réponse HTML classique

GET / HTTP/1.1
Accept: text/markdown      → réponse en markdown

Pas de /md/ dans l'URL, pas de route dédiée. Le RFC 7231 définit ce mécanisme depuis 2014. C'est le navigateur qui envoie text/html par défaut ; les agents IA, eux, peuvent envoyer text/markdown pour signaler leur intention d'obtenir une version épurée.

Trois détails techniques méritent attention avant d'implémenter :

  1. Le type MIME text/markdown n'est pas encore standardisé de façon définitive — le RFC 7763 le définit, mais certains agents envoient text/plain ou des variantes. Votre implémentation doit être tolérante.
  2. La réponse markdown doit porter le bon Content-Type pour éviter que les proxies ou CDN ne la mettent en cache comme du HTML.
  3. L'en-tête Vary: Accept est indispensable : il indique aux caches (navigateurs, Varnish, CDN) que la réponse varie selon cet en-tête, évitant de servir du markdown à un navigateur ou du HTML à un agent.

Implémentation Symfony : un Event Subscriber suffit

L'architecture Symfony s'y prête parfaitement. Un Event Subscriber écoutant KernelEvents::RESPONSE intercepte chaque réponse et la transforme si nécessaire.

Dépendance Composer

Pour la conversion HTML → Markdown, le package league/html-to-markdown est la référence :

composer require league/html-to-markdown

Le Subscriber

<?php

namespace App\EventSubscriber;

use League\HTMLToMarkdown\HtmlConverter;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class MarkdownForAgentsSubscriber implements EventSubscriberInterface
{
    public function __construct(private HtmlConverter $converter) {}

    public static function getSubscribedEvents(): array
    {
        return [KernelEvents::RESPONSE => 'onKernelResponse'];
    }

    public function onKernelResponse(ResponseEvent $event): void
    {
        $request = $event->getRequest();
        $response = $event->getResponse();

        // Vérifier si le client accepte markdown
        $accept = $request->headers->get('Accept', '');
        if (!str_contains($accept, 'text/markdown')) {
            return;
        }

        // Ne transformer que les réponses HTML
        if (!str_contains($response->headers->get('Content-Type', ''), 'text/html')) {
            return;
        }

        $html = $response->getContent();
        $markdown = $this->converter->convert($html);

        $response->setContent($markdown);
        $response->headers->set('Content-Type', 'text/markdown; charset=utf-8');

        // Crucial : indiquer aux caches que la réponse varie selon Accept
        $response->setVary('Accept', false);
    }
}

Déclarer le service

Si l'autowiring est activé (c'est le cas par défaut dans Symfony), le subscriber est détecté automatiquement. Sinon, ajoutez-le dans services.yaml :

App\EventSubscriber\MarkdownForAgentsSubscriber:
    tags: [kernel.event_subscriber]

Les trois pièges à éviter en production

1. L'en-tête Vary oublié

Sans Vary: Accept, un reverse proxy (Varnish, Nginx, votre CDN) peut mettre en cache la version markdown et la servir à tous les visiteurs suivants — y compris les humains avec leurs navigateurs. Résultat : une page incompréhensible. Toujours ajouter cet en-tête.

2. La qualité de la conversion HTML → Markdown

Les pages HTML modernes contiennent des éléments inutiles pour un agent : navigation, footer, scripts, styles inline. Configurez HtmlConverter pour les exclure :

$converter = new HtmlConverter([
    'strip_tags' => true,
    'suppress_errors' => true,
]);

Considérez aussi cibler uniquement le contenu principal (balise <main> ou <article>) avant conversion, pour maximiser le ratio signal/bruit.

3. Ne pas transformer les réponses d'API ou les assets

Votre subscriber doit s'assurer de ne traiter que les réponses HTML de pages entières. Vérifiez le Content-Type de la réponse avant toute transformation, et ignorez les requêtes XHR ou les endpoints JSON.

Ce que ça change concrètement

Pour une page type de 1000 mots dans une application Symfony avec Bootstrap et quelques composants UI, le gain est significatif :

Format Taille approximative Tokens estimés
HTML complet ~60 Ko ~15 000 tokens
Markdown extrait ~8 Ko ~2 000 tokens

Un ratio de 7:1 qui se traduit directement en coûts réduits pour les agents qui consomment vos contenus, et en une meilleure qualité de réponse puisqu'il y a moins de bruit à filtrer.

Au-delà de l'économie de tokens, c'est aussi un signal positif pour les moteurs de recherche IA et les crawlers nouvelle génération qui privilégient les sources faciles à parser.

Conclusion

Ce que Cloudflare vend comme une fonctionnalité Pro n'est, en réalité, qu'une bonne application d'un standard HTTP vieux de trente ans. Symfony vous donne tous les outils pour l'implémenter proprement : un Event Subscriber, une dépendance Composer légère, et quelques dizaines de lignes de code.

L'article original de Tom Girou sur DEV.to détaille l'implémentation complète avec les tests associés — une lecture complémentaire recommandée si vous souhaitez embarquer cette feature en production avec une couverture de tests solide.

Avant d'ouvrir votre carte bleue pour un plugin ou un plan CDN premium, vérifiez ce que votre framework gère nativement. Dans l'écosystème Symfony/PHP, la réponse est souvent : déjà tout.

Partager cet article