Checklist SEO Next.js App Router : rendu SSR, canonicals, hreflang et crawlers IA
Neaptide · 30 octobre 2026 · 8 min de lecture
Vérifiez ce que Next.js App Router sert réellement dans le HTML brut : titres SSR visibles sans opacity:0, héritage alternates, liens de langue et redirections.
Dans cet article

Un site Next.js App Router peut sembler irréprochable dans un navigateur tout en masquant son titre <h1> derrière un style initial opacity:0, en écrasant les balises <link rel="alternate"> définies dans le layout parent ou en enfermant son sélecteur de langue dans un état client non monté. Inspectez toujours la réponse HTTP brute et le HTML rendu par le serveur avant de valider une mise en production.
Même avec les Server Components, les composants d’animation clients, les fonctions generateMetadata() propres à chaque page et le fichier middleware.ts modifient directement ce que reçoivent Googlebot et les robots d’IA (GPTBot, OAI-SearchBot, ClaudeBot, PerplexityBot). Voici cinq défauts techniques corrigés sur le site multilingue Next.js 15 de Neaptide, ainsi qu’une procédure de contrôle reproductible.
Qu’a révélé l’audit technique de notre propre site Next.js 15 ?
En octobre 2026, nous avons contrôlé le HTML servi, les en-têtes HTTP, les métadonnées et le sitemap des six versions linguistiques de Neaptide. Le tableau ci-dessous résume les cinq anomalies identifiées et les correctifs vérifiés en production.
| Couche contrôlée | Défaut dans le HTML SSR initial | Correctif déployé et résultat vérifié |
|---|---|---|
| Composants d’animation Reveal.tsx et Hero.tsx | Le <h1>, le chapô et les cartes étaient servis avec style="opacity:0;transform:translateY(12px)" jusqu’à l’hydratation (~1,2 s) | Suppression de opacity:0 dans l’état SSR initial ; titres et textes immédiatement visibles dans le HTML brut |
| Fonction generateMetadata() dans lib/meta.ts | L’objet alternates: { canonical, languages } des pages écrasait celui du layout et supprimait <link rel="alternate" type="application/rss+xml"> | Ajout de types: { "application/rss+xml": "https://www.neaptide.ai/feed.xml" } dans le helper partagé pageMetadata() |
| Sélecteur de langue components/LangSwitcher.tsx | Les options de langue n’existaient qu’après clic sous forme de <button> sans attribut href | Rendu des 6 langues dans le DOM SSR via des liens <a href={...} hrefLang={l} lang={l}> |
| Redirection racine (/) dans middleware.ts | Les robots visitant / sans en-tête géo ni Accept-Language ne recevaient pas d’en-tête Vary complet | Redirection déterministe vers x-default (/en) avec Vary: Accept-Language, Cookie, x-vercel-ip-country, cf-ipcountry |
| Données structurées lib/schema.ts et app/sitemap.ts | Trois pages de service forçaient areaServed: United States ; le sitemap omettait les images des études de cas | Harmonisation de areaServed et ajout des couvertures et graphiques localisés dans sitemap.xml |
Ces observations portent sur le code et la configuration de notre propre site. Leur correction lève des freins techniques au rendu et à l’exploration ; le positionnement organique et les citations dans les réponses IA dépendent toujours de la concurrence et de la pertinence globale du site.
Pourquoi style="opacity:0" dans les animations pénalise-t-il le LCP et les robots ?
De nombreux sites Next.js enveloppent leurs titres principaux et leurs sections dans des composants d’apparition ou Framer Motion initialisés à opacity: 0. Comme Next.js pré-rend aussi les Client Components sur le serveur dans leur état initial, le HTML envoyé contient style="opacity:0" sur le <h1> et le texte principal.
Cela engendre deux problèmes directs : le navigateur retarde la mesure du Largest Contentful Paint (LCP) jusqu’au téléchargement et à l’hydratation du JavaScript client, et les robots d’indexation ou d’IA qui lisent le HTML brut reçoivent un contenu principal marqué comme invisible.
Conservez une opacité complète dès le HTML serveur : utilisez uniquement une légère translation (transform: translateY(12px)) sans opacity: 0 dans le rendu SSR.
Comment generateMetadata peut supprimer les balises alternates du layout ?
Dans Next.js App Router, la fusion des métadonnées entre un layout et une page est superficielle. Si une page retourne son propre objet alternates contenant canonical et languages, il remplace intégralement l’objet alternates défini dans layout.tsx.
Regroupez canonical, languages (avec x-default) et types (comme le flux RSS) dans une fonction partagée appelée par toutes les pages indexables :
export function pageMetadata(locale: Locale, opts: PageMetaInput): Metadata {
const canonicalUrl = `${SITE.url}${localeHref(locale, opts.path)}`;
const languages: Record<string, string> = {};
for (const l of LOCALES) {
languages[l] = `${SITE.url}${localeHref(l, opts.path)}`;
}
languages["x-default"] = `${SITE.url}${localeHref(X_DEFAULT_LOCALE, opts.path)}`;
return {
alternates: {
canonical: canonicalUrl,
languages,
types: {
"application/rss+xml": `${SITE.url}/feed.xml`,
},
},
};
}Pourquoi rendre explicites le sélecteur de langue et la redirection racine ?
Un menu de langue qui ne monte ses éléments qu’au clic ou utilise des boutons <button> prive les robots de liens HTML internes entre vos versions linguistiques. Rendre de vrais liens <a href="/en/..." hrefLang="en" lang="en"> dans le DOM du header facilite la découverte de toutes les langues.
Vérifiez également la route racine / lorsqu’un robot l’interroge sans cookie ni en-tête Accept-Language : le middleware doit rediriger vers la locale x-default (/en) avec l’en-tête Vary approprié.
Comment valider une mise en ligne Next.js App Router en six étapes
Exécutez ces six contrôles avant de valider votre déploiement :
- Visibilité SSR brute : récupérez la page avec curl -s et vérifiez que le <h1> et le texte principal apparaissent sans style="opacity:0".
- Parité canonical et hreflang : contrôlez la présence du canonical auto-référent, des 6 langues, de x-default et du flux RSS dans <head>.
- Liens HTML explorables : vérifiez que la navigation et le sélecteur de langue utilisent des balises <a href="..."> dans le HTML initial.
- Redirection racine : testez / avec curl -I et un en-tête Accept-Language vide pour confirmer une redirection unique vers la langue par défaut.
- Cohérence JSON-LD : vérifiez que WebPage, Service, Article, BreadcrumbList et FAQPage correspondent au texte visible et à la langue de la page.
- Couverture sitemap.xml et llms.txt : contrôlez que toutes les pages et images clés figurent dans /sitemap.xml et /llms.txt.
curl -s https://www.neaptide.ai/en | grep -E '<h1|rel="canonical"|hrefLang|application/rss\+xml'
curl -I -H 'Accept-Language:' https://www.neaptide.ai/Pour un diagnostic complet de votre site Next.js, Neaptide réalise des audits SEO et GEO techniques avec vérifications HTTP reproductibles et tickets prêts pour vos développeurs.