SEO JavaScript & rendu
Comment Google rend le JavaScript — et comment choisir entre SSG, SSR, ISR ou rendu dynamique pour le SEO.
Les frameworks front-end modernes livrent l’essentiel de leur contenu sous forme de JavaScript exécuté dans le navigateur. C’est formidable pour l’ingénierie produit, et catastrophique pour le modèle mental naïf du SEO, où un robot télécharge du HTML et le lit. Google exécute bien le JavaScript — mais selon son propre calendrier, son propre budget, et avec des modes de défaillance qui rognent silencieusement votre classement. Si votre contenu le plus important n’existe qu’après la résolution d’un fetch(), vous misez votre trafic organique sur un rendu qui peut être différé de plusieurs jours, voire ne jamais aboutir comme vous l’attendez.
Ce guide explique ce qui se passe réellement à l’intérieur du pipeline de rendu de Google, compare les stratégies de rendu que vous pouvez choisir au moment du build, parcourt les pièges qui frappent les applications réelles, et vous donne un workflow de débogage que vous pouvez lancer dès aujourd’hui.
Comment Google effectue le rendu
Il est utile de penser l’indexation comme un processus en deux vagues, et non comme une passe unique.
- Crawl. Googlebot requête l’URL et reçoit le HTML initial — exactement ce que retournerait
curl https://example.com. Les liens découverts ici entrent dans la file d’attente de crawl. Si votre HTML initial est un<div id="root"></div>vide, cette vague ne voit presque rien. - Rendu. L’URL est placée dans la file du Web Rendering Service (WRS). Le WRS est un Chromium headless et evergreen. Quand de la capacité est disponible, il charge la page, exécute le JavaScript, attend la stabilisation du réseau, et produit le HTML rendu (le DOM post-JavaScript).
- Index. Google indexe le HTML rendu et en extrait tout nouveau lien découvert pour le cycle de crawl suivant.
L’idée essentielle, c’est l’écart entre la vague 1 et la vague 2. Le rendu est différé parce qu’exécuter du JavaScript coûte environ un ordre de grandeur plus cher que récupérer du HTML. En pratique le délai n’est souvent que de quelques minutes, mais il peut s’étirer sur plusieurs jours quand la demande de crawl est forte ou que votre site a une grande file de rendu.
CRAWL RENDER (WRS queue) INDEX
Googlebot ──► raw HTML ──► [ wait: minutes → days ] ──► headless
│ Chromium
▼ │
links found rendered DOM
in raw HTML │
└──────────────► next crawl cycle ◄── new links found
Ce report interagit avec deux budgets que vous ne contrôlez pas directement :
- Budget de crawl — combien d’URL Googlebot récupère depuis votre origine sur une fenêtre donnée. Déterminé par l’autorité du site, la vitesse du serveur et le taux d’erreurs.
- Budget de rendu — en gros, combien de CPU/temps le WRS est prêt à dépenser pour rendre vos pages. Les pages lentes et chargées en scripts en consomment davantage, donc moins de vos pages sont rendues par cycle.
⚠️ Attention : Le grand classique de l’échec, c’est le contenu qui n’existe que après injection côté client. La vague de crawl voit une coquille vide, et si la vague de rendu est différée ou que votre script plante sous le WRS, le contenu n’est jamais indexé. « Ça marche dans mon navigateur » n’est pas une preuve que ça marche pour Googlebot — votre navigateur n’est ni limité en débit, ni sandboxé, ni en train de rejouer des réponses mises en cache depuis des semaines.
Quelques spécificités du WRS qui surprennent les développeurs :
- Le WRS ne fait pas défiler la page, ne clique pas et ne bouge pas la souris. Tout ce qui est conditionné par une interaction utilisateur lui est invisible.
- Le WRS refuse automatiquement les demandes de permission (géolocalisation, caméra, notifications). Le code qui attend ces invites reste bloqué.
- Le WRS met agressivement en cache les ressources (JS, CSS, réponses d’API) selon son propre calendrier, qui peut être bien plus long que ce que suggèrent vos en-têtes de cache HTTP. Un « déploiement frais » peut être rendu avec des assets périmés.
- Le WRS utilise un Chromium récent, donc la syntaxe JS moderne passe sans problème — mais il n’y a pas de persistance
localStorage/sessionStoragesur laquelle compter d’un rendu à l’autre, et les cookies ne sont pas transportés comme pour un utilisateur connecté.
Stratégies de rendu
L’endroit où vous générez le HTML détermine la part de risque que vous portez. Voici les cinq stratégies entre lesquelles vous choisirez réellement.
| Stratégie | Quand le HTML est construit | Le robot voit-il le contenu immédiatement ? | Compatibilité SEO | Idéal pour |
|---|---|---|---|---|
| CSR (rendu côté client) | Dans le navigateur, après l’exécution du JS | Non — coquille vide d’abord | ⚠️ Risqué | Tableaux de bord connectés, UI applicatives derrière une authentification |
| SSR (rendu côté serveur) | À chaque requête, sur le serveur | Oui | ✅ Solide | Pages publiques personnalisées ou changeant fréquemment |
| SSG (génération de site statique) | Au moment du build | Oui | ✅✅ La plus forte | Docs, blogs, marketing, tout ce qui est stable |
| ISR (régénération statique incrémentale) | Au build, puis régénéré sur un calendrier/à la demande | Oui (sert le dernier statique valide) | ✅✅ Solide | Grands catalogues qui changent mais pas à chaque requête |
| Rendu dynamique | À chaque requête, le bot reçoit du HTML pré-rendu, les utilisateurs reçoivent le CSR | Oui (pour les bots) | ⚠️ Contournement | SPA héritées que vous ne pouvez pas réécrire |
Quelques notes que le tableau ne peut pas porter :
- CSR est le réglage par défaut d’un simple
create-react-appou d’une SPA non configurée. C’est la stratégie qui crée presque tous les problèmes de SEO JavaScript de ce guide. Elle est parfaitement acceptable derrière une connexion où il n’y a rien à indexer. - SSR envoie du HTML entièrement formé à la première requête, puis l’« hydrate » pour en faire une application interactive. Le robot reçoit du vrai contenu dès la vague 1, donc le report du rendu n’a plus d’importance. Le coût, c’est le calcul serveur et la latence par requête.
- SSG pré-rend chaque page vers un fichier
.htmlstatique au moment du build. Il n’y a pas de rendu à attendre ni de serveur susceptible d’être lent — le robot lit le même fichier qu’un CDN sert. C’est l’étalon-or des sites de contenu. - ISR est du SSG capable de se rafraîchir : les pages sont statiques, mais une page peut être régénérée en arrière-plan après N secondes ou à la demande (par ex.
revalidatede Next.js). Les robots reçoivent toujours une page statique valide ; la fraîcheur a un léger retard. Idéal quand vous avez 50 000 pages produit et que tout reconstruire à chaque changement de prix est irréalisable. - Rendu dynamique renifle le user agent et sert un instantané pré-rendu (via quelque chose comme Prerender.io ou Puppeteer) aux bots tandis que les vrais utilisateurs reçoivent la SPA. Google qualifie désormais cela de contournement, pas de recommandation — il ajoute une fragile couche de détection d’UA et un second pipeline de rendu à maintenir. Ne l’envisagez que lorsque réécrire l’application est exclu.
🧑💻 Point de vue développeur : Voyez-le comme une question de qui paie le coût du rendu. Le CSR le fait payer à l’appareil de l’utilisateur et au WRS de Google (deux fois). Le SSR le fait payer à votre serveur à chaque requête. Le SSG le paie une fois au build et plus jamais ensuite. Plus vous poussez ce coût vers la gauche — vers le moment du build — moins vous consommez du budget de rendu de Google et moins le rendu a de façons d’échouer.
Pièges
Voici les problèmes qui ressortent des audits réels, classés approximativement selon la fréquence à laquelle ils causent des pertes de trafic silencieuses.
1. Un routage côté client qui ne met jamais à jour le <head>. Dans une SPA, naviguer entre les routes remplace le DOM sans rechargement complet de page. Si votre routeur ne met pas aussi à jour le titre, la meta description et la canonical, chaque route hérite de ce qui se trouvait dans le HTML initial. Google finit par indexer dix pages qui prétendent toutes être votre page d’accueil.
// ❌ Title and canonical are stale on every soft navigation
router.push('/pricing'); // DOM changes, <head> does not
// ✅ Update head metadata on every route change
router.afterEach((to) => {
document.title = to.meta.title;
document
.querySelector('link[rel="canonical"]')
.setAttribute('href', `https://example.com${to.path}`);
});
2. Du contenu en chargement différé qui exige un défilement ou une interaction. Le défilement infini, les boutons « charger plus » et le contenu qui se monte sur IntersectionObserver sont invisibles pour le WRS parce qu’il ne fait pas défiler la page et ne clique pas. Si le corps de votre article ou votre liste de produits n’apparaît qu’après un événement de scroll, il n’est pas dans le DOM rendu que Google indexe.
3. Routage basé sur les ancres (hash). Les URL comme example.com/#/products/42 sont une relique des anciennes SPA. Tout ce qui suit # est un fragment — par spécification, il n’est jamais envoyé au serveur et Google le traite comme la même URL que example.com/. Les routes par hash réduisent l’ensemble de votre site à une seule page indexable. Utilisez toujours l’History API (pushState) afin que les routes soient de vrais chemins résolubles côté serveur.
4. Timeouts et travail bloquant le rendu. Le WRS abandonne un rendu qui prend trop de temps. Une cascade d’appels fetch() dépendants, un bundle de 4 Mo, ou un script tiers qui se bloque peuvent repousser le contenu au-delà du seuil. Les rendus lents brûlent aussi du budget de rendu, donc moins de vos pages sont rendues du tout.
5. Du contenu conditionné par une interaction utilisateur ou un consentement. Un mur de consentement aux cookies qui bloque le rendu jusqu’au clic sur « Accepter », des onglets dont le contenu ne se charge qu’au clic, ou du texte révélé par un bouton « Lire la suite » — le WRS ne clique sur aucun d’eux. Si c’est important pour le SEO, cela doit être dans le DOM sans interaction.
6. Soft 404 et états d’erreur pilotés par JS. Quand une SPA ne trouve pas une ressource, elle affiche souvent un message « Introuvable » tout en renvoyant un statut HTTP 200. Google voit une réponse réussie avec un contenu maigre et peut indexer la page d’erreur. Renvoyez un vrai statut 404/410 depuis le serveur, ou utilisez un noindex pour le contenu réellement manquant.
Bonnes pratiques
Le fil conducteur : faites en sorte que le contenu critique et les métadonnées se trouvent dans du HTML qui existe avant l’exécution du JavaScript, et traitez le JavaScript comme une amélioration.
- Rendez le contenu critique côté serveur ou au moment du build. Les titres, le corps du texte, la navigation principale, les détails produit et les prix appartiennent au HTML initial via SSR ou SSG. Laissez le JavaScript enrichir une page déjà complète plutôt que de la créer.
- Pratiquez l’amélioration progressive. La page doit transmettre son sens fondamental avec le JS désactivé. Les liens doivent être de vrais éléments
<a href>(le WRS suit<a href>, pas les gestionnairesonClickqui appellentrouter.push). Les boutons qui naviguent doivent être des liens. - Utilisez l’History API et mettez à jour les métadonnées par route. Chaque route a besoin de son propre
title, de sa meta description et de sacanonicalauto-référente, mis à jour à la navigation. Un gestionnaire de head de framework (react-helmet-async,@unhead/vue, les outils intégrés de Next/Astro) gère cela proprement.
// Per-route head with a framework-agnostic shape
function setSeoHead({ title, description, canonical }) {
document.title = title;
upsertMeta('description', description);
upsertLink('canonical', canonical);
}
- Placez les données structurées dans le HTML, pas derrière un fetch différé. Le JSON-LD doit être présent dans le DOM rendu le plus tôt possible. Le SSG/SSR l’émet directement ; si vous devez l’injecter côté client, faites-le de façon synchrone au montage, jamais après un aller-retour de données asynchrone.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "JavaScript SEO & Rendering",
"datePublished": "2026-06-22"
}
</script>
- Gardez le JS léger et résilient. Faites du code-splitting, différez les scripts non critiques, et assurez-vous qu’une seule défaillance tierce ne vide pas la page. Auto-hébergez ou passez en
asyncl’analytics et les tag managers pour qu’ils ne bloquent jamais le contenu. - Ne bloquez pas le JS/CSS dans
robots.txt. Le WRS a besoin de récupérer vos scripts et vos styles pour rendre la page. Bloquer/_next/ou/assets/revient à faire rendre par Google une page cassée.
💡 Conseil : Un test de cohérence rapide pour n’importe quelle page — lancez
curl -s https://your-url | grep "your headline text". Si votre contenu principal n’est pas dans cette sortie, la vague de crawl ne peut pas le voir et vous dépendez entièrement de la vague de rendu différée. C’est la ligne de partage entre SSG/SSR (contenu présent) et CSR (contenu absent).
Débogage
Vous n’avez pas à deviner ce que Google voit — ses outils vous montrent directement la sortie rendue.
- Inspection d’URL dans la Google Search Console. Inspectez une URL en direct, puis ouvrez Afficher la page testée → HTML. C’est le HTML rendu tel que le WRS l’a produit. Cherchez-y votre titre, votre canonical, votre JSON-LD. S’ils manquent ici, ils manquent dans l’index. Les onglets Capture d’écran et Plus d’infos → Ressources de la page révèlent les scripts bloqués et les erreurs de console qui ont cassé le rendu.
- L’opérateur
site:.site:example.com/pricingousite:example.com "phrase exacte de la page"vous dit si une page (et un texte spécifique injecté par JS) est réellement indexée. Aucun résultat pour une phrase qui n’existe que dans votre DOM rendu est un signal fort que le rendu n’aboutit pas. - Comparez vous-même le DOM brut et le DOM rendu.
view-source:(oucurl) montre le HTML de la vague de crawl. DevTools → Elements montre le DOM post-JS. Le diff entre les deux est exactement le contenu qui dépend d’un rendu réussi — minimisez-le pour tout ce qui est critique pour le SEO. - Simulez les contraintes de Googlebot. Dans les Chrome DevTools, désactivez le JavaScript (Command Menu → « Disable JavaScript ») et rechargez : cela approxime la vague de crawl. Bridez le réseau et le CPU pour faire ressortir les rendus qui dépasseraient le délai sous la charge du WRS. Les gratuits Rich Results Test et Mobile-Friendly Test effectuent aussi le rendu avec le moteur même de Google et signalent les erreurs de rendu.
⚠️ Attention : Testez toujours l’URL en direct, pas la version mise en cache par Google, lorsque vous validez un correctif — le cache reflète le dernier rendu réussi, qui peut précéder votre déploiement de plusieurs jours.
Pour aller plus loin
Le rendu JavaScript est fondamentalement une décision que vous prenez dans votre couche de build — le framework et le mode de sortie que vous choisissez déterminent si vous rencontrez jamais ces problèmes. Creusez ces arbitrages dans le guide de la couche Build.
Ce site même contourne toute cette catégorie de problèmes en utilisant Astro avec génération statique : chaque page est pré-rendue en HTML au moment du build, si bien que la vague de crawl voit le contenu complet, les données structurées et les métadonnées, sans aucune dépendance à la file de rendu différée. Ce n’est pas un hasard — c’est le réglage par défaut le plus favorable au SEO disponible, et c’est pourquoi les sites de contenu devraient se tourner vers le SSG en premier.
Points clés à retenir
- ✅ Google rend le JavaScript dans une seconde vague différée (WRS) ; ne supposez jamais que le contenu injecté est indexé rapidement — ni même tout court.
- ✅ Poussez le coût du rendu vers le moment du build : le SSG est le plus solide, le SSR solide, le CSR risqué pour tout ce que vous voulez indexer.
- ✅ Mettez à jour le
title, la meta description et lacanonicalauto-référente à chaque changement de route côté client. - ✅ Gardez le contenu critique et le JSON-LD dans du HTML qui existe avant l’exécution du JS ; le WRS ne fera pas défiler, ne cliquera pas et n’accordera pas de permissions.
- ✅ Vérifiez avec l’Inspection d’URL de la GSC (HTML rendu), l’opérateur
site:et un rechargement JS désactivé — testez l’URL en direct, pas le cache.