Optimisation & SEO Core Web Vitals Performance Lighthouse

Core Web Vitals : optimiser les performances de votre site

LCP, CLS, INP : comprendre et améliorer les Core Web Vitals de Google pour booster votre référencement et l'expérience utilisateur dès aujourd'hui.

Benjamin Schweitzer Benjamin Schweitzer
Mardi 18 juin 2024
6 min de lecture
Mis à jour Aujourd'hui
Core Web Vitals : optimiser les performances de votre site

Les Core Web Vitals : le vrai impact sur votre référencement

Depuis mai 2021, Google intègre les Core Web Vitals (CWV) comme signal de classement. Ce n'est pas anecdotique : ces métriques mesurent l'expérience utilisateur réelle sur votre site, et Google considère qu'un site lent pénalise les internautes.

Un bon score CWV ne va pas faire de vous le numéro 1 sur "chaussures Paris", mais à contenu égal, le site le plus rapide passera devant. Et sur les requêtes locales et de niche, l'impact peut être déterminant.

Voici comment comprendre chaque métrique et l'optimiser concrètement.

LCP — Largest Contentful Paint

Ce que c'est

Le LCP mesure le temps de rendu du plus grand élément visible (image, vidéo ou bloc de texte) dans la fenêtre de vue initiale. C'est la métrique la plus liée à la perception de vitesse par l'utilisateur.

Seuils :

  • ✅ Bon : < 2,5 secondes
  • ⚠️ À améliorer : 2,5 - 4 secondes
  • ❌ Mauvais : > 4 secondes

Identifier l'élément LCP

// Dans la console DevTools
new PerformanceObserver((list) => {
  const entries = list.getEntries();
  const lastEntry = entries[entries.length - 1];
  console.log('LCP element:', lastEntry.element);
  console.log('LCP time:', lastEntry.startTime);
}).observe({type: 'largest-contentful-paint', buffered: true});

L'élément LCP est le plus souvent : image hero, logo, titre H1 avec grande police, vidéo poster.

Optimisations LCP concrètes

1. Précharger les ressources critiques

<!-- Dans le <head>, avant tout autre asset -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high">

2. Image hero optimisée

<!-- ❌ Mauvais -->
<div style="background-image: url(hero.jpg)"></div>

<!-- ✅ Bon -->
<img src="/hero.webp"
     srcset="/hero-480.webp 480w, /hero-768.webp 768w, /hero-1200.webp 1200w"
     sizes="100vw"
     width="1200" height="600"
     loading="eager"
     fetchpriority="high"
     alt="Titre descriptif de l'image">

3. Supprimer les chaînes de redirections

Chaque redirection ajoute un aller-retour réseau. Vérifiez que vos ressources critiques n'ont pas de redirections avec curl -I URL.

4. Optimiser le TTFB (Time To First Byte)

Le TTFB doit être < 800ms idéalement < 200ms. Leviers :

  • Cache serveur (Varnish, Redis, cache page statique)
  • CDN pour les assets statiques
  • Requêtes SQL optimisées
  • PHP OPcache activé
// .htaccess — cache des assets statiques
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/webp "access plus 1 year"
    ExpiresByType text/css "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 month"
    ExpiresByType image/svg+xml "access plus 1 year"
</IfModule>

CLS — Cumulative Layout Shift

Ce que c'est

Le CLS mesure tous les déplacements inattendus de contenu pendant le chargement. Chaque fois qu'un élément "saute" et décale ce que l'utilisateur est en train de lire, c'est du CLS.

Seuils :

  • ✅ Bon : < 0,1
  • ⚠️ À améliorer : 0,1 - 0,25
  • ❌ Mauvais : > 0,25

Causes principales

1. Images sans dimensions

<!-- ❌ Crée du CLS : l'espace n'est pas réservé -->
<img src="produit.jpg" alt="Produit">

<!-- ✅ Pas de CLS : le navigateur réserve la place -->
<img src="produit.jpg" width="400" height="300" alt="Produit">

En CSS, la solution moderne :

img {
  height: auto;
  width: 100%;
  /* Le navigateur utilise les attributs width/height pour calculer l'aspect ratio */
}

2. Publicités et embeds dynamiques

/* Réserver l'espace pour un embed avant son chargement */
.pub-container {
  min-height: 250px; /* hauteur minimale de la pub */
  min-width: 300px;
  contain: layout; /* isole l'impact des changements */
}

3. Polices web avec FOUT/FOIT

@font-face {
  font-family: 'InterVar';
  src: url('/fonts/inter-var.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-display: swap; /* Affiche police système en attendant = 0 CLS */
}

Encore mieux : utilisez size-adjust pour que la police de substitution ait les mêmes dimensions :

@font-face {
  font-family: 'InterFallback';
  src: local('Arial');
  size-adjust: 96%;           /* Ajuster pour matcher Inter */
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
}

body {
  font-family: 'InterVar', 'InterFallback', Arial, sans-serif;
}

4. Injections dynamiques de bannières ou cookies

Le bandeau cookies qui apparaît en haut et pousse le contenu vers le bas est une source classique de CLS. Utilisez position: fixed ou réservez l'espace à l'avance.

INP — Interaction to Next Paint

Ce que c'est

L'INP (qui a remplacé le FID en mars 2024) mesure la réactivité aux interactions : clic, tap, frappe au clavier. Il mesure le temps entre l'interaction et le prochain rendu visuel.

Seuils :

  • ✅ Bon : < 200ms
  • ⚠️ À améliorer : 200ms - 500ms
  • ❌ Mauvais : > 500ms

Diagnostiquer l'INP

// Surveiller les Long Tasks
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.duration > 50) { // Long task > 50ms
      console.warn('Long task:', {
        duration: entry.duration,
        startTime: entry.startTime,
        attribution: entry.attribution
      });
    }
  }
});
observer.observe({type: 'longtask', buffered: true});

