Aller au contenu
Neaptidestudio
blog

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
Checklist SEO pour Next.js App Router — 1 SSR HTML (<h1> visible) · 2 Alternates (6 hreflang + RSS) · 3 Liens HTML (<a hreflang>) · 4 Schema (JSON-LD + sitemap).

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.

Cinq contrôles SEO Next.js App Router issus de l’audit de production Neaptide
Couche contrôléeDéfaut dans le HTML SSR initialCorrectif déployé et résultat vérifié
Composants d’animation Reveal.tsx et Hero.tsxLe <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.tsL’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.tsxLes options de langue n’existaient qu’après clic sous forme de <button> sans attribut hrefRendu des 6 langues dans le DOM SSR via des liens <a href={...} hrefLang={l} lang={l}>
Redirection racine (/) dans middleware.tsLes robots visitant / sans en-tête géo ni Accept-Language ne recevaient pas d’en-tête Vary completRedirection 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.tsTrois pages de service forçaient areaServed: United States ; le sitemap omettait les images des études de casHarmonisation 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 :

  1. 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".
  2. 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>.
  3. Liens HTML explorables : vérifiez que la navigation et le sélecteur de langue utilisent des balises <a href="..."> dans le HTML initial.
  4. 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.
  5. Cohérence JSON-LD : vérifiez que WebPage, Service, Article, BreadcrumbList et FAQPage correspondent au texte visible et à la langue de la page.
  6. 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.

faq

L'essentiel en bref

Les Server Components de Next.js suffisent-ils à garantir un bon SEO technique ?

Non. Les wrappers d’animation clients, la surcharge partielle de generateMetadata(), les menus en <button> et les redirections middleware peuvent toujours masquer le contenu ou casser les balises hreflang.

Pourquoi style="opacity:0" dans le HTML SSR pose-t-il problème si JavaScript affiche ensuite le texte ?

L’opacité nulle retarde le Largest Contentful Paint (LCP) jusqu’à la fin de l’hydratation et signale le contenu comme invisible aux robots qui analysent le HTML brut.

Pourquoi le lien RSS du layout disparaît-il sur les pages enfants ?

La fusion de generateMetadata() est superficielle : définir alternates sur une page remplace tout l’objet alternates du layout parent si alternates.types n’est pas inclus dans le helper.

Faut-il des liens <a hreflang> dans le header si hreflang est déjà dans <head> ?

Oui. Des balises <a href="..." hrefLang="..."> présentes dans le DOM offrent aux robots un maillage interne direct entre toutes les versions linguistiques.

Comment le middleware doit-il gérer la racine / pour un robot sans en-tête de langue ?

Il doit rediriger de façon déterministe vers la langue x-default (par exemple /en) et envoyer un en-tête Vary incluant Accept-Language, Cookie et les en-têtes géographiques.

Que comprend l’audit SEO Next.js proposé par Neaptide ?

Nous contrôlons le HTML SSR, le LCP, les balises canonical et hreflang, les schémas JSON-LD, le sitemap et l’accès des robots IA, avec une liste de correctifs directement exploitables par vos développeurs.