Les défis SEO d'une architecture headless
En architecture WordPress traditionnelle, des plugins comme Yoast SEO ou Rank Math génèrent automatiquement les balises meta, le sitemap et les données structurées dans le HTML rendu par le thème PHP. En mode headless, le thème WordPress n'est plus utilisé pour le rendu : ces éléments SEO doivent être implémentés côté frontend Next.js.
Yoast et Rank Math restent utiles côté back-end
En mode headless, Yoast SEO et Rank Math conservent leur utilité : ils exposent les métadonnées (titre SEO, description, image OG) via l'API REST ou GraphQL. Les rédacteurs continuent de renseigner ces champs dans l'éditeur WordPress. Le frontend Next.js les récupère via l'API et les injecte dans le HTML.
Métadonnées dynamiques avec generateMetadata
Next.js (App Router) fournit la fonction generateMetadata pour définir les balises meta de chaque page à partir des données récupérées depuis l'API WordPress.
// app/articles/[slug]/page.tsx
import { Metadata } from 'next';
type Props = { params: { slug: string } };
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const article = await fetch(
`${process.env.WORDPRESS_API_URL}/wp/v2/posts?slug=${params.slug}&_fields=title,excerpt,yoast_head_json`
).then(res => res.json()).then(data => data[0]);
const seo = article.yoast_head_json;
return {
title: seo?.title || article.title.rendered,
description: seo?.description || article.excerpt.rendered.replace(/<[^>]*>/g, ''),
openGraph: {
title: seo?.og_title || article.title.rendered,
description: seo?.og_description,
images: seo?.og_image ? [{ url: seo.og_image[0].url }] : [],
type: 'article',
},
twitter: {
card: 'summary_large_image',
title: seo?.twitter_title || seo?.og_title,
description: seo?.twitter_description || seo?.og_description,
},
alternates: {
canonical: seo?.canonical || `https://www.votre-site.fr/articles/${params.slug}`,
},
};
}
Sitemap XML dynamique
Next.js permet de générer un sitemap XML à partir du fichier app/sitemap.ts. Ce fichier interroge l'API WordPress pour lister toutes les pages indexables.
// app/sitemap.ts
import { MetadataRoute } from 'next';
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const baseUrl = 'https://www.votre-site.fr';
// Récupérer tous les articles depuis WordPress
const posts = await fetch(
`${process.env.WORDPRESS_API_URL}/wp/v2/posts?per_page=100&_fields=slug,modified`
).then(res => res.json());
// Récupérer toutes les pages
const pages = await fetch(
`${process.env.WORDPRESS_API_URL}/wp/v2/pages?per_page=100&_fields=slug,modified`
).then(res => res.json());
const postEntries = posts.map((post: any) => ({
url: `${baseUrl}/articles/${post.slug}`,
lastModified: new Date(post.modified),
changeFrequency: 'weekly' as const,
priority: 0.7,
}));
const pageEntries = pages.map((page: any) => ({
url: `${baseUrl}/${page.slug}`,
lastModified: new Date(page.modified),
changeFrequency: 'monthly' as const,
priority: 0.8,
}));
return [
{ url: baseUrl, lastModified: new Date(), changeFrequency: 'daily', priority: 1.0 },
...pageEntries,
...postEntries,
];
}
Configuration de robots.txt
// app/robots.ts
import { MetadataRoute } from 'next';
export default function robots(): MetadataRoute.Robots {
return {
rules: [
{
userAgent: '*',
allow: '/',
disallow: ['/api/', '/admin/'],
},
],
sitemap: 'https://www.votre-site.fr/sitemap.xml',
};
}
Bloquer l'indexation du back-end WordPress
Le serveur WordPress (par exemple admin.votre-site.fr) ne doit pas être indexé par les moteurs de recherche. Ajoutez un fichier robots.txt sur le domaine WordPress avec Disallow: / pour empêcher tout crawl. Seul le frontend Next.js doit être indexé.
Données structurées JSON-LD
Les données structurées (schema.org) permettent aux moteurs de recherche de comprendre le type de contenu d'une page. Elles sont implémentées sous forme de scripts JSON-LD dans les composants Next.js.
// components/ArticleJsonLd.tsx
type ArticleJsonLdProps = {
title: string;
description: string;
url: string;
imageUrl: string;
datePublished: string;
dateModified: string;
authorName: string;
};
export function ArticleJsonLd(props: ArticleJsonLdProps) {
const schema = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: props.title,
description: props.description,
url: props.url,
image: props.imageUrl,
datePublished: props.datePublished,
dateModified: props.dateModified,
author: { '@type': 'Person', name: props.authorName },
publisher: {
'@type': 'Organization',
name: 'Votre Site',
logo: { '@type': 'ImageObject', url: 'https://www.votre-site.fr/logo.png' },
},
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
);
}
URL canoniques et redirections
URL canoniques
Chaque page doit déclarer son URL canonique pour éviter les problèmes de contenu dupliqué. La propriété alternates.canonical dans generateMetadata assure cette déclaration.
Gestion des redirections et des 404
Les redirections (anciennes URL vers nouvelles) se configurent dans next.config.js :
// next.config.js — redirections
module.exports = {
async redirects() {
return [
{
source: '/ancien-article/:slug',
destination: '/articles/:slug',
permanent: true, // 301
},
];
},
};
Pour les pages introuvables, Next.js utilise le fichier app/not-found.tsx qui retourne automatiquement un code HTTP 404.
Configurer les métadonnées dynamiques
Implémentez generateMetadata dans chaque layout ou page dynamique. Récupérez les données SEO depuis l'API WordPress (champs Yoast ou Rank Math) et mappez-les sur l'objet Metadata de Next.js.
Générer le sitemap et le robots.txt
Créez les fichiers app/sitemap.ts et app/robots.ts. Le sitemap interroge l'API WordPress pour lister toutes les URL indexables avec leur date de dernière modification.
Implémenter les données structurées JSON-LD
Ajoutez des composants JSON-LD pour chaque type de contenu (Article, BreadcrumbList, Organization, FAQ). Utilisez les données récupérées depuis WordPress pour remplir les champs schema.org.
Configurer les URL canoniques et les redirections
Déclarez les URL canoniques via generateMetadata et configurez les redirections dans next.config.js pour préserver le référencement des anciennes URL.
Ajouter les balises hreflang (si multilingue)
Pour un site multilingue, ajoutez les balises hreflang dans generateMetadata via la propriété alternates.languages pour indiquer aux moteurs de recherche les versions linguistiques de chaque page.
Balises hreflang pour le multilingue
Si votre site est disponible en plusieurs langues, les balises hreflang indiquent aux moteurs de recherche quelle version linguistique servir selon la localisation de l'utilisateur.
// Dans generateMetadata
return {
alternates: {
canonical: `https://www.votre-site.fr/articles/${slug}`,
languages: {
'fr': `https://www.votre-site.fr/articles/${slug}`,
'en': `https://www.votre-site.fr/en/articles/${slug}`,
},
},
};
Open Graph et Twitter Cards
Les balises Open Graph contrôlent l'apparence de vos pages lorsqu'elles sont partagées sur les réseaux sociaux (Facebook, LinkedIn). Les Twitter Cards font de même pour X/Twitter. Les deux se configurent dans generateMetadata comme illustré dans la section sur les métadonnées dynamiques.
Checklist SEO technique headless
- Balises title et description uniques sur chaque page
- URL canonique déclarée sur chaque page
- Sitemap XML dynamique accessible à
/sitemap.xml - Fichier robots.txt configuré (frontend indexable, backend bloqué)
- Données structurées JSON-LD pour chaque type de contenu
- Balises Open Graph et Twitter Card renseignées
- Balises hreflang si le site est multilingue
- Redirections 301 pour les anciennes URL
- Page 404 personnalisée avec code HTTP 404
- Rendu SSR ou SSG pour que le HTML soit complet au crawl
Suivi avec Google Search Console
Après déploiement, soumettez votre sitemap dans Google Search Console et surveillez :
- Le nombre de pages indexées vs découvertes
- Les erreurs de crawl (404, 5xx, redirections en boucle)
- Les performances de recherche (impressions, clics, position moyenne)
- Les Core Web Vitals (LCP, FID, CLS) mesurés sur les données terrain
Performance et Core Web Vitals en headless
Article suivantHébergement et mise en ligne - La méthode
Continuer la lecture
Pour aller plus loin