Article publié le 15 mai 2025, mis à jour le 18 juillet 2026 (intégration du guide médias et images).
Qu'est-ce que les Core Web Vitals et pourquoi comptent-ils ?
Les Core Web Vitals sont trois métriques définies par Google pour mesurer la qualité de l'expérience utilisateur d'une page web. Elles sont un facteur de classement dans les résultats de recherche depuis juin 2021 (voir la documentation officielle Google Search Central).
LCP
Largest Contentful Paint
Temps d'affichage du plus grand élément visible à l'écran — presque toujours l'image ou le titre principal. Objectif : moins de 2,5 secondes.
CLS
Cumulative Layout Shift
Stabilité visuelle de la page : les décalages inattendus de mise en page. Objectif : score inférieur à 0,1.
INP
Interaction to Next Paint
Délai entre une interaction (clic, touche) et la mise à jour visuelle. Remplace le FID depuis mars 2024. Objectif : moins de 200 millisecondes.
À contenu équivalent, un site avec de meilleurs Core Web Vitals est favorisé dans les résultats de recherche. Au-delà du SEO, ces métriques mesurent ce que ressent réellement le visiteur : le temps d'attente, la stabilité de l'affichage et la réactivité de l'interface — trois facteurs qui pèsent directement sur le taux de rebond et la conversion.
Pourquoi le headless part-il avec un avantage structurel ?
Parce que la séparation backend/frontend permet d'optimiser chaque couche indépendamment : WordPress ne fait plus que gérer le contenu, et le frontend est construit pour la vitesse. Si le principe de cette séparation est nouveau pour vous, commencez par comprendre le WordPress headless.
Cet avantage est structurel, pas automatique : chaque ligne du tableau correspond à un réglage à mettre en place, détaillé dans les sections qui suivent. Le rôle du framework dans cette équation (rendu, routes, composants) est expliqué dans Next.js pour WordPress headless.
Un HTML complet servi en moins de 50 ms profite aussi aux robots des moteurs de réponse IA, qui lisent mal les pages lentes ou dépendantes du JavaScript — cet angle visibilité est traité dans la rubrique Être trouvé à l'heure de l'IA.
Comment mesurer les Core Web Vitals de votre site ?
Avant d'optimiser, mesurez. Deux familles d'outils, aux verdicts parfois différents :
Données de laboratoire (environnement contrôlé, utile pour diagnostiquer) :
- Lighthouse (intégré à Chrome DevTools) : audit de performance avec scores détaillés
- PageSpeed Insights : analyse par URL, combine laboratoire et terrain
- WebPageTest : tests détaillés avec filmstrip et waterfall
Données terrain (utilisateurs réels, celles qui comptent pour le classement) :
- Chrome User Experience Report (CrUX) : données réelles des utilisateurs Chrome, agrégées sur 28 jours
- Rapport « Signaux Web essentiels » de la Search Console : suivi page par page
- web-vitals (librairie JavaScript) : collecte des métriques dans votre propre outil d'analytics
Pour intégrer la collecte dans un projet Next.js déployé sur Vercel :
// app/layout.tsx — Collecte des Web Vitals
import { Analytics } from '@vercel/analytics/react';
import { SpeedInsights } from '@vercel/speed-insights/next';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="fr">
<body>
{children}
<Analytics />
<SpeedInsights />
</body>
</html>
);
}
Ce que ces mesures donnent en production réelle sur des projets headless, nous le documentons dans WordPress headless en 2026 : ce qui a changé.
Comment optimiser le LCP ? Commencez par les images
Le LCP mesure le temps d'affichage du plus grand élément visible — et sur la grande majorité des pages, cet élément est l'image principale (hero, visuel d'article, photo produit). Optimiser les images est donc le levier n° 1 du LCP : c'est pourquoi cette section détaille toute la chaîne, de la médiathèque WordPress à l'écran du visiteur.
D'où viennent les images : la médiathèque WordPress reste le point d'entrée
En headless, rien ne change pour vos éditeurs : les images sont uploadées dans la médiathèque WordPress, qui génère automatiquement plusieurs tailles et stocke les métadonnées (dimensions, poids, texte alternatif). Ces données sont exposées via l'API REST (/wp-json/wp/v2/media) ou WPGraphQL, et c'est le frontend Next.js qui se charge de l'optimisation d'affichage.
La réponse de l'API contient tout ce dont le frontend a besoin — notamment les dimensions, indispensables pour réserver l'espace et éviter le CLS :
{
"id": 156,
"alt_text": "Page d'accueil du site portfolio",
"source_url": "https://votre-site.com/wp-content/uploads/2024/11/capture-projet.jpg",
"media_details": {
"width": 2400,
"height": 1600,
"sizes": {
"medium": { "width": 300, "height": 200, "source_url": "…-300x200.jpg" },
"large": { "width": 1024, "height": 683, "source_url": "…-1024x683.jpg" },
"full": { "width": 2400, "height": 1600, "source_url": "…/capture-projet.jpg" }
}
}
}
Les tailles générées par WordPress (thumbnail, medium, large…) perdent l'essentiel de leur utilité en headless : next/image redimensionne lui-même à la demande depuis l'original. Inutile donc de multiplier les tailles personnalisées côté functions.php pour l'affichage.
Que fait next/image automatiquement ?
Le composant next/image applique quatre optimisations sans configuration :
- Conversion de format : JPEG/PNG servis en AVIF (ou WebP en repli) si le navigateur le supporte — un gain de poids de 30 à 50 % par rapport au JPEG.
- Redimensionnement à la demande : un
srcsetde plusieurs largeurs est généré ; le navigateur charge la taille adaptée à l'écran, jamais l'original de 2 400 px sur un mobile. - Lazy loading natif : les images hors écran ne sont chargées qu'à l'approche du viewport.
- Réservation d'espace : les dimensions déclarées évitent tout décalage de mise en page.
-50%
de poids d'image
Conversion automatique JPEG vers AVIF par next/image
< 2,5 s
de LCP atteignable
Image principale préchargée, dimensionnée et servie au bon format
0 ko
de JavaScript client
next/image génère du HTML natif (img + srcset), sans runtime JS
Une configuration est toutefois obligatoire pour les images distantes : déclarer le domaine WordPress dans next.config.js.
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'votre-site.com',
pathname: '/wp-content/uploads/**',
},
],
formats: ['image/avif', 'image/webp'],
},
};
module.exports = nextConfig;
Configuration obligatoire pour les images distantes
Sans remotePatterns, le composant next/image refuse de charger les images provenant d'un domaine externe. C'est une mesure de sécurité : le serveur d'optimisation intégré à Next.js ne traite que les sources explicitement autorisées.
priority, sizes, lazy loading : les règles d'usage
Image above the fold : priority
L'image visible sans scroll (hero, visuel principal) reçoit la propriété priority : elle est préchargée via un <link rel="preload"> dans le <head> et échappe au lazy loading. C'est le réglage qui pèse le plus sur le LCP. Une seule image priority par page, en général.
Images below the fold : lazy loading automatique
Par défaut, next/image applique le lazy loading natif : les images ne sont chargées qu'à l'approche du viewport. Aucune configuration nécessaire — ne désactivez surtout pas ce comportement.
Attribut sizes pour le bon srcset
sizes indique au navigateur quelle largeur l'image occupera selon le viewport, pour qu'il choisisse la bonne variante du srcset sans attendre le rendu CSS. Sans lui, le navigateur peut charger la taille maximale. Exemple : sizes="(max-width: 768px) 100vw, 50vw".
Placeholder flou pour le chargement progressif
placeholder="blur" affiche un aperçu flou pendant le chargement. Pour les images WordPress (distantes), générez le blurDataURL au build, par exemple avec la librairie plaiceholder.
En pratique, on encapsule ces règles dans un composant réutilisable branché sur les données de l'API :
import Image from 'next/image';
interface WPImage {
sourceUrl: string;
altText: string;
mediaDetails: { width: number; height: number };
}
export function WordPressImage({
image,
priority = false,
sizes = '(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw',
}: {
image: WPImage;
priority?: boolean;
sizes?: string;
}) {
return (
<Image
src={image.sourceUrl}
alt={image.altText || ''}
width={image.mediaDetails.width}
height={image.mediaDetails.height}
priority={priority}
sizes={sizes}
/>
);
}
Où servir les médias : direct, proxy Next.js ou CDN dédié ?
| Stratégie | Fonctionnement | Avantage |
|---|---|---|
| Direct | Le frontend pointe vers votre-site.com/wp-content/uploads/ | Simple, aucune configuration |
| Proxy Next.js | Les images transitent par le serveur Next.js (/_next/image) | Optimisation automatique, cache intégré |
| CDN dédié | Les médias sont répliqués sur un CDN (Cloudflare, CloudFront) | Latence minimale, décharge le serveur WordPress |
Verdict : proxy Next.js + CDN
La configuration optimale combine le proxy d'images de Next.js (optimisation et redimensionnement) avec un CDN en amont (cache des versions optimisées au plus près des visiteurs). Le CDN de Vercel gère cette combinaison nativement — voir déployer sur Vercel.
Les polices avec next/font
Après les images, les polices sont le deuxième frein courant du LCP : si le texte principal attend la police pour s'afficher, tout attend. next/font télécharge la police au build et la sert depuis le même domaine que le site (auto-hébergement) : aucune requête vers les serveurs Google Fonts, un aller-retour réseau en moins.
// app/layout.tsx
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap', // Affiche le texte immédiatement avec une police de repli
});
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="fr" className={inter.className}>
<body>{children}</body>
</html>
);
}
Réduire le temps de réponse serveur
Utiliser ISR au lieu de SSR
Le SSR génère le HTML à chaque requête, temps de requête vers l'API WordPress inclus. L'ISR sert une page pré-générée depuis le CDN, avec un TTFB inférieur à 50 ms.
Déployer sur un CDN global
Vercel, Netlify ou Cloudflare Pages déploient les pages statiques sur un réseau mondial : le visiteur reçoit la page depuis le serveur le plus proche. Le détail (hébergement du backend compris) est dans déploiement Vercel + Next.js.
Alléger les réponses de l'API WordPress
Limitez les champs retournés pour réduire la taille des réponses et le temps de traitement :
const res = await fetch(
`${API_URL}/posts?_fields=id,slug,title,excerpt,date&per_page=10`
);
Comment éviter le CLS (décalages de mise en page) ?
Le CLS mesure les décalages inattendus de mise en page. Un score supérieur à 0,1 signifie que des éléments bougent après le chargement initial — le bouton qui se dérobe sous le doigt. Trois causes couvrent la quasi-totalité des cas :
Images sans dimensions explicites : le navigateur réserve 0 px puis recalcule à l'arrivée de l'image.
// Problème : pas de dimensions → décalage au chargement
<img src="/image.jpg" alt="Photo" />
// Solution : next/image impose les dimensions et réserve l'espace
// (width/height viennent directement de media_details de l'API WordPress)
<Image src="/image.jpg" alt="Photo" width={800} height={450} />
Polices web : la police de repli n'a pas la même hauteur de ligne que la police définitive, d'où un décalage à l'affichage. next/font gère automatiquement font-display: swap et l'ajustement des métriques.
Contenu injecté dynamiquement : publicités, embeds et composants chargés côté client décalent le contenu existant.
// Solution : réserver l'espace avec une hauteur minimale
<div style={{ minHeight: '250px' }}>
<DynamicComponent />
</div>
Comment garder l'INP sous 200 ms ?
L'INP mesure le délai entre une interaction (clic, touche, tap) et la mise à jour visuelle qui en résulte. En Next.js, le principal coût est l'hydratation : les pages SSG/ISR arrivent en HTML statique, puis React exécute du JavaScript pour les rendre interactives — et ce JavaScript peut bloquer les interactions.
// Server Components par défaut : zéro hydratation
// app/blog/[slug]/page.tsx
export default async function ArticlePage({ params }: { params: { slug: string } }) {
const post = await getPostBySlug(params.slug);
return (
<article>
<h1 dangerouslySetInnerHTML={{ __html: post.title.rendered }} />
<div dangerouslySetInnerHTML={{ __html: post.content.rendered }} />
{/* Seul ce composant est hydraté côté client */}
<LikeButton postId={post.id} />
</article>
);
}
Les Server Components de l'App Router n'envoient pas de JavaScript au navigateur ; seuls les composants marqués 'use client' sont hydratés. Limitez les Client Components aux éléments réellement interactifs.
Pour les composants lourds non visibles immédiatement, ajoutez le chargement dynamique :
import dynamic from 'next/dynamic';
const HeavyChart = dynamic(() => import('@/components/HeavyChart'), {
loading: () => <div style={{ height: '400px' }}>Chargement du graphique…</div>,
ssr: false, // Composant purement interactif : inutile de le générer côté serveur
});
Quelles stratégies de cache appliquer ?
Le cache est le levier de performance le plus efficace. Trois niveaux se superposent en architecture headless :
// next.config.js — En-têtes de cache pour les assets statiques
const nextConfig = {
async headers() {
return [
{
source: '/_next/static/:path*',
headers: [
{ key: 'Cache-Control', value: 'public, max-age=31536000, immutable' },
],
},
{
source: '/images/:path*',
headers: [
{ key: 'Cache-Control', value: 'public, max-age=86400, stale-while-revalidate=604800' },
],
},
];
},
};
Comment alléger les réponses de l'API WordPress ?
La taille et le temps de réponse de l'API WordPress impactent directement le temps de build (SSG) et de régénération (ISR).
Sélectionner les champs et paginer :
// Uniquement les champs nécessaires
const res = await fetch(`${API_URL}/posts?_fields=id,slug,title,excerpt,featured_media,date`);
// Limiter le nombre de résultats
const res = await fetch(`${API_URL}/posts?per_page=10&page=1`);
Éviter une requête par image avec _embed : en API REST, l'image mise en avant d'un article n'est référencée que par son ID. Le paramètre _embed inclut ses données complètes (URL, dimensions, alt) dans la même réponse :
// Sans _embed : posts[0].featured_media = 156 → 2e requête nécessaire
// Avec _embed : tout est inclus
const posts = await fetch('/wp-json/wp/v2/posts?_embed').then(r => r.json());
// posts[0]._embedded['wp:featuredmedia'][0].source_url
// posts[0]._embedded['wp:featuredmedia'][0].media_details.sizes
(En WPGraphQL, la question ne se pose pas : featuredImage se demande directement dans la requête.)
Côté WordPress : installez un cache objet (Redis Object Cache ou Memcached) pour accélérer les requêtes à la base de données. WP Super Cache et équivalents sont inutiles en headless : ils mettent en cache le HTML PHP, pas les réponses API.
Le bundle JavaScript est-il sous contrôle ?
Un bundle trop volumineux ralentit le chargement et l'hydratation. Analysez sa composition avec @next/bundle-analyzer :
npm install @next/bundle-analyzer
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer(nextConfig);
# Générer le rapport d'analyse
ANALYZE=true npm run build
Règles pratiques pour la taille du bundle
Pour un site WordPress headless standard, le bundle JavaScript par route ne devrait pas dépasser 100 Ko (compressé gzip). Au-delà, vérifiez que les librairies lourdes (éditeurs, graphiques, cartes) sont chargées dynamiquement et non incluses dans le bundle principal, et importez uniquement les fonctions nécessaires (import spécifique plutôt qu'import global) pour profiter du tree-shaking.
Verdict : qui a vraiment besoin de ce niveau d'optimisation ?
- Site riche en visuels (portfolio, média, catalogue, immobilier) : c'est le cas où le headless rapporte le plus — l'élément LCP est presque toujours une image, et la chaîne next/image (AVIF, srcset, preload) fait la différence de façon mesurable.
- Site à fort enjeu SEO avec des Core Web Vitals dans le rouge : si le rapport Search Console est dégradé et que l'empilement de plugins de cache n'y change rien, la refonte du frontend traite la cause plutôt que les symptômes. Notre offre WordPress headless couvre exactement ce cas.
- Audience internationale : le HTML pré-généré sur CDN mondial sert chaque visiteur depuis le serveur le plus proche — un avantage qu'un hébergement WordPress unique ne peut pas répliquer.
- Petit site vitrine déjà au vert : soyons honnêtes, un WordPress classique bien tenu (bon hébergeur, cache, images WebP, thème léger) passe aussi les Core Web Vitals. Dans ce cas, la performance seule ne justifie pas une migration — les autres critères de décision sont dans dois-je passer au headless ?
En cas de doute sur votre situation précise, une visio conseil permet de trancher sur pièces (vos données CrUX, votre stack, vos contraintes) plutôt que sur des généralités.
Checklist de performance
Checklist Core Web Vitals pour WordPress headless + Next.js
LCP (< 2,5 s) :
- Utiliser
next/imageavecprioritysur l'image principale (et uniquement elle) - Déclarer le domaine WordPress dans
remotePatternsavecformats: ['image/avif', 'image/webp'] - Renseigner
sizespour que le navigateur charge la bonne variante dusrcset - Utiliser
next/fontpour le chargement optimisé des polices - Déployer avec ISR sur un CDN global
- Limiter les champs de l'API WordPress avec
_fields, inclure les images avec_embed
CLS (< 0,1) :
- Définir
width/heightde toutes les images (depuismedia_detailsde l'API) - Utiliser
next/fontavecdisplay: swap - Réserver l'espace des composants dynamiques avec
minHeight
INP (< 200 ms) :
- Server Components par défaut, Client Components limités à l'interactif
- Charger dynamiquement les composants lourds avec
dynamic() - Maintenir le bundle JavaScript par route sous 100 Ko (gzip)
Cache :
- Configurer les en-têtes
Cache-Controldes assets statiques - ISR avec revalidation à la demande pour les pages de contenu
- Cache objet (Redis) sur le serveur WordPress
Sécurité d'un WordPress headless : admin, API, sessions
Article suivantSEO technique pour architecture headless
Continuer la lecture
Comprendre le WordPress headless : le guide 2026
Ce qu'est le WordPress headless, comment il fonctionne, ses bénéfices réels et ses limites — le guide de référence pour décider en connaissance de cause.
Dois-je passer au WordPress headless ? Guide de décision
Signaux qui justifient le headless, verdicts pour 6 profils, diagnostic interactif et phase pilote en 5 étapes pour décider sans risquer votre site.
L'API REST WordPress
Endpoints, requêtes et authentification de l'API REST native de WordPress pour une architecture headless.
Pour aller plus loin