Optimisations INP

1. Décomposer les tâches longues

// ❌ Bloque l'UI pendant tout le traitement
function filtrerProduits(produits, criteres) {
  return produits.filter(p => correspondAuxCriteres(p, criteres));
  // Si 10 000 produits = plusieurs centaines de ms bloqués
}

// ✅ Traitement par lots avec scheduler API
async function filtrerProduitsAsync(produits, criteres) {
  const resultats = [];
  const BATCH = 100;

  for (let i = 0; i < produits.length; i += BATCH) {
    const lot = produits.slice(i, i + BATCH);
    resultats.push(...lot.filter(p => correspondAuxCriteres(p, criteres)));

    // Céder le contrôle au thread principal entre chaque lot
    if ('scheduler' in window) {
      await scheduler.yield();
    } else {
      await new Promise(r => setTimeout(r, 0));
    }
  }
  return resultats;
}

2. Debounce sur les événements fréquents

// ❌ Déclenche à chaque frappe
input.addEventListener('input', rechercherProduits);

// ✅ Attendre 300ms d'inactivité
let timer;
input.addEventListener('input', (e) => {
  clearTimeout(timer);
  timer = setTimeout(() => rechercherProduits(e.target.value), 300);
});

3. Utiliser les Web Workers pour les calculs lourds

// main.js
const worker = new Worker('/workers/calcul-prix.js');

worker.postMessage({ produits, remises });
worker.onmessage = (e) => {
  afficherPrix(e.data.resultats);
};

// /workers/calcul-prix.js
self.onmessage = (e) => {
  const { produits, remises } = e.data;
  const resultats = calculerPrixAvecRemises(produits, remises);
  self.postMessage({ resultats });
};

FID — First Input Delay (déprécié mais encore mesuré)

Bien que remplacé par INP, le FID est encore présent dans les rapports historiques. Il mesure le délai entre la première interaction et la réaction du navigateur.

L'optimisation est identique à INP : réduire les Long Tasks, différer le JavaScript non essentiel.

Outils de mesure et monitoring

Mesure en labo (synthétique)

# Lighthouse CLI
npx lighthouse https://votresite.fr   --output=html   --output-path=./rapport-perf.html   --chrome-flags="--headless"

# WebPageTest API
curl "https://www.webpagetest.org/runtest.php?url=https://votresite.fr&f=json&k=VOTRE_API_KEY"

Mesure terrain (Real User Monitoring)

Les données terrain depuis Chrome User Experience Report (CrUX) :

// Via l'API PageSpeed Insights
fetch(`https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://votresite.fr&strategy=mobile&key=VOTRE_CLE`)
  .then(r => r.json())
  .then(data => {
    const metrics = data.loadingExperience.metrics;
    console.log('LCP:', metrics.LARGEST_CONTENTFUL_PAINT_MS.percentile);
    console.log('CLS:', metrics.CUMULATIVE_LAYOUT_SHIFT_SCORE.percentile);
    console.log('INP:', metrics.INTERACTION_TO_NEXT_PAINT.percentile);
  });

Dashboard de monitoring continu

Je recommande de combiner :

  • Google Search Console : données CWV officielles, segmentées par device
  • Sentry Performance : monitoring en temps réel avec traces détaillées
  • SpeedCurve : monitoring continu avec alertes sur régressions

Stratégie d'optimisation progressive

Ne cherchez pas à tout optimiser d'un coup. Voici la séquence efficace :

Sprint 1 (impact rapide)

  • Convertir les images en WebP et ajouter width/height
  • Activer la compression gzip/brotli
  • Activer OPcache sur PHP
  • Ajouter les headers de cache sur les assets statiques
Sprint 2 (optimisation avancée)
  • Mettre en place un CDN (Cloudflare gratuit suffit pour commencer)
  • Optimiser les requêtes SQL (EXPLAIN, index manquants)
  • Différer le JavaScript non critique (defer/async)
  • Précharger les polices et images above-the-fold
Sprint 3 (tuning fin)
  • Implémenter le Critical CSS inline
  • Passer à HTTP/2 ou HTTP/3
  • Utiliser le Resource Hints (prefetch, preconnect)
  • Optimiser l'ordre de chargement des ressources
Chaque sprint devrait améliorer de 20-40% votre score. La mesure systématique avant/après chaque optimisation est indispensable pour savoir ce qui fonctionne vraiment.


Pour aller plus loin : PageSpeed Insights

Cet article vous a plu ?

Donnez-lui une note, ça m'aide vraiment !

5/5 1 avis
Partager l'article