optimisation core web vitals 9 min

VM Ubuntu avec VirtualBox : des Core Web Vitals mesurés au plus près du réel

Mesurer le LCP sur ton navigateur local fausse ton diagnostic. Une VM Ubuntu VirtualBox te donne un banc d'essai reproductible pour débuguer tes Core Web Vitals.

Par Julien Morel
Partager

On a perdu une demi-journée sur un LCP à 4,2 secondes. Le site tournait sur un Next.js 14 tout ce qu’il y a de classique, déployé sur un VPS Ubuntu 22.04 derrière Nginx. En local sur mon Mac, Lighthouse affichait 1,9 seconde. L’écart ne venait pas du bundle, pas du lazy-loading, pas d’une police. Il venait de l’absence d’un environnement réseau honnête. La machine hôte et le serveur de dev partageaient le même localhost, aucun round-trip réaliste, aucune limitation de backlog Nginx, aucune interférence de l’IO disque sur les lectures de fichiers statiques. Pour réconcilier les chiffres, il a fallu sortir du navigateur local et exécuter les mesures dans une VM Ubuntu pilotée par VirtualBox. La méthode a changé notre manière de valider les optimisations Core Web Vitals.

Pourquoi ton navigateur local te ment sur le LCP

Un test Lighthouse lancé depuis Chrome sur ton poste de dev traverse une pile qui n’a rien à voir avec celle de production. Le serveur de développement (webpack-dev-server, Vite, turbopack) sert les assets en mémoire, avec des optimisations désactivées et une compression absente ou partielle. Le navigateur attaque 127.0.0.1 sans résolution DNS, sans négociation TLS, et avec une latence proche de zéro. Résultat : le TTFB s’effondre artificiellement, et le Largest Contentful Paint paraît excellent.

Ce n’est pas un simple écart de marge. Sur un projet e-commerce avec une page d’accueil chargée à 3,8 secondes sur mobile selon la Search Console, le même test en local renvoyait 1,7 seconde. Le delta ne venait pas d’un réseau 4G simulé (Lighthouse le fait déjà). Il venait du fait que le serveur local ne gère ni le keepalive_timeout de Nginx, ni la latence réelle de lecture d’un fichier depuis un volume ext4, ni la queue de requêtes concurrentes que le VPS absorbe quand le bot Google arrive avec 40 connexions simultanées. Le navigateur local te donne une vue idéalisée, là où la métrique de terrain te dit la vérité.

Les outils de type PageSpeed Insights ou WebPageTest sont utiles, mais ils mesurent un point de passage unique. Ils ne t’offrent pas la possibilité de modifier la configuration serveur, de changer la stratégie de cache HTTP, ou d’instrumenter le backend pour comprendre l’origine d’un INP qui dérape. La VM locale, elle, te rend ce contrôle.

Ce qu’une VM Ubuntu reproduit que ton Mac ne peut pas

Une machine virtuelle exécutant le même OS que le serveur cible reproduit trois couches critiques pour le diagnostic des Core Web Vitals.

La première, c’est la couche réseau. Une requête HTTP même intra-machine passe par une interface réseau virtualisée, avec ses propres buffers, son MTU, et surtout une latence mesurable. Tu peux y appliquer des règles de trafic : limitation de bande passante, délai, perte de paquets. Avec tc sous Linux, tu cartographies précisément les conditions d’un mobile lent sans dépendre du throttling de Chrome DevTools, qui ne s’applique qu’au navigateur et pas aux échanges entre le reverse proxy et l’application.

La seconde couche, c’est le serveur HTTP. Un Nginx ou un Apache configuré avec les mêmes directives que la production (compression Brotli, cache des statiques, timeouts) réagit différemment du next start en mode développement. La VM te permet d’activer HTTP/2, de tester l’effet du sendfile, du tcp_nodelay et du gzip_min_length sur le temps de chargement des chunks JS les plus lourds. Tu observes directement le nombre de requêtes que le serveur accepte avant de mettre en file, et son impact sur le TBT quand le navigateur attend un bundle bloquant.

