SEO Next.js : bien choisir son mode de rendu

SEO Next.js : bien choisir son mode de rendu

Sur un projet Next.js, la décision qui pèse le plus lourd sur le référencement ne se prend pas dans le contenu de la page, mais avant : dans le choix du mode de rendu de chaque route. Next.js propose plusieurs façons de produire le HTML envoyé au navigateur — et au robot d'exploration — et ce choix détermine directement si Googlebot voit votre contenu au moment où il visite la page, ou s'il doit revenir plus tard, avec un budget d'exploration entamé et une patience limitée. Une balise title mal optimisée coûte quelques positions. Une route rendue uniquement côté client, sans rien dans le HTML initial, peut tout simplement ne jamais être comprise correctement.

Cet article suppose que vous connaissez déjà la différence générale entre rendu côté client et rendu côté serveur — ce terrain est couvert ailleurs. Ici, l'angle est spécifique à Next.js : quels modes le framework propose concrètement, comment la coexistence de l'App Router et du Pages Router change la donne, quelles métadonnées gérer route par route, et les erreurs qui reviennent le plus souvent sur ce framework précis plutôt que sur le rendu JavaScript en général.

Les modes de rendu de Next.js, en conséquences plutôt qu'en jargon

Next.js met à disposition plusieurs stratégies de rendu, et chacune a une conséquence SEO différente plutôt qu'une simple différence de vitesse.

La génération statique (SSG) produit le HTML complet au moment du build. Le fichier livré au navigateur, et au robot, contient déjà tout le texte, les liens internes, les balises meta. C'est la situation la plus confortable pour l'exploration : rien à attendre, rien à exécuter, le contenu est présent dès la première requête.

Le rendu côté serveur (SSR) produit ce même HTML complet, mais à chaque requête plutôt qu'une fois pour toutes au build. La conséquence SEO est identique à la génération statique, le contenu est présent dans la réponse initiale, mais le temps de réponse dépend de ce qui se passe côté serveur à cet instant précis : appel base de données, appel à une API externe, calcul. Un SSR lent n'empêche pas l'indexation, mais il ralentit l'exploration et pèse sur les métriques de performance perçues par le robot comme par le visiteur.

La régénération incrémentale (ISR) est un compromis entre les deux : la page est générée statiquement, puis régénérée en arrière-plan après un délai ou sur déclenchement, sans reconstruire l'ensemble du site. Pour le SEO, l'ISR se comporte comme du contenu statique, un HTML complet servi immédiatement, avec l'avantage de pouvoir refléter un contenu qui change (un prix, un stock, un nombre d'avis) sans republier tout le site à chaque modification.

Le rendu côté client (CSR) est la situation à traiter avec le plus de prudence. Dans ce mode, le HTML initial renvoyé au navigateur est un squelette quasiment vide ; le contenu réel n'apparaît qu'après que le JavaScript s'est chargé et exécuté dans le navigateur. La règle à retenir est simple : tout contenu qui n'existe qu'après l'exécution du JavaScript côté client est un contenu que vous demandez au robot d'aller chercher lui-même, avec un budget de calcul et de temps qui n'est ni illimité ni garanti sur chaque page d'un grand site. Pour une route dont le classement compte, ne vous reposez jamais sur cette exécution différée comme seule source de contenu.

  • Un contenu éditorial — article, page produit, page catégorie — devrait sortir du serveur déjà rempli : statique, SSR ou ISR selon la fréquence de mise à jour du contenu.
  • Un contenu strictement réservé à un utilisateur connecté, sans vocation à apparaître dans une recherche, peut rester en rendu client sans dommage pour le référencement.
  • Le doute se tranche avec une question simple : si je désactive JavaScript et que je recharge la page, le contenu que je veux voir ranker est-il là ?

App Router contre Pages Router : le point à vérifier avant de suivre un conseil

Next.js a connu un changement d'architecture majeur avec l'arrivée de l'App Router, le dossier app/, qui coexiste désormais avec l'historique Pages Router, le dossier pages/. C'est probablement la source numéro un de conseils SEO obsolètes ou mal appliqués sur ce framework : un article, un fil de discussion ou une réponse trouvée en ligne peut décrire un comportement propre à un seul des deux routeurs, sans le préciser explicitement.

