J’ai passé un après-midi à débugger un LCP qui grimpait à 4,1 secondes sur le site d’une association. Le coupable : un plugin de mise en cache qui poussait un script de consentement aux cookies dans le <head> avant toute ressource critique, parce que sa logique de priorité datait de PHP 7.1. Ce site tournait sous un CMS open source réputé « performant », avec un thème soi-disant léger et sept plugins de performance superposés. À ce stade, ce n’est plus un site : c’est une usine à gaz dont aucun cache HTTP ne peut corriger l’architecture HTML défaillante.
Tu me diras qu’un développement ad-hoc, c’est long, cher et que le client ne pourra jamais modifier ses pages. On va poser le problème autrement. La question n’est pas « est-ce que le CMS nous fait gagner du temps au début », mais « dans deux ans, qui passera ses journées à enlever du JavaScript mort injecté par un slider que l’équipe éditoriale n’utilise plus ». Et surtout, quel que soit le choix initial, c’est le rendu HTML final que Googlebot va digérer pour calculer vos Core Web Vitals. Si vous traitez le front-end d’un CMS comme une boîte noire, vous offrez aux algorithmes de classement un LCP qui fluctue à chaque mise à jour automatique d’un thème ou d’un constructeur de pages.
Le vrai coût d’un CMS « prêt à l’emploi » se lit sur votre waterfall
Ouvre la Network Tab de ton DevTools, coche « Désactiver le cache », recharge une page d’accueil propulsée par un CMS open source classique. Tu verras défiler une cascade de feuilles de style que tu n’as jamais vues, un jquery.min.js chargé même si aucun script de la page ne l’utilise, et une ribambelle de polices Google dont la moitié ne sert qu’au défilement d’un carrousel. C’est le prix de l’abstraction.
Un CMS fonctionne avec un système de hooks, de filtres et d’actions qui empilent les sorties HTML, CSS et JS à chaque couche du rendu. Même avec un thème épuré, les premières requêtes réseau démarrent rarement avant 500 millisecondes de traitement serveur. On croit gagner du temps de développement en installant un plugin de lazy-loading. En réalité, on ajoute souvent un fichier supplémentaire de 12 ko qui va s’exécuter après le DOMContentLoaded et retarder l’interaction jusqu’à ce que le load complet soit émis. Pour Google, un TBT qui traîne au-dessus de 200 ms sur mobile, c’est un signal qui plombe vos Core Web Vitals.
Le développement sur mesure, lui, part d’un fichier vide. Vous décidez que le <head> contient uniquement le strict nécessaire pour le LCP : un style critique inline, une balise preload pour l’image héro, et basta. Le reste du CSS est différé, le JavaScript chargé en async ou monté en hydration partielle uniquement là où une interaction le justifie. Ce n’est pas du micro-optimisation : c’est ce qui fait passer un LCP de 3,8 secondes à 1,2 seconde sur un serveur mutualisé basique, sans toucher au matériel.
Pourquoi un HTML sur mesure bat tous les caches de WordPress dès le premier octet
Le dogme classique veut qu’un bon cache de page résolve la performance d’un CMS. Empilez Varnish, un CDN et un plugin de minification, et vous retombez sur des temps de réponse dégradés dès que Googlebot explore une URL hors cache, comme une facette produit ou une variante paginée. La réalité physique ne change pas : pour générer le HTML, le CMS exécute PHP, interroge une base MySQL, construit le DOM serveur, injecte les sidebars et les scripts de suivi, avant de renvoyer le premier octet. Sur un site à 15 plugins actifs, un TTFB à 800 millisecondes au 75e centile est monnaie courante, même avec un cache en amont.
Un développement ad-hoc statique, lui, n’interroge aucune base de données au moment de la requête. Une fois le build terminé, le serveur sert un fichier HTML prêt à l’emploi. Le TTFB se réduit à la latence réseau et au temps de traitement du serveur HTTP, soit régulièrement moins de 100 millisecondes depuis un CDN correctement configuré. Ce n’est pas une amélioration incrémentale : c’est la différence entre un site qui réagit instantanément et un site qui donne l’impression de « réfléchir » au visiteur.
Et là où le CMS laisse traîner des règles CSS inutilisées de trois frameworks différentes (le thème, le builder, le plugin de formulaire), l’approche sur mesure purifie le CSS final avec exactement les classes utilisées dans le rendu réel. On est tombés de notre chaise la première fois qu’on a vu une page d’accueil passer de 230 ko de CSS à 14 ko sans perdre le moindre style visuel. Ce n’est pas de la magie, c’est l’absence de feuilles mortes que personne n’ose retirer dans le CMS par peur de casser un module inconnu.
Le jour où le plugin d’optimisation devient votre pire ennemi
⚠️ Attention : Les plugins de performance fonctionnent en surcouche d’une pile existante. Ils ne corrigent pas la racine du mal, ils tentent d’en atténuer les symptômes par minification, concaténation et différé automatique. Le premier conflit de script entre un plugin de lazy-loading et un autre qui manipule le DOM toastera en silence, laissant un INP dégradé que vous ne verrez jamais si vous ne testez pas manuellement sur mobile avec une limitation CPU.
J’ai littéralement perdu une matinée sur un site e-commerce où un plugin « tout-en-un » fusionnait des scripts de tracking avec le code du panier, provoquant une erreur silencieuse qui empêchait l’ajout au panier pour 3 % des visiteurs sous iOS 17. Le dev ad-hoc n’a pas ce problème, parce qu’il n’y a pas de plugin à mettre à jour : le code métier et le code de performance ne font qu’un, embarqués dans une même pipeline de build qui rejette les régressions avant la mise en production.
L’INP ne pardonne pas les couches d’abstraction
Un clic sur un menu hamburger dans un template WordPress peut déclencher 200 millisecondes de délai à cause de la délégation d’événements jQuery et de la propagation dans un arbre DOM alourdi. En développement ad-hoc, un event listener natif en écoute directe sur l’élément interactif, c’est moins de 2 millisecondes. L’écart vient de l’épaisseur logicielle que le CMS ajoute pour garantir la compatibilité avec n’importe quel plugin.
Quand on choisit l’ad-hoc, on peut opter pour un framework minimal ou simplement du JavaScript vanilla pour les interactions critiques, en réservant l’écosystème composants (React, Vue) aux zones où l’interactivité justifie l’hydratation, avec un pattern d’état géré par Zustand qui limite les re-rendus et garde le bundle maigre.
Ad-hoc ne veut pas dire repartir de zéro chaque fois
Le vrai frein au développement sur mesure, c’est la perte d’autonomie des équipes éditoriales. On vous dit qu’avec le CMS tout le monde peut créer des pages. Mais dans les faits, qui ose toucher au constructeur visuel sans avoir peur de planter le slider ou de décaler la médiane du LCP ?
Le compromis moderne, c’est de construire un front-end en générateur statique (Astro, Nuxt, Eleventy) avec un headless CMS léger pour le contenu structurant, ou même en pur Markdown versionné via Git. L’équipe éditoriale remplit des champs bien délimités, et le pipeline de build recrache un HTML optimisé, sans sidebars inutiles, sans script tiers qui déborde. C’est ce qu’on a fait sur notre propre site de veille technique : on a gardé la souplesse de rédaction via un CMS headless, mais le rendu final, lui, est un jeu de composants HTML statiques qu’on contrôle à la virgule. Le LCP est passé sous 1,5 seconde sans qu’on fasse de concession sur la productivité des rédacteurs.
Et quand on code le front-end soi-même, on garde la main sur les choix d’outillage. On peut par exemple décider de s’appuyer sur un assistant de code comme Claude Code pour générer les squelettes de composants, ou structurer son projet dans un IDE qu’on a comparé Claude Code vs Cursor pour choisir celui qui nous fait gagner du temps sans nous éloigner du standard du Web. Le CMS, lui, impose sa propre logique de développement et son éditeur propriétaire.
Le même contenu, deux stacks, un écart de 45 points sur Lighthouse
J’ai monté un petit site de veille personnelle avec une soixantaine d’articles. Dans sa première version, un WordPress 6.6 avec GeneratePress et un cache agressif, le LCP oscillait entre 2,6 et 3,1 secondes selon les pages, et l’indexation conditionnelle amassait une centaine d’URLs inutiles à cause de pagination fantôme. Le tout sur un hébergement à 8 euros par mois.
La seconde version, c’est un site statique assemblé avec Astro et du Markdown, hébergé sur Cloudflare Pages. Aucune base de données, aucun script de tracking lourd, aucun constructeur de thème. Le LCP est tombé à 1,0 seconde sur le 90e percentile, l’indexation est devenue parfaitement saine (seules les pages articles canoniques émergent), et le score Lighthouse Performance ne descend plus sous 98. Cette migration m’a pris trois jours de développement, le temps de réécrire les templates en Astro et de reprendre le balisage sémantique.
Ce n’est pas une preuve universelle, c’est un ordre de grandeur. Mais il illustre la règle : le CMS donne des performances « acceptables » jusqu’au jour où un seuil de Core Web Vitals est franchi et qu’on n’arrive plus à diagnostiquer le coupable dans l’empilement de plugins. L’ad-hoc vous redonne la capacité de mesurer chaque octet, chaque requête et chaque milliseconde d’interaction, parce que c’est vous qui avez écrit le code qui les produit.
Questions fréquentes
Dois-je migrer un site média existant sous WordPress vers du statique si son LCP est à 2,5 secondes ?
Pas nécessairement. Un LCP sous 2,5 secondes reste dans les clous du rapport Search Console « À améliorer », et une migration comporte des risques SEO qu’il faut chiffrer. Commencez par identifier la part de la lenteur qui vient du backend CMS (TTFB élevé) par opposition au poids des ressources (images, JS). Si le TTFB est inférieur à 800 ms et que le principal coupable est une image de héros non optimisée, le travail d’optimisation sera plus léger que de tout reconstruire. En revanche, si le TTFB monte au-delà d’une seconde au 75e centile alors que le cache est chaud, une génération statique changera la donne.
Un CMS headless avec un front-end React n’est-il pas un développement ad-hoc déguisé ?
C’en est une forme, mais avec un risque : l’hydratation complète de React côté client peut ruiner l’INP si le bundle initial est trop lourd. L’ad-hoc bien maîtrisé choisit où l’hydratation est nécessaire et laisse le reste en HTML statique. Un CMS headless couplé à un générateur de site statique (Astro, Nuxt avec hybrid rendering) permet de garder le contrôle du front-end sans imposer un runtime monolithique. Ce n’est pas le CMS qui fait la performance, c’est l’architecture de rendu que vous bâtissez autour.