La troisième couche, c’est le système de fichiers. Une application Next.js ou Remix qui lit une centaine de fichiers markdown ou des assets statiques se comporte différemment sur APFS et sur ext4. La VM Linux reproduit le temps d’accès disque, le cache inode, et le comportement du lstat qui peuvent allonger le TTFB de 100 à 200 ms sur les pages dynamiques servies par getServerSideProps. Ces 200 ms, invisibles en local, font la différence entre un classement « bon » et « à améliorer » dans la Search Console.

Installer Ubuntu avec VirtualBox : le strict nécessaire pour un dev web

On ne va pas refaire la doc d’Oracle. Ce qui compte pour nos métriques, c’est la configuration réseau, la RAM allouée et le partage des dossiers.

Crée une machine avec 4 Go de RAM minimum si tu veux exécuter un serveur Node.js et un reverse proxy. Choisis l’image Ubuntu 24.04 LTS. Dans les réglages réseau, commence par le mode « Accès par pont » plutôt que NAT. Le pont expose la VM comme une machine distincte sur ton réseau local, avec sa propre IP. C’est indispensable pour que le test Lighthouse s’exécute depuis ton navigateur hôte vers l’IP de la VM, en traversant une vraie pile TCP complète, sans les raccourcis du NAT.

Une fois l’OS installé, clone ton dépôt applicatif. Si tu travailles sur une app React avec un state manager comme Zustand et que tu cherches à isoler un INP causé par un rendu serveur partiel, tu as tout intérêt à exécuter le build de production dans la VM, pas sur ton poste. La référence au state management avec Zustand est pertinente ici : une mauvaise sérialisation du store au premier rendu peut générer un TBT de 800 ms que tu ne verras que si le serveur évalue les chemins critiques côté serveur, dans les bonnes conditions CPU.

Installe les paquets système qui manquent (nginx, certbot si tu as besoin de TLS local, htop, sysstat). Lance le serveur en mode production. Vérifie avec curl -I que les en-têtes de cache et de compression sont identiques à ceux du VPS.

⚠️ Attention : ne jamais mesurer un Core Web Vital avec le mode développement activé sur le serveur Node. Les transformations à la volée et le hot module replacement faussent le nombre de requêtes et le poids des bundles. Seul un build de production exécuté dans la VM est valable.

Mesurer les Core Web Vitals dans la VM : le banc d’essai reproductible

Une fois le serveur prêt, le protocole de mesure est simple. Depuis ton poste hôte, tu vises l’IP de la VM sur le port exposé. Tu lances Lighthouse en mode headless via le CLI ou depuis les DevTools, en cochant « Désactiver le cache » mais en conservant le throttling réseau « Simulé Fast 3G » si tu veux un profil mobile. L’important est d’avoir un script reproductible qui effectue plusieurs passes et conserve la médiane, pas le meilleur score.

lighthouse http://192.168.1.42:3000/ --preset=desktop --throttling-method=devtools --output=json --chrome-flags="--headless" --quiet

Sur un projet Next.js dont l’image principale était lazy-loadée avec fetchpriority="low", le test en VM a révélé un LCP dégradé de 900 ms par rapport au test localhost. La cause : le lazy-loading natif chargeait l’image après le premier rendu, mais la latence réseau intra-VM (même minime) reculait suffisamment le décodage pour que Lighthouse considère un autre élément comme candidat LCP, plus petit et moins optimisé. Sur localhost, l’image arrivait dans la même frame de rendu.

Autre avantage : tu peux instrumenter le backend. Pendant le test Lighthouse, un strace sur le processus Node montre les appels système qui ralentissent la réponse. Sur un appel à une API externe non mockée, le TTFB est directement corrélé au temps de résolution DNS et au handshake TLS que la VM réplique parfaitement. C’est ce niveau de détail qui t’évite d’accuser à tort le poids du bundle JS.

Le piège du réseau NAT : pourquoi tu dois simuler une vraie latence

Beaucoup de devs gardent le mode réseau NAT par défaut parce qu’il est simple. La VM a accès à Internet, l’hôte peut joindre les services exposés via le « Port forwarding ». Mais ce mode introduit un court-circuit dans la pile TCP : les paquets sont traités par un module kernel de l’hôte qui réécrit les adresses à la volée, ce qui lisse la latence et masque les défauts de mise en file sur la machine invitée.