La conséquence pratique est simple à énoncer : avant d'appliquer un conseil trouvé sur Next.js et le référencement, vérifiez d'abord de quel routeur il parle. Un guide écrit avant la stabilisation de l'App Router décrit presque toujours le Pages Router sans le nommer, tout simplement parce qu'à l'époque il n'existait pas d'alternative à nommer. Appliqué tel quel sur un projet App Router récent, ce conseil peut ne rien faire du tout, ou pointer vers une API qui n'existe plus sous la même forme.

  • La gestion des métadonnées diffère structurellement : l'App Router exporte un objet ou une fonction de métadonnées depuis le fichier de route, avec une fonction dédiée pour les métadonnées dynamiques, tandis que le Pages Router s'appuie historiquement sur un composant Head importé et inséré manuellement dans chaque page.
  • Le comportement de rendu par défaut n'est pas le même : dans l'App Router, les composants sont serveur par défaut, ce qui pousse naturellement vers du contenu déjà rendu ; dans le Pages Router, la stratégie de rendu se déclare explicitement page par page.
  • La génération du sitemap et des routes dynamiques suit des conventions de fichiers différentes entre les deux routeurs, ce qui rend un exemple de code copié tel quel rarement transposable d'un routeur à l'autre.

Les métadonnées, route par route

Le SEO on-page dans Next.js se joue en grande partie dans la façon dont chaque route déclare ses métadonnées : titre, description, balise canonique, balises Open Graph.

Pour une page fixe, une page à propos, une page de contact, ces métadonnées se déclarent une fois dans le fichier de la route et n'ont pas besoin d'être recalculées à chaque visite.

Pour une page templatée, une fiche produit, un article de blog, une page de catégorie générée à partir d'un identifiant dans l'URL, les métadonnées doivent être générées dynamiquement à partir des données de l'enregistrement affiché. Un titre générique répété sur des centaines de fiches produit revient à renoncer à tout signal de pertinence différenciant sur ces pages, alors que la donnée nécessaire pour un titre spécifique, le nom du produit par exemple, est déjà chargée pour afficher la page elle-même.

La balise canonique mérite une attention particulière sur les sites Next.js à forte pagination ou à facettes de filtrage, tri, couleur, taille dans un catalogue e-commerce. Sans canonique explicite, chaque combinaison de paramètres d'URL peut être traitée comme une page distincte, ce qui dilue le signal de pertinence entre des dizaines de variantes d'une même liste de produits. La canonique doit pointer vers l'URL de référence, généralement la version sans filtre, et cette logique doit être calculée à partir des paramètres réellement présents dans l'URL, jamais codée en dur.

Les balises Open Graph et Twitter, image, titre, description, suivent la même logique que les métadonnées classiques : statiques pour les pages fixes, dynamiques et tirées du contenu réel pour les pages templatées. Une image Open Graph par défaut, identique sur tout le site, fonctionne mais fait perdre une partie de l'effet sur les partages de contenu individuel ; un article ou une fiche produit gagne à avoir sa propre image ou, au minimum, son propre titre affiché lors d'un partage.

Les erreurs qui reviennent le plus souvent

Certaines erreurs reviennent avec une régularité suffisante sur les projets Next.js pour mériter d'être listées explicitement.

Le soft 404 est la plus fréquente. Quand un enregistrement n'existe pas, un produit supprimé, un identifiant mal formé, un slug qui ne correspond à rien en base, la route doit renvoyer un vrai statut 404, pas une page qui affiche « Produit introuvable » avec un statut HTTP 200. Dans l'App Router, cela passe par un fichier de page introuvable associé à un appel explicite dans le code de la route au moment où l'absence de données est constatée. Sans cet appel, la page rend un contenu d'erreur avec un statut de succès, et un robot qui s'appuie sur le code de statut la traite comme une page valide, ce qui peut la faire indexer, voire remonter dans les résultats pour des requêtes qu'elle ne peut satisfaire.

Le titre qui ne se met pas à jour lors d'une navigation côté client est une autre erreur classique, héritée de l'époque où les applications React étaient de simples applications monopage. Dans du Next.js correctement configuré, que ce soit via les conventions de métadonnées de l'App Router ou via un composant Head bien positionné dans le Pages Router, chaque route doit produire son propre titre, y compris lors d'une navigation qui ne recharge pas la page dans le navigateur.

