Comprendre et maîtriser le référencement naturel de A à Z : crawl, indexation, Core Web Vitals, données structurées, maillage interne et Search Console. La formation technique pour développeurs qui veulent que leur site soit trouvé.
Depuis 2021, les Core Web Vitals font partie de l'algorithme de classement de Google. Ce sont trois métriques de performance mesurées sur des données réelles (vraies visites de Chrome), pas sur des tests en laboratoire.
Un mauvais score ne vous fait pas tomber de la première page à la dixième — mais entre deux pages de qualité similaire, celle avec de bons CWV l'emporte. C'est un signal de départage.
Ce que ça mesure : le temps d'affichage du plus grand élément visible dans la fenêtre — généralement l'image hero, le titre H1, ou un bloc de texte large.
Objectif : ≤ 2,5 secondes.
< 2,5s → Bon (vert)
2,5-4s → À améliorer (orange)
> 4s → Mauvais (rouge)Causes fréquentes de LCP lent :
| Cause | Impact | Solution |
|---|---|---|
| Image hero lourde (PNG 2Mo) | Majeur | WebP + compression |
| TTFB > 600ms (serveur lent) | Majeur | Cache, CDN |
| CSS render-blocking | Modéré | <link rel=preload> |
loading="lazy" sur l'image hero | Modéré | Supprimer lazy sur hero |
Polices en blocking | Mineur | font-display: swap |
Solutions concrètes :
<!-- ✅ Précharger l'image LCP dès le head -->
<link
rel="preload"
as="image"
href="/hero.webp"
fetchpriority="high"
>
<!-- ✅ L'image hero : high priority, pas de lazy -->
<img
src="/hero.webp"
alt="Description"
width="1200"
height="600"
fetchpriority="high"
>
<!-- ❌ Jamais loading=lazy sur l'image au-dessus du fold -->
<img src="/hero.webp" loading="lazy">/* ✅ Font-display swap — le texte s'affiche immédiatement avec la police système */
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom.woff2') format('woff2');
font-display: swap;
}Ce que ça mesure : la stabilité visuelle de la page — combien les éléments se déplacent pendant le chargement, au total.
Objectif : ≤ 0,1
< 0,1 → Bon (vert)
0,1-0,25 → À améliorer (orange)
> 0,25 → Mauvais (rouge)Vous avez sûrement vécu un mauvais CLS : vous êtes sur le point de cliquer sur un lien, une bannière se charge et pousse le contenu vers le bas — vous cliquez sur la mauvaise chose.
Causes fréquentes :
Images sans dimensions :
<!-- ❌ Le navigateur ne connaît pas la taille avant le chargement → décalage -->
<img src="photo.jpg" alt="...">
<!-- ✅ Dimensions explicites — le navigateur réserve l'espace -->
<img src="photo.webp" alt="..." width="800" height="450">Iframes et publicités sans dimensions :
<!-- ❌ -->
<iframe src="video.html"></iframe>
<!-- ✅ -->
<div style="aspect-ratio: 16/9; position: relative">
<iframe src="video.html" style="position: absolute; inset: 0; width: 100%; height: 100%"></iframe>
</div>Polices qui s'échangent (FOUT) :
Le texte s'affiche d'abord avec une police système (petite), puis avec la police custom (plus grande) — le texte repousse les éléments qui suivent. Solution : font-display: swap + polices préchargées.
Éléments injectés dynamiquement :
// ❌ Injecter une bannière au-dessus du contenu existant
document.body.insertBefore(banner, document.body.firstChild)
// ✅ Réserver de l'espace dès le départ
// <div class="banner-placeholder" style="min-height: 60px"></div>Ce que ça mesure : la réactivité aux interactions utilisateur (clics, touches clavier, tapotements). INP remplace FID depuis mars 2024.
Objectif : ≤ 200ms
< 200ms → Bon (vert)
200-500ms → À améliorer (orange)
> 500ms → Mauvais (rouge)Un mauvais INP signifie que votre site "répond" lentement aux actions utilisateur — bouton qui ne réagit pas immédiatement, champ de saisie avec lag.
Causes fréquentes :
click ou keydown avec des calculs lourdsSolutions :
// ❌ Calcul lourd sur le thread principal = UI gelée
button.addEventListener('click', () => {
const result = heavyComputation(largeDataset) // Bloque l'UI pendant 500ms
displayResult(result)
})
// ✅ Découper avec setTimeout ou scheduler
button.addEventListener('click', () => {
setTimeout(() => {
const result = heavyComputation(largeDataset)
displayResult(result)
}, 0) // Laisse l'UI se mettre à jour d'abord
})
// ✅ Web Worker pour les vrais calculs lourds
const worker = new Worker('/worker.js')
button.addEventListener('click', () => {
worker.postMessage(largeDataset)
})
worker.onmessage = (e) => displayResult(e.data)// ✅ Code splitting Next.js — ne charger que ce qui est nécessaire
import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => <Skeleton />,
})Search Console → Expérience → Core Web Vitals — la source la plus fiable. Données issues de vraies visites Chrome, sur 28 jours glissants.
Le rapport distingue :
https://pagespeed.web.dev/ — analyse une URL spécifique et combine données de lab (Lighthouse) et données terrain (CrUX).
# Via CLI
npm install -g lighthouse
lighthouse https://iducation.fr --output html --output-path ./report.html
# Ou dans Chrome DevTools → Lighthouse → Analyser la pageimport { onLCP, onCLS, onINP } from 'web-vitals'
function sendToAnalytics(metric) {
console.log(metric.name, metric.value, metric.rating)
// Envoyer à votre outil de monitoring (Datadog, custom endpoint...)
fetch('/api/vitals', {
method: 'POST',
body: JSON.stringify(metric),
})
}
onLCP(sendToAnalytics)
onCLS(sendToAnalytics)
onINP(sendToAnalytics)LCP
fetchpriority="high" sur l'image heroloading="lazy" sur l'image au-dessus du fold<link rel="preload" as="image"> pour l'image LCPfont-display: swap pour les polices customCLS
width et height sur toutes les images et vidéosaspect-ratio en CSS pour les conteneurs d'iframesINP
À faire : Testez votre site sur
pagespeed.web.dev. Notez les scores LCP, CLS, INP pour mobile et desktop. Identifiez le premier problème de chaque métrique (Search Console ou PageSpeed vous dit quelle ressource est responsable) et appliquez la solution correspondante.