Avec le NAT, un test Lighthouse depuis l’hôte vers la VM sur le port 3000 forwardé montre un TTFB régulier, souvent inférieur à 80 ms. Dès que tu bascules en pont, le même test voit son TTFB grimper à 120-150 ms, simplement à cause de la traversée complète de la pile réseau Linux, de l’interruption virtuelle et du traitement par Nginx. Ces 40-70 ms de différence ne sont pas un artefact. C’est exactement ce qui se produit quand la requête utilisateur passe d’un datacenter à un autre, ou d’un load balancer à un backend.

Pour tester de manière fiable l’impact du TLS, installe un certificat auto-signé et active HTTPS dans Nginx. Le handshake ajoute une latence que le NAT gomme partiellement à cause du traitement en espace utilisateur côté hôte. La VM en pont te donne le vrai coût du ssl_early_data et de la session TLS reprise. Et tu verras rapidement si la meilleure stratégie est un CDN en frontal ou un stale-while-revalidate agressif sur les assets.

Quand la VM devient ton labo d’optimisation continue

Au-delà du diagnostic ponctuel, une VM Ubuntu snapshotée devient un environnement de régression pour les Core Web Vitals. Avant chaque mise en production, un script shell lance le build, démarre les services, exécute le test Lighthouse et compare le rapport JSON à la baseline. Si le LCP médian dépasse un seuil, le pipeline alerte avant la fusion.

Sur une app React avec un state manager global, une modification du store Zustand qui ajoute un calcul synchrone au montage peut faire bondir le Total Blocking Time. La VM permet de le mesurer avec précision parce qu’elle dispose d’une allocation CPU fixe, contrairement à ton poste de dev où les ressources varient selon ce que Docker, Slack et Spotify consomment à côté. C’est cette stabilité qui permet de comparer deux builds en toute confiance.

Certains IDE comme Cursor ou des outils comme Claude Code aident à coder plus vite, mais ils ne remplacent pas la validation dans un environnement maîtrisé. La VM reste le filet de sécurité pour valider que l’optimisation n’a pas introduit une régression silencieuse, en particulier quand on change la stratégie de chargement des polices ou le fetchpriority de l’image principale. La méthode est brutale mais elle ne ment pas. Et c’est ce qu’on demande à un labo de mesure.

Questions fréquentes

Pourquoi ne pas utiliser Docker plutôt qu’une VM complète ? Docker partage le noyau de l’hôte, ce qui efface la couche réseau indépendante et les spécificités du scheduler Linux. Le TTFB mesuré depuis un conteneur Docker vers l’application est souvent plus bas qu’une configuration avec un reverse proxy sur un VPS distinct. La VM complète reste le plus proche du comportement d’un serveur dédié.

La virtualisation n’ajoute-t-elle pas une surcharge qui fausse les mesures ? L’overhead CPU de VirtualBox est de l’ordre de quelques pourcents sur les instructions entières, et il est stable. L’essentiel pour les Core Web Vitals est la latence réseau reproductible, bien plus que les micro-variations de temps d’exécution JS. Ce qu’on perd en finesse CPU, on le gagne en réalisme réseau, et c’est ce qui compte pour le LCP et le TTFB.

Peut-on mesurer l’INP dans la VM avec des interactions réelles ? L’INP nécessite du trafic utilisateur réel pour être pertinent. La VM sert surtout à débuguer un TBT élevé qui présage un mauvais INP, en reproduisant des interactions scriptées (clic, frappe) avec Puppeteer. C’est un indicateur avancé, pas une mesure de terrain.

Articles similaires

Julien Morel

Julien Morel

Ancien dev front React passé SEO technique après une migration e-commerce qui a fait perdre 60% du trafic organique à son employeur en une nuit (fichier robots.txt oublié en staging). Depuis, il écrit pour que ça n'arrive à personne d'autre et teste sur ses propres side-projects avant de publier quoi que ce soit.

Cet article est publie a titre informatif. Faites vos propres recherches avant toute decision.