Les problèmes d'hydratation méritent d'être pris au sérieux au-delà de la simple erreur affichée dans la console. Une hydratation qui échoue force React à réconcilier ou à réafficher une partie de l'arbre, ce qui peut faire disparaître ou décaler brièvement un contenu pourtant déjà visible dans le HTML initial, exactement le contenu que vous vouliez montrer au robot. Une hydratation propre n'est pas qu'un confort de développement, c'est une garantie que le contenu reste stable entre le HTML brut et l'état final de la page.

Les redirections implémentées côté client, un effet déclenché après le chargement qui renvoie l'utilisateur ailleurs, sont invisibles pour un robot qui ne lit que la réponse HTTP initiale. Une redirection destinée à corriger une URL, fusionner un doublon ou rediriger une ancienne structure de site doit être une vraie redirection HTTP, gérée au niveau du serveur ou du middleware Next.js, avec un code de statut explicite, jamais un effet de bord déclenché après le rendu.

Enfin, le sitemap qui devient obsolète sur un site en génération statique est une erreur silencieuse : le sitemap est lui-même généré au moment du build, et si le contenu change entre deux builds sans que le sitemap soit régénéré, il continue de lister des URL supprimées et omet les URL ajoutées depuis. Sur un site dont le contenu change plus vite que le rythme des déploiements, le sitemap doit être généré de façon à refléter l'état réel des données, pas seulement l'état du code au dernier build.

Images et Core Web Vitals

Next.js fournit un composant Image qui gère automatiquement le redimensionnement, le format moderne et le chargement différé, ce qui règle une bonne partie des problèmes de performance liés aux images sans configuration supplémentaire. Ce n'est pas pour autant une garantie automatique sur l'image la plus importante de la page, celle qui détermine le Largest Contentful Paint (LCP).

Le comportement par défaut du composant Image retarde le chargement des images situées hors du premier écran, ce qui est le comportement souhaité pour la majorité des images d'une page. Mais si cette optimisation s'applique par erreur à l'image principale, celle visible immédiatement, souvent l'image d'en-tête d'un article ou la photo principale d'une fiche produit, elle retarde exactement l'élément que le LCP mesure. Cette image doit être marquée comme prioritaire explicitement, pour que le navigateur commence à la charger dès le début du rendu de la page plutôt que d'attendre qu'elle entre dans le champ visible.

La taille des images reste aussi une responsabilité qui n'est pas prise en charge automatiquement : fournir les dimensions réellement attendues à l'affichage, dans un format compressé, évite qu'une image surdimensionnée soit envoyée puis réduite côté navigateur, ce qui gaspille de la bande passante et ralentit l'affichage sans aucun bénéfice visuel.

Le décalage de mise en page (CLS) mérite la même vigilance : une image sans dimensions déclarées pousse le contenu qui l'entoure au moment où elle finit de charger. Le composant Image de Next.js réserve l'espace nécessaire quand les dimensions sont fournies, ce qui évite ce décalage, encore faut-il les fournir plutôt que de laisser une image en taille automatique dans un conteneur dont la hauteur dépend du contenu.

Le routage internationalisé

Pour un site qui publie en plusieurs langues, le routage internationalisé est une dimension du SEO Next.js qui concerne directement les projets francophones, bien plus qu'un site qui ne publie qu'en anglais.

Next.js propose une gestion native des sous-chemins de langue dans le routage, mais cette gestion ne remplace pas les signaux SEO qui doivent accompagner chaque version linguistique : une balise hreflang pointant vers l'équivalent de la page dans chaque autre langue disponible, y compris une valeur par défaut pour les visiteurs dont la langue n'est pas ciblée, et une URL canonique propre à chaque langue plutôt qu'une canonique qui pointerait toutes les versions vers une seule.

Un piège fréquent sur les sites multilingues Next.js est la génération incomplète des hreflang sur les pages générées statiquement : si la liste des langues disponibles pour un contenu donné n'est pas recalculée à chaque build à partir des traductions réellement publiées, une page peut continuer à déclarer une langue dont la traduction a été retirée, ou omettre une langue récemment ajoutée. Le hreflang doit être dérivé de l'état réel du contenu, jamais d'une liste statique des langues supportées par le site en général.

