SEO JavaScript : ce qui est servi, ce qui est rendu, ce qui est indexé
Un moteur peut exécuter du JavaScript, mais le rendu est différé, coûteux et non garanti. La règle qui évite la quasi-totalité des problèmes est de servir dans le HTML initial tout ce qui doit être indexé, puis d'ajouter l'interactivité par-dessus. Le diagnostic consiste à comparer le HTML brut au DOM final : l'écart constitue exactement le périmètre du travail.
Les trois états d’une page
Il faut distinguer rigoureusement :
- le HTML servi — ce que renvoie le serveur, avant toute exécution ;
- le DOM rendu — l’état après exécution du JavaScript ;
- ce qui est indexé — un sous-ensemble du précédent, après un rendu différé et non garanti.
La plupart des diagnostics erronés viennent d’une confusion entre les deux premiers. L’inspecteur du navigateur affiche le DOM rendu : tout y paraît correct. Le robot, lui, commence par le HTML servi.
Le diagnostic en une commande
curl -sL -A "Mozilla/5.0 (compatible; diagnostic)" https://exemple.fr/page/ \
| grep -c "une phrase du contenu principal"
Si le résultat est 0, le contenu n’est pas dans le HTML servi. Même principe pour les éléments décisifs :
curl -sL https://exemple.fr/page/ | grep -E "<title>|canonical|<h1"
L’extracteur de balises publié ici fait la même chose dans le navigateur, et affiche en plus la latence et la taille de la réponse.
Choisir le mode de rendu par route
Le choix ne se fait pas pour le site entier, mais route par route.
| Type de route | Mode recommandé | Raison |
|---|---|---|
| Pages de contenu stables | Statique, généré au build | Le plus rapide, le moins coûteux, indexable par construction |
| Listes avec contenu fréquemment modifié | Statique avec revalidation | Fraîcheur maîtrisée sans coût par requête |
| Pages dépendant de la requête (recherche, filtres) | Rendu serveur | Le contenu ne peut pas être prégénéré |
| Catalogue très volumineux | Rendu serveur avec cache | Génération au build impraticable au-delà d’un certain volume |
| Espace authentifié, tableau de bord | Rendu client | Ne doit pas être indexé de toute façon |
Pour une route non indexable, le rendu client est le bon choix : il n’y a aucune raison de payer un rendu serveur pour une page qui porte un noindex.
Les pièges de routage
Un lien qui n’est pas une balise ancre
<!-- Non explorable : aucun href à suivre -->
<div onclick="navigate('/produits/')">Produits</div>
<span class="lien" data-href="/produits/">Produits</span>
<!-- Explorable -->
<a href="/produits/">Produits</a>
Le robot suit les attributs href des balises <a>. Une navigation déclenchée par un gestionnaire d’événement sur un div est invisible pour lui, et inaccessible au clavier par la même occasion.
Des métadonnées écrites après l’exécution
Un title ou une canonical ajoutés côté client peuvent ne pas être pris en compte de façon fiable. Ces éléments doivent figurer dans la réponse serveur.
Du contenu derrière une interaction
Un contenu qui n’est chargé qu’après un clic sur un onglet, un défilement ou l’ouverture d’un accordéon n’est pas vu si le chargement est déclenché par l’interaction. En revanche, un contenu présent dans le HTML et simplement masqué par CSS est bien lu : ce qui compte est la présence dans le document servi, pas la visibilité.
Un défilement infini sans URL paginées
Si la seule façon d’atteindre le contenu de la page 3 est de défiler, ce contenu est inatteignable pour un robot. La solution est de doubler le défilement infini par des URL paginées réelles et explorables.
Verrouiller par des contrôles
Un correctif de rendu non testé se perd. Il suffit qu’un composant interactif soit remonté d’un niveau dans l’arbre pour que le HTML servi se vide à nouveau, sans que personne ne le remarque.
Les contrôles à exécuter sur le HTML produit, à chaque build :
Pour chaque route indexable :
[x] la réponse contient un <title> non vide
[x] la réponse contient exactement un <h1>
[x] la réponse contient une <link rel="canonical">
[x] la réponse contient au moins N caractères de texte hors navigation
[x] la réponse contient au moins un <a href> interne
Ces contrôles tournent sur ce site avant chaque publication, et un échec bloque la mise en production. C’est ce qui garantit l’affirmation faite plus haut : le contenu principal de chaque page de ce site est présent dans le HTML servi et reste lisible avec JavaScript désactivé.
Le coût de l’hydratation
Une fois l’indexation réglée, le problème restant est l’interactivité. Un bundle important retarde le moment où la page répond réellement aux actions de l’utilisateur, ce qui se lit dans les données terrain quand elles existent.
Trois leviers, par ordre d’efficacité :
- Ne pas hydrater ce qui n’a pas besoin de l’être. Un bloc de texte statique ne nécessite aucun JavaScript.
- Isoler l’interactivité le plus bas possible dans l’arbre. Une frontière client placée trop haut entraîne tout son sous-arbre.
- Découper le JavaScript par route, plutôt que de servir un bundle unique à tout le site.
L’ordre est important : la troisième mesure est souvent tentée en premier alors que la première donne davantage, pour un effort moindre.
Limites de cette page
- Le rendu serveur ajoute une infrastructure et un coût d'exécution qui croît avec le trafic.
- Rendre une page indexable ne la rend pas pertinente : le contenu reste déterminant.
- Les délais de prise en compte dépendent de la fréquence d'exploration de chaque URL et ne sont pas contrôlables.
Questions fréquentes
Google exécute-t-il vraiment le JavaScript ?
Oui, mais le rendu est mis en file d'attente et peut être différé. Un script lent, bloqué ou en échec n'est pas toujours rattrapé, et les ressources consacrées au rendu ne sont pas illimitées. C'est pourquoi dépendre du rendu client pour du contenu indexable est un pari, pas une architecture.
Le rendu dynamique est-il une bonne solution ?
C'est un contournement. Il demande une infrastructure dédiée à maintenir et crée une divergence entre ce que reçoivent les robots et les utilisateurs, donc un risque de dérive silencieuse. Le rendu serveur ou le prérendu des routes indexables est préférable quand il est atteignable.
Comment vérifier ce qu'un robot reçoit ?
En récupérant le HTML brut de la réponse, sans exécuter le JavaScript, et en le comparant au DOM final. L'extracteur de balises publié sur ce site lit le HTML servi sans l'exécuter, ce qui rend l'écart immédiatement visible.
Sources
Sources primaires consultées
Diagnostic
Diagnostiquer une application
Une réponse argumentée sous deux jours ouvrés, avec un premier avis sur le maillon qui bloque. Pas de prise de rendez-vous imposée avant d'avoir compris le contexte.