La détection automatique de la langue du visiteur, par géolocalisation ou par l'en-tête de langue du navigateur, pose un risque spécifique si elle prend la forme d'une redirection automatique côté client déclenchée après le chargement de la page : un robot qui explore la version d'une URL dans une langue peut alors ne jamais atteindre la version dans une autre langue si la redirection l'en empêche. La pratique la plus sûre reste de laisser chaque URL localisée accessible directement, sans redirection forcée, et de proposer la détection comme une suggestion visible plutôt qu'une redirection automatique imposée au robot comme à l'utilisateur.

Vérifier ce qui est vraiment servi

Le meilleur moyen de savoir si une route Next.js expose réellement son contenu à un robot n'est pas de la regarder dans un navigateur avec JavaScript activé : c'est ainsi qu'un humain la voit, pas nécessairement ainsi qu'un robot d'indexation la reçoit lors de son premier passage. La vérification fiable consiste à récupérer le HTML brut renvoyé par le serveur, avant toute exécution de JavaScript, et à l'inspecter directement.

Une simple requête HTTP vers l'URL de la page, sans navigateur, suffit pour cela : la réponse contient exactement ce que le serveur a envoyé. Si le titre, le contenu principal, les liens internes et les balises meta attendues sont visibles dans cette réponse brute, la page est dans une situation confortable quel que soit le mode de rendu utilisé. S'ils sont absents et n'apparaissent qu'après exécution du JavaScript, la page dépend du rendu côté client pour être comprise, avec tout ce que cela implique pour la rapidité et la fiabilité de son exploration.

Cette vérification vaut la peine d'être répétée régulièrement plutôt que faite une seule fois au lancement du site : un changement de configuration, une migration partielle du Pages Router vers l'App Router, ou l'ajout d'un composant qui déplace du contenu vers un rendu client peuvent faire régresser une page qui fonctionnait correctement, sans que rien ne le signale dans l'interface visible du site. Le contenu continue de s'afficher normalement pour un visiteur, c'est justement ce qui rend la régression difficile à repérer sans revérifier le HTML brut périodiquement.

Questions fréquentes

Faut-il choisir l'App Router ou le Pages Router pour un nouveau projet, du point de vue du SEO ?

Les deux permettent de produire du HTML complet côté serveur, donc aucun des deux n'est disqualifié pour le référencement en soi. La différence pratique est ailleurs : l'App Router pousse par défaut vers des composants serveur et une gestion des métadonnées plus structurée, ce qui réduit le risque d'oublier une route en rendu client par accident. Sur un projet neuf, cela en fait le choix le plus sûr par défaut.

Le rendu côté client empêche-t-il complètement l'indexation par Google ?

Non, Googlebot peut exécuter du JavaScript et indexer du contenu apparu après cette exécution. Le problème n'est pas une impossibilité, mais un coût supplémentaire et une fiabilité moindre : l'exécution n'est pas instantanée, pas garantie sur chaque page d'un grand site, et une erreur silencieuse peut empêcher le contenu de s'afficher au moment où le robot vérifie la page.

Comment savoir rapidement si une page Next.js dépend du rendu côté client ?

Récupérez la réponse HTML brute de l'URL avec une requête HTTP simple, sans navigateur, et cherchez-y le contenu principal attendu. S'il est présent tel quel, la page est déjà rendue côté serveur ou statiquement. S'il manque et n'apparaît que dans un navigateur avec JavaScript activé, la page dépend du rendu côté client pour être comprise.

Le composant Image de Next.js suffit-il à lui seul pour un bon score de Largest Contentful Paint ?

Il aide, mais ne garantit rien automatiquement. Par défaut, il retarde le chargement des images hors du premier écran, un comportement à désactiver explicitement pour l'image la plus importante de la page, sous peine de retarder précisément l'élément mesuré par le LCP.

Un site en une seule langue a-t-il besoin de hreflang ?

Non. Le hreflang sert à indiquer l'équivalence entre plusieurs versions linguistiques ou régionales d'une même page. Sans version alternative réelle à déclarer, l'ajouter n'apporte rien et peut même introduire des erreurs si la balise est mal maintenue au fil des mises à jour du site.

Tous les articles