[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$foKydEtyBteJdHaz_5PpYClxGviiAcBm_RkWIa9OjklQ":3,"$fJNpyiFUKuRvWV9cPaoHHNSAiRSNclm_woatE8UhhOg0":25},{"_id":4,"slug":5,"__v":6,"author":7,"body":8,"canonical":9,"category":10,"createdAt":11,"date":12,"description":13,"htmlContent":14,"image":15,"imageAlt":15,"readingTime":16,"tags":17,"title":23,"updatedAt":24},"69d1c32a2656ec73a0642d5c","google-explains-googlebot-byte-limits-and-crawling-architecture-via-sejournal-mattgsouthern",0,"Equipe Seogard","Gary Illyes vient de publier un billet qui change la compréhension qu'on avait du fonctionnement interne de Googlebot. Le point central : Googlebot n'est pas un crawler autonome. C'est un **client** d'une plateforme de crawl centralisée chez Google, et cette plateforme applique des limites strictes en bytes — des limites que la plupart des SEO technique n'avaient jamais pu quantifier avec certitude.\n\nCes révélations ont des conséquences directes sur la façon dont vous structurez vos pages, servez vos ressources et priorisez votre crawl budget. Voici l'analyse technique complète.\n\n## L'architecture réelle de Googlebot : un client parmi d'autres\n\nLa révélation la plus structurante du billet de Gary Illyes concerne l'architecture même du système. Googlebot n'est pas un programme monolithique qui crawle le web de manière autonome. Il s'agit d'un **client** connecté à une plateforme de crawl centralisée — une infrastructure partagée avec d'autres services Google (Google Shopping, Google News, etc.).\n\n### La plateforme de crawl centralisée\n\nCette architecture implique plusieurs choses. D'abord, les limites de ressources ne sont pas propres à Googlebot : elles sont imposées par la plateforme sous-jacente. Quand la plateforme décide qu'un fetch a consommé assez de bytes, elle coupe — que ce soit Googlebot, le crawler de Google News ou un autre client.\n\nConcrètement, la plateforme gère :\n- La file d'attente des URLs à crawler\n- L'allocation de bande passante par domaine (le fameux crawl rate)\n- Les limites de taille par réponse HTTP\n- Le scheduling entre les différents clients\n\nCette centralisation explique pourquoi des comportements de crawl qui semblaient incohérents prennent soudain du sens. Un pic de crawl Google News sur votre domaine peut indirectement affecter la fréquence de crawl de Googlebot pour l'indexation organique — ils partagent la même infrastructure et les mêmes politiques de politeness.\n\n### Ce que ça change pour le diagnostic\n\nQuand vous observez des anomalies dans les logs serveur — des fetches incomplets, des ressources non chargées, des pages partiellement crawlées — vous savez maintenant que le problème peut venir de la plateforme centralisée, pas de Googlebot lui-même. La distinction est importante pour le diagnostic.\n\nPour analyser ce comportement dans vos logs, filtrez les user-agents de manière granulaire :\n\n```bash\n# Extraire les requêtes Googlebot avec le status code et la taille de la réponse\nawk '$0 ~ /Googlebot/ {\n  match($0, /\"[A-Z]+ ([^ ]+)/, url);\n  match($0, /\\\" ([0-9]{3}) ([0-9]+)/, resp);\n  print url[1], resp[1], resp[2]\n}' /var/log/nginx/access.log | sort -t' ' -k3 -n -r | head -50\n```\n\nCette commande vous donne les 50 plus grosses réponses servies à Googlebot. Si vous voyez des réponses qui s'arrêtent systématiquement autour de la même taille, vous touchez probablement une limite de la plateforme de crawl.\n\n## Les limites en bytes : les chiffres enfin documentés\n\nLe billet confirme et précise des limites que la communauté SEO avait partiellement devinées à travers l'expérimentation.\n\n### HTML brut : 15 MB\n\nLa limite pour le document HTML principal est de **15 mégaoctets**. Au-delà, Googlebot tronque. Le contenu après la coupure n'existe tout simplement pas pour Google.\n\n15 MB semble confortable pour une page standard. Mais considérez un catalogue e-commerce qui injecte 8 000 produits dans une page catégorie avec du JSON-LD par produit, des attributs data-*, du markup microdata, et un DOM lourd généré par un framework frontend. On atteint ces limites plus vite qu'on ne le pense.\n\n### Ressources individuelles : la limite par fichier\n\nChaque ressource chargée (CSS, JS, images pour le rendering) est soumise à sa propre limite. Les fichiers JavaScript, en particulier, sont limités eux aussi. Quand un bundle JS dépasse la limite, le Web Rendering Service (WRS) ne peut pas exécuter le code complet — et le contenu rendu côté client disparaît de l'index.\n\nC'est ici que l'impact est le plus vicieux. Un fichier `vendor.js` de 3 MB qui passe un jour et se fait tronquer le lendemain (parce que la plateforme est sous pression) produit un rendering partiel. Le contenu critique — titres, descriptions produit, prix — peut disparaître de la version rendue que Google indexe. Ce type de régression est exactement ce que décrit notre article sur [pourquoi Google voit une page blanche sur votre SPA](/blog/pourquoi-google-voit-une-page-blanche-sur-votre-spa).\n\n### Impact sur le rendering JavaScript\n\nLe Web Rendering Service (WRS) de Google, basé sur une version headless de Chrome, doit charger et exécuter le JavaScript dans les mêmes contraintes de bytes imposées par la plateforme. Voici la chaîne complète :\n\n1. La plateforme fetch le HTML (limite 15 MB)\n2. Le WRS parse le HTML, identifie les ressources\n3. Chaque ressource JS/CSS est fetchée (limite par fichier)\n4. Le WRS exécute le JS dans un Chrome headless avec timeout\n5. Le DOM rendu est renvoyé au pipeline d'indexation\n\nSi le bundle JS est tronqué à l'étape 3, l'étape 4 échoue silencieusement. Pas d'erreur dans Search Console. Pas de signal dans les logs. Juste du contenu manquant dans l'index.\n\nPour vérifier si vos bundles sont à risque :\n\n```bash\n# Lister tous les fichiers JS servis avec leur taille, triés par poids\nfind /var/www/votre-site/public -name \"*.js\" -exec du -h {} + | sort -rh | head -20\n\n# Ou via curl pour voir ce que le serveur envoie réellement (avec compression)\ncurl -sI -H \"Accept-Encoding: gzip\" https://shop.votredomaine.fr/static/js/main.abc123.js | grep -i content-length\n```\n\nAttention : la limite s'applique au contenu **décompressé**. Un fichier de 800 KB gzippé qui fait 2.8 MB une fois décompressé est évalué à 2.8 MB par le WRS.\n\n## Scénario concret : un e-commerce de 22 000 pages face aux byte limits\n\nPrenons un cas réaliste. **MaisonDuMeuble.fr** est un e-commerce Next.js avec 22 000 pages : 18 000 fiches produit, 350 pages catégorie, le reste en pages CMS, blog, et landing pages.\n\n### Le problème initial\n\nL'équipe SEO constate une chute de 35% du trafic organique sur les pages catégorie en 3 semaines. Search Console montre une baisse d'impressions mais aucune erreur de couverture. Le site a déployé une refonte frontend 4 semaines avant.\n\n### Le diagnostic\n\nL'analyse des logs révèle que Googlebot crawle bien les pages catégorie. Mais l'inspection URL dans Search Console montre un écart entre le HTML brut et le HTML rendu : sur les pages catégorie, le listing produits (généré côté client) n'apparaît pas dans la version rendue.\n\nLa cause : la refonte a introduit un nouveau design system. Le bundle principal est passé de 1.2 MB à 4.1 MB (décompressé). Les pages catégorie, qui dépendent entièrement du JS pour afficher les produits, se retrouvent avec un rendering incomplet.\n\n### La correction\n\nL'équipe applique trois mesures :\n\n**1. Code splitting agressif**\n\n```javascript\n// next.config.js - Configuration du code splitting\n/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  webpack: (config, { isServer }) => {\n    if (!isServer) {\n      config.optimization.splitChunks = {\n        chunks: 'all',\n        maxSize: 500000, // 500 KB max par chunk (décompressé)\n        cacheGroups: {\n          vendor: {\n            test: /[\\\\/]node_modules[\\\\/]/,\n            name(module) {\n              // Séparer chaque gros package en son propre chunk\n              const packageName = module.context.match(\n                /[\\\\/]node_modules[\\\\/](.*?)([\\\\/]|$)/\n              )[1];\n              return `vendor.${packageName.replace('@', '')}`;\n            },\n            priority: 10,\n          },\n          // Chunk séparé pour le design system\n          designSystem: {\n            test: /[\\\\/]components[\\\\/]ui[\\\\/]/,\n            name: 'design-system',\n            priority: 20,\n            reuseExistingChunk: true,\n          },\n        },\n      };\n    }\n    return config;\n  },\n};\n\nmodule.exports = nextConfig;\n```\n\n**2. Migration des pages catégorie vers SSR**\n\nLes pages catégorie passent de CSR à SSR avec `getServerSideProps`. Le listing produit est désormais dans le HTML initial, indépendant du JavaScript. Ce choix de rendering est détaillé dans notre comparatif [ISR, SSR, SSG : quel mode de rendering pour le SEO](/blog/isr-ssr-ssg-quel-mode-de-rendering-pour-le-seo).\n\n**3. Monitoring du poids des ressources**\n\nL'équipe ajoute un check dans leur CI/CD :\n\n```bash\n#!/bin/bash\n# check-bundle-size.sh — Script CI pour bloquer les déploiements\n# si un chunk JS dépasse la limite de sécurité\n\nMAX_SIZE_KB=600  # Seuil de sécurité : 600 KB décompressé\nBUILD_DIR=\".next/static/chunks\"\nFAILED=0\n\necho \"Vérification des tailles de bundles JS...\"\n\nfor file in \"$BUILD_DIR\"/*.js; do\n  SIZE_KB=$(du -k \"$file\" | cut -f1)\n  if [ \"$SIZE_KB\" -gt \"$MAX_SIZE_KB\" ]; then\n    echo \"❌ FAIL: $(basename $file) = ${SIZE_KB}KB (limite: ${MAX_SIZE_KB}KB)\"\n    FAILED=1\n  fi\ndone\n\nif [ \"$FAILED\" -eq 1 ]; then\n  echo \"\"\n  echo \"Des chunks JS dépassent la limite de sécurité.\"\n  echo \"Googlebot peut tronquer ces fichiers lors du rendering.\"\n  echo \"Appliquez du code splitting ou migrez le contenu critique vers SSR.\"\n  exit 1\nfi\n\necho \"✅ Tous les chunks JS sont sous ${MAX_SIZE_KB}KB\"\nexit 0\n```\n\n### Le résultat\n\nTrois semaines après le déploiement des correctifs, les pages catégorie récupèrent leur visibilité. Le trafic organique revient au niveau d'avant la refonte, puis dépasse de 8% grâce à l'amélioration du HTML initial (SSR) sur les pages catégorie.\n\nLe point clé : sans l'information sur les byte limits, l'équipe aurait cherché un problème de contenu ou d'algorithme. C'était un problème d'infrastructure pure — un bundle trop gros qui empêchait le rendering.\n\n## Optimiser la taille de vos pages pour la plateforme de crawl\n\nConnaître les limites ne suffit pas. Il faut les intégrer dans votre workflow de développement.\n\n### Auditer le poids réel de vos pages\n\nScreaming Frog permet de mesurer la taille du HTML brut de chaque page, mais pas la taille des ressources chargées pour le rendering. Pour une vision complète, combinez deux approches :\n\n**Screaming Frog** pour le HTML :\n- Configuration > Spider > cochez \"Store HTML\" et \"Store Rendered HTML\"\n- Bulk Export > Response Codes > exportez avec la colonne \"HTML Size\"\n- Filtrez les pages > 5 MB en HTML brut (zone de danger)\n\n**Chrome DevTools** pour les ressources :\n- Network tab > filtrez par \"JS\" > triez par \"Size\" (colonne décompressée)\n- Identifiez les fichiers > 1 MB décompressé\n- Vérifiez le Coverage tab pour le taux de JS inutilisé\n\nPour automatiser cette vérification sur un grand nombre de pages, un script Puppeteer est plus adapté :\n\n```javascript\n// audit-page-weight.mjs\nimport puppeteer from 'puppeteer';\n\nconst urls = [\n  'https://shop.votredomaine.fr/categorie/canapes',\n  'https://shop.votredomaine.fr/categorie/tables',\n  'https://shop.votredomaine.fr/produit/canape-stockholm-velours',\n];\n\nconst BYTE_LIMIT_HTML = 15 * 1024 * 1024; // 15 MB\nconst BYTE_LIMIT_JS = 2 * 1024 * 1024;    // 2 MB seuil d'alerte\n\n(async () => {\n  const browser = await puppeteer.launch({ headless: 'new' });\n\n  for (const url of urls) {\n    const page = await browser.newPage();\n    const resources = [];\n\n    page.on('response', async (response) => {\n      const resourceUrl = response.url();\n      const headers = response.headers();\n      const contentLength = parseInt(headers['content-length'] || '0', 10);\n      const contentType = headers['content-type'] || '';\n\n      if (contentType.includes('javascript')) {\n        try {\n          const buffer = await response.buffer();\n          resources.push({\n            url: resourceUrl.substring(0, 80),\n            compressed: contentLength,\n            decompressed: buffer.length,\n          });\n        } catch (e) { /* response already disposed */ }\n      }\n    });\n\n    const response = await page.goto(url, { waitUntil: 'networkidle0', timeout: 30000 });\n    const htmlContent = await page.content();\n    const htmlSize = Buffer.byteLength(htmlContent, 'utf8');\n\n    console.log(`\\n--- ${url} ---`);\n    console.log(`HTML rendu : ${(htmlSize / 1024).toFixed(0)} KB ${htmlSize > BYTE_LIMIT_HTML ? '⚠️ DÉPASSE 15MB' : '✓'}`);\n\n    const dangerousJS = resources.filter(r => r.decompressed > BYTE_LIMIT_JS);\n    if (dangerousJS.length > 0) {\n      console.log(`⚠️ ${dangerousJS.length} fichier(s) JS > 2MB décompressé :`);\n      dangerousJS.forEach(r => {\n        console.log(`   ${r.url}... → ${(r.decompressed / 1024 / 1024).toFixed(2)} MB`);\n      });\n    } else {\n      console.log(`JS : ${resources.length} fichiers, tous \u003C 2MB ✓`);\n    }\n\n    await page.close();\n  }\n\n  await browser.close();\n})();\n```\n\n### Le piège du HTML gonflé artificiellement\n\nCertains frameworks (surtout en SSR avec hydration) produisent un HTML qui contient **deux fois** le contenu : une fois dans le DOM HTML, une fois sérialisé dans un `\u003Cscript>` pour l'hydration côté client. C'est le cas de Next.js avec `__NEXT_DATA__`, de Nuxt avec `__NUXT__`, et de tout framework qui injecte le state initial en JSON.\n\nSur un catalogue de 200 produits par page catégorie, le JSON sérialisé dans `__NEXT_DATA__` peut facilement atteindre 2-3 MB. Ajoutez le HTML du DOM, les scripts inline, le CSS critique — et vous flirtez avec les limites.\n\nLa solution : paginer agressivement (50 produits max par page), utiliser `getServerSideProps` avec des projections strictes (ne sérialisez que les champs nécessaires à l'affichage), et implémentez la pagination côté serveur, pas côté client. C'est un cas typique de [mismatch entre SSR et hydration](/blog/hydration-mismatch-le-bug-invisible-qui-tue-votre-seo) qui peut avoir des conséquences sur l'indexation.\n\n### Configuration serveur : servir des réponses optimisées à Googlebot\n\nVous ne voulez pas servir une version différente à Googlebot (c'est du cloaking). Mais vous pouvez optimiser la compression et les headers pour que la plateforme de crawl travaille efficacement :\n\n```nginx\n# nginx.conf — Optimisations pour le crawl\nserver {\n    # Compression Brotli pour réduire le transfert\n    # (la limite s'applique au décompressé, mais un transfert plus rapide\n    # réduit les risques de timeout de la plateforme)\n    brotli on;\n    brotli_types text/html text/css application/javascript application/json;\n    brotli_comp_level 6;\n\n    # Headers de taille pour permettre à la plateforme de planifier le fetch\n    location ~* \\.(js|css)$ {\n        add_header Content-Length $content_length always;\n        # Cache long pour que la plateforme ne re-fetche pas les assets stables\n        add_header Cache-Control \"public, max-age=31536000, immutable\";\n    }\n\n    # Limiter la taille des pages HTML dynamiques côté applicatif\n    # Si une page dépasse 10 MB, c'est un bug — renvoyez une 500\n    location / {\n        proxy_pass http://backend;\n        proxy_buffer_size 16k;\n        proxy_buffers 8 1m;\n        proxy_max_temp_file_size 10m; # Sécurité : coupe à 10 MB\n    }\n}\n```\n\n## Implications pour le crawl budget et la priorisation\n\nLes byte limits interagissent directement avec le [crawl budget et les limites de crawl documentées par Google](/blog/google-core-update-crawl-limits-gemini-traffic-data-seo-pulse-via-sejournal-mattgsouthern). La plateforme de crawl centralisée alloue un budget en bytes, pas seulement en requêtes.\n\n### Chaque byte compte dans l'allocation\n\nSi votre page catégorie pèse 8 MB en HTML et que Googlebot doit crawl 350 pages catégorie, c'est 2.8 GB de données rien que pour les pages catégorie. Ajoutez les ressources JS et CSS nécessaires au rendering, et vous pouvez facilement multiplier par 3. Comparez avec un site concurrent dont les pages catégorie pèsent 200 KB en HTML avec du contenu SSR : il consomme 14 fois moins de bande passante pour le même nombre de pages.\n\nLa plateforme de crawl va naturellement favoriser le site le moins coûteux à crawler. Ce n'est pas une pénalité — c'est de la gestion de ressources.\n\n### Prioriser les pages légères pour un crawl complet\n\nPour un site volumineux, la stratégie est claire :\n\n- Les pages à haute valeur SEO (catégories, fiches produit principales) doivent être les plus légères possible en HTML initial. SSR avec des payloads minimaux.\n- Les pages secondaires (CGV, FAQ longues, documentation technique) peuvent être plus lourdes — elles sont crawlées moins fréquemment.\n- Les sitemaps doivent être segmentés par priorité et type de contenu, comme expliqué dans notre article sur [le découpage des sitemaps en fichiers multiples](/blog/google-answers-why-some-seos-split-their-sitemap-into-multiple-files-via-sejournal-martinibuster).\n\n### Surveiller les signaux de truncation\n\nQuand Googlebot tronque une page, il n'y a pas de signal explicite dans Search Console. Les seuls indices :\n\n- **Inspection URL** : comparez le HTML brut et le HTML rendu. Si le rendu s'arrête au milieu d'une section, c'est un signe de truncation JS.\n- **Logs serveur** : des requêtes avec des réponses partielles (connexion fermée avant la fin du transfert).\n- **Couverture d'index** : des pages qui oscillent entre \"indexée\" et \"découverte, pas encore indexée\" sans raison apparente.\n- **Données structurées** : des rich results qui apparaissent et disparaissent de manière cyclique. Si votre JSON-LD est en fin de page et que le HTML est tronqué, le markup disparaît.\n\nUn outil de monitoring continu comme Seogard détecte automatiquement ce type de régression — quand des [meta tags](/blog/meta-tags-seo-le-guide-complet-2025) ou des éléments structurels disparaissent soudainement du rendu, c'est souvent le signe d'un problème de byte limit ou de rendering incomplet.\n\n## Les edge cases que cette architecture révèle\n\n### Les soft 404 géantes\n\nCertains sites génèrent des pages d'erreur personnalisées extrêmement lourdes (navigation complète, recommandations produit, recherche intégrée). Si votre page 404 pèse 3 MB et que Googlebot crawle des milliers d'URLs en 404, vous gaspillez du budget de crawl en bytes sur des pages que vous ne voulez même pas indexer.\n\n### Les pages d'API consommées par le WRS\n\nQuand le WRS exécute votre JavaScript et que celui-ci fait des appels API pour récupérer du contenu, chaque réponse API consomme du budget bytes. Un appel qui retourne 500 KB de JSON pour afficher 10 lignes de texte est un gaspillage que la plateforme n'a aucune raison de tolérer indéfiniment.\n\nSi votre architecture repose sur du client-side rendering avec des appels API, considérez sérieusement le passage à du SSR ou du [prerendering](/blog/prerendering-quand-et-comment-l-utiliser-pour-le-seo) pour éliminer cette couche de fetches supplémentaires. Le [dynamic rendering](/blog/dynamic-rendering-solution-temporaire-ou-piege-seo) peut aussi être une solution transitoire, avec les réserves habituelles sur sa maintenance.\n\n### Le cas des single-page applications\n\nLes SPA sont les plus exposées. Le HTML initial est quasi vide (quelques KB), mais le rendering dépend entièrement de bundles JS lourds. Si un seul de ces bundles est tronqué, l'intégralité du contenu disparaît. C'est la raison pour laquelle [le choix entre SSR et CSR](/blog/ssr-vs-csr-impact-reel-sur-le-seo) n'est pas qu'une question de performance — c'est une question de résilience face aux contraintes de la plateforme de crawl.\n\nPour [vérifier ce que Google voit réellement](/blog/comment-tester-ce-que-google-voit-reellement-sur-votre-site) sur vos pages critiques, l'inspection URL de Search Console reste le premier réflexe. Mais elle ne teste qu'un snapshot à un instant T. Les variations de comportement de la plateforme de crawl (charge, priorisation) font que le résultat peut différer du crawl réel en production.\n\n## Ce que Google ne dit pas (encore)\n\nLe billet de Gary Illyes est une avancée significative en termes de transparence. Mais plusieurs zones d'ombre persistent.\n\nLa limite exacte par fichier JS/CSS n'est pas documentée publiquement. Les 15 MB pour le HTML sont confirmés, mais le seuil pour les ressources individuelles reste flou. L'expérience de la communauté SEO suggère un seuil autour de 15 MB par ressource également, selon la [documentation Google sur les tailles maximales](https://developers.google.com/search/docs/crawling-indexing/googlebot#size-limits), mais le comportement réel peut varier selon la charge de la plateforme.\n\nLa question de la **priorisation interne** entre les clients de la plateforme (Googlebot Search vs. Googlebot News vs. autres) n'est pas abordée. Si votre site est intensivement crawlé par Google Shopping, cela affecte-t-il le budget disponible pour l'indexation organique ? Le billet ne le dit pas.\n\nEnfin, l'impact du rendering JavaScript sur le budget bytes global reste opaque. Quand le WRS fetch 20 fichiers JS pour rendre une page, est-ce que le coût bytes total (HTML + JS + CSS + API calls) est agrégé et limité ? Ou chaque ressource est-elle indépendante ? La réponse a des implications majeures pour les architectures frontend modernes.\n\n---\n\nLes révélations de Gary Illyes sur l'architecture de crawl de Google transforment des intuitions de la communauté SEO en faits documentés. Le takeaway principal : votre site est en compétition pour un budget en bytes autant qu'un budget en requêtes, et chaque mégaoctet superflu dans vos pages réduit la capacité de Google à crawler et indexer l'ensemble de votre site. Intégrez les checks de poids dans votre CI/CD, migrez le contenu critique vers du SSR, et surveillez en continu les régressions de rendering — un outil comme Seogard automatise cette détection pour que vous ne découvriez pas le problème trois semaines après la chute de trafic.\n```","https://seogard.io/blog/google-explains-googlebot-byte-limits-and-crawling-architecture-via-sejournal-mattgsouthern","Actualités SEO","2026-04-05T02:04:26.316Z","2026-04-05","Analyse technique des limites en bytes de Googlebot révélées par Gary Illyes : impact sur le crawl, le rendering et l'optimisation de sites volumineux.","\u003Cp>Gary Illyes vient de publier un billet qui change la compréhension qu'on avait du fonctionnement interne de Googlebot. Le point central : Googlebot n'est pas un crawler autonome. C'est un \u003Cstrong>client\u003C/strong> d'une plateforme de crawl centralisée chez Google, et cette plateforme applique des limites strictes en bytes — des limites que la plupart des SEO technique n'avaient jamais pu quantifier avec certitude.\u003C/p>\n\u003Cp>Ces révélations ont des conséquences directes sur la façon dont vous structurez vos pages, servez vos ressources et priorisez votre crawl budget. Voici l'analyse technique complète.\u003C/p>\n\u003Ch2>L'architecture réelle de Googlebot : un client parmi d'autres\u003C/h2>\n\u003Cp>La révélation la plus structurante du billet de Gary Illyes concerne l'architecture même du système. Googlebot n'est pas un programme monolithique qui crawle le web de manière autonome. Il s'agit d'un \u003Cstrong>client\u003C/strong> connecté à une plateforme de crawl centralisée — une infrastructure partagée avec d'autres services Google (Google Shopping, Google News, etc.).\u003C/p>\n\u003Ch3>La plateforme de crawl centralisée\u003C/h3>\n\u003Cp>Cette architecture implique plusieurs choses. D'abord, les limites de ressources ne sont pas propres à Googlebot : elles sont imposées par la plateforme sous-jacente. Quand la plateforme décide qu'un fetch a consommé assez de bytes, elle coupe — que ce soit Googlebot, le crawler de Google News ou un autre client.\u003C/p>\n\u003Cp>Concrètement, la plateforme gère :\u003C/p>\n\u003Cul>\n\u003Cli>La file d'attente des URLs à crawler\u003C/li>\n\u003Cli>L'allocation de bande passante par domaine (le fameux crawl rate)\u003C/li>\n\u003Cli>Les limites de taille par réponse HTTP\u003C/li>\n\u003Cli>Le scheduling entre les différents clients\u003C/li>\n\u003C/ul>\n\u003Cp>Cette centralisation explique pourquoi des comportements de crawl qui semblaient incohérents prennent soudain du sens. Un pic de crawl Google News sur votre domaine peut indirectement affecter la fréquence de crawl de Googlebot pour l'indexation organique — ils partagent la même infrastructure et les mêmes politiques de politeness.\u003C/p>\n\u003Ch3>Ce que ça change pour le diagnostic\u003C/h3>\n\u003Cp>Quand vous observez des anomalies dans les logs serveur — des fetches incomplets, des ressources non chargées, des pages partiellement crawlées — vous savez maintenant que le problème peut venir de la plateforme centralisée, pas de Googlebot lui-même. La distinction est importante pour le diagnostic.\u003C/p>\n\u003Cp>Pour analyser ce comportement dans vos logs, filtrez les user-agents de manière granulaire :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Extraire les requêtes Googlebot avec le status code et la taille de la réponse\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">awk\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> '$0 ~ /Googlebot/ {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  match($0, /\"[A-Z]+ ([^ ]+)/, url);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  match($0, /\\\" ([0-9]{3}) ([0-9]+)/, resp);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  print url[1], resp[1], resp[2]\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">}'\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> /var/log/nginx/access.log\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#B392F0\"> sort\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -t\u003C/span>\u003Cspan style=\"color:#9ECBFF\">' '\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -k3\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -n\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -r\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#B392F0\"> head\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -50\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Cette commande vous donne les 50 plus grosses réponses servies à Googlebot. Si vous voyez des réponses qui s'arrêtent systématiquement autour de la même taille, vous touchez probablement une limite de la plateforme de crawl.\u003C/p>\n\u003Ch2>Les limites en bytes : les chiffres enfin documentés\u003C/h2>\n\u003Cp>Le billet confirme et précise des limites que la communauté SEO avait partiellement devinées à travers l'expérimentation.\u003C/p>\n\u003Ch3>HTML brut : 15 MB\u003C/h3>\n\u003Cp>La limite pour le document HTML principal est de \u003Cstrong>15 mégaoctets\u003C/strong>. Au-delà, Googlebot tronque. Le contenu après la coupure n'existe tout simplement pas pour Google.\u003C/p>\n\u003Cp>15 MB semble confortable pour une page standard. Mais considérez un catalogue e-commerce qui injecte 8 000 produits dans une page catégorie avec du JSON-LD par produit, des attributs data-*, du markup microdata, et un DOM lourd généré par un framework frontend. On atteint ces limites plus vite qu'on ne le pense.\u003C/p>\n\u003Ch3>Ressources individuelles : la limite par fichier\u003C/h3>\n\u003Cp>Chaque ressource chargée (CSS, JS, images pour le rendering) est soumise à sa propre limite. Les fichiers JavaScript, en particulier, sont limités eux aussi. Quand un bundle JS dépasse la limite, le Web Rendering Service (WRS) ne peut pas exécuter le code complet — et le contenu rendu côté client disparaît de l'index.\u003C/p>\n\u003Cp>C'est ici que l'impact est le plus vicieux. Un fichier \u003Ccode>vendor.js\u003C/code> de 3 MB qui passe un jour et se fait tronquer le lendemain (parce que la plateforme est sous pression) produit un rendering partiel. Le contenu critique — titres, descriptions produit, prix — peut disparaître de la version rendue que Google indexe. Ce type de régression est exactement ce que décrit notre article sur \u003Ca href=\"/blog/pourquoi-google-voit-une-page-blanche-sur-votre-spa\">pourquoi Google voit une page blanche sur votre SPA\u003C/a>.\u003C/p>\n\u003Ch3>Impact sur le rendering JavaScript\u003C/h3>\n\u003Cp>Le Web Rendering Service (WRS) de Google, basé sur une version headless de Chrome, doit charger et exécuter le JavaScript dans les mêmes contraintes de bytes imposées par la plateforme. Voici la chaîne complète :\u003C/p>\n\u003Col>\n\u003Cli>La plateforme fetch le HTML (limite 15 MB)\u003C/li>\n\u003Cli>Le WRS parse le HTML, identifie les ressources\u003C/li>\n\u003Cli>Chaque ressource JS/CSS est fetchée (limite par fichier)\u003C/li>\n\u003Cli>Le WRS exécute le JS dans un Chrome headless avec timeout\u003C/li>\n\u003Cli>Le DOM rendu est renvoyé au pipeline d'indexation\u003C/li>\n\u003C/ol>\n\u003Cp>Si le bundle JS est tronqué à l'étape 3, l'étape 4 échoue silencieusement. Pas d'erreur dans Search Console. Pas de signal dans les logs. Juste du contenu manquant dans l'index.\u003C/p>\n\u003Cp>Pour vérifier si vos bundles sont à risque :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Lister tous les fichiers JS servis avec leur taille, triés par poids\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">find\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> /var/www/votre-site/public\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -name\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"*.js\"\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -exec\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> du\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -h\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> {}\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> +\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#B392F0\"> sort\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -rh\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#B392F0\"> head\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -20\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Ou via curl pour voir ce que le serveur envoie réellement (avec compression)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">curl\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -sI\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -H\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"Accept-Encoding: gzip\"\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> https://shop.votredomaine.fr/static/js/main.abc123.js\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#B392F0\"> grep\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -i\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> content-length\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Attention : la limite s'applique au contenu \u003Cstrong>décompressé\u003C/strong>. Un fichier de 800 KB gzippé qui fait 2.8 MB une fois décompressé est évalué à 2.8 MB par le WRS.\u003C/p>\n\u003Ch2>Scénario concret : un e-commerce de 22 000 pages face aux byte limits\u003C/h2>\n\u003Cp>Prenons un cas réaliste. \u003Cstrong>MaisonDuMeuble.fr\u003C/strong> est un e-commerce Next.js avec 22 000 pages : 18 000 fiches produit, 350 pages catégorie, le reste en pages CMS, blog, et landing pages.\u003C/p>\n\u003Ch3>Le problème initial\u003C/h3>\n\u003Cp>L'équipe SEO constate une chute de 35% du trafic organique sur les pages catégorie en 3 semaines. Search Console montre une baisse d'impressions mais aucune erreur de couverture. Le site a déployé une refonte frontend 4 semaines avant.\u003C/p>\n\u003Ch3>Le diagnostic\u003C/h3>\n\u003Cp>L'analyse des logs révèle que Googlebot crawle bien les pages catégorie. Mais l'inspection URL dans Search Console montre un écart entre le HTML brut et le HTML rendu : sur les pages catégorie, le listing produits (généré côté client) n'apparaît pas dans la version rendue.\u003C/p>\n\u003Cp>La cause : la refonte a introduit un nouveau design system. Le bundle principal est passé de 1.2 MB à 4.1 MB (décompressé). Les pages catégorie, qui dépendent entièrement du JS pour afficher les produits, se retrouvent avec un rendering incomplet.\u003C/p>\n\u003Ch3>La correction\u003C/h3>\n\u003Cp>L'équipe applique trois mesures :\u003C/p>\n\u003Cp>\u003Cstrong>1. Code splitting agressif\u003C/strong>\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">// next.config.js - Configuration du code splitting\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">/** \u003C/span>\u003Cspan style=\"color:#F97583\">@type\u003C/span>\u003Cspan style=\"color:#B392F0\"> {import('next').NextConfig}\u003C/span>\u003Cspan style=\"color:#6A737D\"> */\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> nextConfig\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">  webpack\u003C/span>\u003Cspan style=\"color:#E1E4E8\">: (\u003C/span>\u003Cspan style=\"color:#FFAB70\">config\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, { \u003C/span>\u003Cspan style=\"color:#FFAB70\">isServer\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> }) \u003C/span>\u003Cspan style=\"color:#F97583\">=>\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"color:#F97583\">!\u003C/span>\u003Cspan style=\"color:#E1E4E8\">isServer) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      config.optimization.splitChunks \u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        chunks: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'all'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        maxSize: \u003C/span>\u003Cspan style=\"color:#79B8FF\">500000\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#6A737D\">// 500 KB max par chunk (décompressé)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        cacheGroups: {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">          vendor: {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            test:\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> /\u003C/span>\u003Cspan style=\"color:#79B8FF\">[\u003C/span>\u003Cspan style=\"color:#85E89D;font-weight:bold\">\\\\\u003C/span>\u003Cspan style=\"color:#79B8FF\">/]\u003C/span>\u003Cspan style=\"color:#DBEDFF\">node_modules\u003C/span>\u003Cspan style=\"color:#79B8FF\">[\u003C/span>\u003Cspan style=\"color:#85E89D;font-weight:bold\">\\\\\u003C/span>\u003Cspan style=\"color:#79B8FF\">/]\u003C/span>\u003Cspan style=\"color:#9ECBFF\">/\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">            name\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#FFAB70\">module\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">              // Séparer chaque gros package en son propre chunk\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">              const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> packageName\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#79B8FF\"> module\u003C/span>\u003Cspan style=\"color:#E1E4E8\">.context.\u003C/span>\u003Cspan style=\"color:#B392F0\">match\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">                /\u003C/span>\u003Cspan style=\"color:#79B8FF\">[\u003C/span>\u003Cspan style=\"color:#85E89D;font-weight:bold\">\\\\\u003C/span>\u003Cspan style=\"color:#79B8FF\">/]\u003C/span>\u003Cspan style=\"color:#DBEDFF\">node_modules\u003C/span>\u003Cspan style=\"color:#79B8FF\">[\u003C/span>\u003Cspan style=\"color:#85E89D;font-weight:bold\">\\\\\u003C/span>\u003Cspan style=\"color:#79B8FF\">/]\u003C/span>\u003Cspan style=\"color:#DBEDFF\">(\u003C/span>\u003Cspan style=\"color:#79B8FF\">.\u003C/span>\u003Cspan style=\"color:#F97583\">*?\u003C/span>\u003Cspan style=\"color:#DBEDFF\">)(\u003C/span>\u003Cspan style=\"color:#79B8FF\">[\u003C/span>\u003Cspan style=\"color:#85E89D;font-weight:bold\">\\\\\u003C/span>\u003Cspan style=\"color:#79B8FF\">/]\u003C/span>\u003Cspan style=\"color:#F97583\">|$\u003C/span>\u003Cspan style=\"color:#DBEDFF\">)\u003C/span>\u003Cspan style=\"color:#9ECBFF\">/\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">              )[\u003C/span>\u003Cspan style=\"color:#79B8FF\">1\u003C/span>\u003Cspan style=\"color:#E1E4E8\">];\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">              return\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> `vendor.${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">packageName\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#B392F0\">replace\u003C/span>\u003Cspan style=\"color:#9ECBFF\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'@'\u003C/span>\u003Cspan style=\"color:#9ECBFF\">, \u003C/span>\u003Cspan style=\"color:#9ECBFF\">''\u003C/span>\u003Cspan style=\"color:#9ECBFF\">)\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}`\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            },\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            priority: \u003C/span>\u003Cspan style=\"color:#79B8FF\">10\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">          },\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">          // Chunk séparé pour le design system\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">          designSystem: {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            test:\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> /\u003C/span>\u003Cspan style=\"color:#79B8FF\">[\u003C/span>\u003Cspan style=\"color:#85E89D;font-weight:bold\">\\\\\u003C/span>\u003Cspan style=\"color:#79B8FF\">/]\u003C/span>\u003Cspan style=\"color:#DBEDFF\">components\u003C/span>\u003Cspan style=\"color:#79B8FF\">[\u003C/span>\u003Cspan style=\"color:#85E89D;font-weight:bold\">\\\\\u003C/span>\u003Cspan style=\"color:#79B8FF\">/]\u003C/span>\u003Cspan style=\"color:#DBEDFF\">ui\u003C/span>\u003Cspan style=\"color:#79B8FF\">[\u003C/span>\u003Cspan style=\"color:#85E89D;font-weight:bold\">\\\\\u003C/span>\u003Cspan style=\"color:#79B8FF\">/]\u003C/span>\u003Cspan style=\"color:#9ECBFF\">/\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            name: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'design-system'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            priority: \u003C/span>\u003Cspan style=\"color:#79B8FF\">20\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            reuseExistingChunk: \u003C/span>\u003Cspan style=\"color:#79B8FF\">true\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">          },\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        },\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      };\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    return\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> config;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  },\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">};\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">module\u003C/span>\u003Cspan style=\"color:#E1E4E8\">.\u003C/span>\u003Cspan style=\"color:#79B8FF\">exports\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> nextConfig;\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>\u003Cstrong>2. Migration des pages catégorie vers SSR\u003C/strong>\u003C/p>\n\u003Cp>Les pages catégorie passent de CSR à SSR avec \u003Ccode>getServerSideProps\u003C/code>. Le listing produit est désormais dans le HTML initial, indépendant du JavaScript. Ce choix de rendering est détaillé dans notre comparatif \u003Ca href=\"/blog/isr-ssr-ssg-quel-mode-de-rendering-pour-le-seo\">ISR, SSR, SSG : quel mode de rendering pour le SEO\u003C/a>.\u003C/p>\n\u003Cp>\u003Cstrong>3. Monitoring du poids des ressources\u003C/strong>\u003C/p>\n\u003Cp>L'équipe ajoute un check dans leur CI/CD :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">#!/bin/bash\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># check-bundle-size.sh — Script CI pour bloquer les déploiements\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># si un chunk JS dépasse la limite de sécurité\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">MAX_SIZE_KB\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">600\u003C/span>\u003Cspan style=\"color:#6A737D\">  # Seuil de sécurité : 600 KB décompressé\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">BUILD_DIR\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\".next/static/chunks\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">FAILED\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">0\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">echo\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"Vérification des tailles de bundles JS...\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">for\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> file \u003C/span>\u003Cspan style=\"color:#F97583\">in\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$BUILD_DIR\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"/*.js\u003C/span>\u003Cspan style=\"color:#E1E4E8\">; \u003C/span>\u003Cspan style=\"color:#F97583\">do\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  SIZE_KB\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$(\u003C/span>\u003Cspan style=\"color:#B392F0\">du\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -k\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$file\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#F97583\"> |\u003C/span>\u003Cspan style=\"color:#B392F0\"> cut\u003C/span>\u003Cspan style=\"color:#79B8FF\"> -f1\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> [ \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$SIZE_KB\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#F97583\"> -gt\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$MAX_SIZE_KB\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> ]; \u003C/span>\u003Cspan style=\"color:#F97583\">then\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">    echo\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"❌ FAIL: $(\u003C/span>\u003Cspan style=\"color:#B392F0\">basename\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> $file\u003C/span>\u003Cspan style=\"color:#9ECBFF\">) = ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">SIZE_KB\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}KB (limite: ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">MAX_SIZE_KB\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}KB)\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    FAILED\u003C/span>\u003Cspan style=\"color:#F97583\">=\u003C/span>\u003Cspan style=\"color:#9ECBFF\">1\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  fi\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">done\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> [ \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">$FAILED\u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"\u003C/span>\u003Cspan style=\"color:#F97583\"> -eq\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 1\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> ]; \u003C/span>\u003Cspan style=\"color:#F97583\">then\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  echo\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  echo\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"Des chunks JS dépassent la limite de sécurité.\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  echo\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"Googlebot peut tronquer ces fichiers lors du rendering.\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  echo\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"Appliquez du code splitting ou migrez le contenu critique vers SSR.\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">  exit\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 1\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">fi\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">echo\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> \"✅ Tous les chunks JS sont sous ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">MAX_SIZE_KB\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}KB\"\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#79B8FF\">exit\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 0\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Ch3>Le résultat\u003C/h3>\n\u003Cp>Trois semaines après le déploiement des correctifs, les pages catégorie récupèrent leur visibilité. Le trafic organique revient au niveau d'avant la refonte, puis dépasse de 8% grâce à l'amélioration du HTML initial (SSR) sur les pages catégorie.\u003C/p>\n\u003Cp>Le point clé : sans l'information sur les byte limits, l'équipe aurait cherché un problème de contenu ou d'algorithme. C'était un problème d'infrastructure pure — un bundle trop gros qui empêchait le rendering.\u003C/p>\n\u003Ch2>Optimiser la taille de vos pages pour la plateforme de crawl\u003C/h2>\n\u003Cp>Connaître les limites ne suffit pas. Il faut les intégrer dans votre workflow de développement.\u003C/p>\n\u003Ch3>Auditer le poids réel de vos pages\u003C/h3>\n\u003Cp>Screaming Frog permet de mesurer la taille du HTML brut de chaque page, mais pas la taille des ressources chargées pour le rendering. Pour une vision complète, combinez deux approches :\u003C/p>\n\u003Cp>\u003Cstrong>Screaming Frog\u003C/strong> pour le HTML :\u003C/p>\n\u003Cul>\n\u003Cli>Configuration > Spider > cochez \"Store HTML\" et \"Store Rendered HTML\"\u003C/li>\n\u003Cli>Bulk Export > Response Codes > exportez avec la colonne \"HTML Size\"\u003C/li>\n\u003Cli>Filtrez les pages > 5 MB en HTML brut (zone de danger)\u003C/li>\n\u003C/ul>\n\u003Cp>\u003Cstrong>Chrome DevTools\u003C/strong> pour les ressources :\u003C/p>\n\u003Cul>\n\u003Cli>Network tab > filtrez par \"JS\" > triez par \"Size\" (colonne décompressée)\u003C/li>\n\u003Cli>Identifiez les fichiers > 1 MB décompressé\u003C/li>\n\u003Cli>Vérifiez le Coverage tab pour le taux de JS inutilisé\u003C/li>\n\u003C/ul>\n\u003Cp>Pour automatiser cette vérification sur un grand nombre de pages, un script Puppeteer est plus adapté :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">// audit-page-weight.mjs\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">import\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> puppeteer \u003C/span>\u003Cspan style=\"color:#F97583\">from\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> 'puppeteer'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> urls\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> [\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  'https://shop.votredomaine.fr/categorie/canapes'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  'https://shop.votredomaine.fr/categorie/tables'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">  'https://shop.votredomaine.fr/produit/canape-stockholm-velours'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">];\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> BYTE_LIMIT_HTML\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 15\u003C/span>\u003Cspan style=\"color:#F97583\"> *\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 1024\u003C/span>\u003Cspan style=\"color:#F97583\"> *\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 1024\u003C/span>\u003Cspan style=\"color:#E1E4E8\">; \u003C/span>\u003Cspan style=\"color:#6A737D\">// 15 MB\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> BYTE_LIMIT_JS\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 2\u003C/span>\u003Cspan style=\"color:#F97583\"> *\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 1024\u003C/span>\u003Cspan style=\"color:#F97583\"> *\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 1024\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;    \u003C/span>\u003Cspan style=\"color:#6A737D\">// 2 MB seuil d'alerte\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#F97583\">async\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> () \u003C/span>\u003Cspan style=\"color:#F97583\">=>\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> browser\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#F97583\"> await\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> puppeteer.\u003C/span>\u003Cspan style=\"color:#B392F0\">launch\u003C/span>\u003Cspan style=\"color:#E1E4E8\">({ headless: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'new'\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  for\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"color:#F97583\">const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> url\u003C/span>\u003Cspan style=\"color:#F97583\"> of\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> urls) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> page\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#F97583\"> await\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> browser.\u003C/span>\u003Cspan style=\"color:#B392F0\">newPage\u003C/span>\u003Cspan style=\"color:#E1E4E8\">();\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> resources\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> [];\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    page.\u003C/span>\u003Cspan style=\"color:#B392F0\">on\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'response'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#F97583\">async\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"color:#FFAB70\">response\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) \u003C/span>\u003Cspan style=\"color:#F97583\">=>\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">      const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> resourceUrl\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> response.\u003C/span>\u003Cspan style=\"color:#B392F0\">url\u003C/span>\u003Cspan style=\"color:#E1E4E8\">();\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">      const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> headers\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> response.\u003C/span>\u003Cspan style=\"color:#B392F0\">headers\u003C/span>\u003Cspan style=\"color:#E1E4E8\">();\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">      const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> contentLength\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#B392F0\"> parseInt\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(headers[\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'content-length'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">] \u003C/span>\u003Cspan style=\"color:#F97583\">||\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> '0'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#79B8FF\">10\u003C/span>\u003Cspan style=\"color:#E1E4E8\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">      const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> contentType\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> headers[\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'content-type'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">] \u003C/span>\u003Cspan style=\"color:#F97583\">||\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> ''\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">      if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (contentType.\u003C/span>\u003Cspan style=\"color:#B392F0\">includes\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">'javascript'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">)) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        try\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">          const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> buffer\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#F97583\"> await\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> response.\u003C/span>\u003Cspan style=\"color:#B392F0\">buffer\u003C/span>\u003Cspan style=\"color:#E1E4E8\">();\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">          resources.\u003C/span>\u003Cspan style=\"color:#B392F0\">push\u003C/span>\u003Cspan style=\"color:#E1E4E8\">({\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            url: resourceUrl.\u003C/span>\u003Cspan style=\"color:#B392F0\">substring\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#79B8FF\">0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#79B8FF\">80\u003C/span>\u003Cspan style=\"color:#E1E4E8\">),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            compressed: contentLength,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            decompressed: buffer.\u003C/span>\u003Cspan style=\"color:#79B8FF\">length\u003C/span>\u003Cspan style=\"color:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">          });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        } \u003C/span>\u003Cspan style=\"color:#F97583\">catch\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (e) { \u003C/span>\u003Cspan style=\"color:#6A737D\">/* response already disposed */\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> response\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#F97583\"> await\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> page.\u003C/span>\u003Cspan style=\"color:#B392F0\">goto\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(url, { waitUntil: \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'networkidle0'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">, timeout: \u003C/span>\u003Cspan style=\"color:#79B8FF\">30000\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> htmlContent\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#F97583\"> await\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> page.\u003C/span>\u003Cspan style=\"color:#B392F0\">content\u003C/span>\u003Cspan style=\"color:#E1E4E8\">();\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> htmlSize\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> Buffer.\u003C/span>\u003Cspan style=\"color:#B392F0\">byteLength\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(htmlContent, \u003C/span>\u003Cspan style=\"color:#9ECBFF\">'utf8'\u003C/span>\u003Cspan style=\"color:#E1E4E8\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    console.\u003C/span>\u003Cspan style=\"color:#B392F0\">log\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">`\u003C/span>\u003Cspan style=\"color:#79B8FF\">\\n\u003C/span>\u003Cspan style=\"color:#9ECBFF\">--- ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">url\u003C/span>\u003Cspan style=\"color:#9ECBFF\">} ---`\u003C/span>\u003Cspan style=\"color:#E1E4E8\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    console.\u003C/span>\u003Cspan style=\"color:#B392F0\">log\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">`HTML rendu : ${\u003C/span>\u003Cspan style=\"color:#9ECBFF\">(\u003C/span>\u003Cspan style=\"color:#E1E4E8\">htmlSize\u003C/span>\u003Cspan style=\"color:#F97583\"> /\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 1024\u003C/span>\u003Cspan style=\"color:#9ECBFF\">).\u003C/span>\u003Cspan style=\"color:#B392F0\">toFixed\u003C/span>\u003Cspan style=\"color:#9ECBFF\">(\u003C/span>\u003Cspan style=\"color:#79B8FF\">0\u003C/span>\u003Cspan style=\"color:#9ECBFF\">)\u003C/span>\u003Cspan style=\"color:#9ECBFF\">} KB ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">htmlSize\u003C/span>\u003Cspan style=\"color:#F97583\"> >\u003C/span>\u003Cspan style=\"color:#79B8FF\"> BYTE_LIMIT_HTML\u003C/span>\u003Cspan style=\"color:#F97583\"> ?\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> '⚠️ DÉPASSE 15MB'\u003C/span>\u003Cspan style=\"color:#F97583\"> :\u003C/span>\u003Cspan style=\"color:#9ECBFF\"> '✓'}`\u003C/span>\u003Cspan style=\"color:#E1E4E8\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    const\u003C/span>\u003Cspan style=\"color:#79B8FF\"> dangerousJS\u003C/span>\u003Cspan style=\"color:#F97583\"> =\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> resources.\u003C/span>\u003Cspan style=\"color:#B392F0\">filter\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#FFAB70\">r\u003C/span>\u003Cspan style=\"color:#F97583\"> =>\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> r.decompressed \u003C/span>\u003Cspan style=\"color:#F97583\">>\u003C/span>\u003Cspan style=\"color:#79B8FF\"> BYTE_LIMIT_JS\u003C/span>\u003Cspan style=\"color:#E1E4E8\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    if\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> (dangerousJS.\u003C/span>\u003Cspan style=\"color:#79B8FF\">length\u003C/span>\u003Cspan style=\"color:#F97583\"> >\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 0\u003C/span>\u003Cspan style=\"color:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      console.\u003C/span>\u003Cspan style=\"color:#B392F0\">log\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">`⚠️ ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">dangerousJS\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#79B8FF\">length\u003C/span>\u003Cspan style=\"color:#9ECBFF\">} fichier(s) JS > 2MB décompressé :`\u003C/span>\u003Cspan style=\"color:#E1E4E8\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      dangerousJS.\u003C/span>\u003Cspan style=\"color:#B392F0\">forEach\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#FFAB70\">r\u003C/span>\u003Cspan style=\"color:#F97583\"> =>\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        console.\u003C/span>\u003Cspan style=\"color:#B392F0\">log\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">`   ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">r\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#E1E4E8\">url\u003C/span>\u003Cspan style=\"color:#9ECBFF\">}... → ${\u003C/span>\u003Cspan style=\"color:#9ECBFF\">(\u003C/span>\u003Cspan style=\"color:#E1E4E8\">r\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#E1E4E8\">decompressed\u003C/span>\u003Cspan style=\"color:#F97583\"> /\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 1024\u003C/span>\u003Cspan style=\"color:#F97583\"> /\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 1024\u003C/span>\u003Cspan style=\"color:#9ECBFF\">).\u003C/span>\u003Cspan style=\"color:#B392F0\">toFixed\u003C/span>\u003Cspan style=\"color:#9ECBFF\">(\u003C/span>\u003Cspan style=\"color:#79B8FF\">2\u003C/span>\u003Cspan style=\"color:#9ECBFF\">)\u003C/span>\u003Cspan style=\"color:#9ECBFF\">} MB`\u003C/span>\u003Cspan style=\"color:#E1E4E8\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    } \u003C/span>\u003Cspan style=\"color:#F97583\">else\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">      console.\u003C/span>\u003Cspan style=\"color:#B392F0\">log\u003C/span>\u003Cspan style=\"color:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#9ECBFF\">`JS : ${\u003C/span>\u003Cspan style=\"color:#E1E4E8\">resources\u003C/span>\u003Cspan style=\"color:#9ECBFF\">.\u003C/span>\u003Cspan style=\"color:#79B8FF\">length\u003C/span>\u003Cspan style=\"color:#9ECBFF\">} fichiers, tous &#x3C; 2MB ✓`\u003C/span>\u003Cspan style=\"color:#E1E4E8\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    await\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> page.\u003C/span>\u003Cspan style=\"color:#B392F0\">close\u003C/span>\u003Cspan style=\"color:#E1E4E8\">();\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">  }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">  await\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> browser.\u003C/span>\u003Cspan style=\"color:#B392F0\">close\u003C/span>\u003Cspan style=\"color:#E1E4E8\">();\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">})();\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Ch3>Le piège du HTML gonflé artificiellement\u003C/h3>\n\u003Cp>Certains frameworks (surtout en SSR avec hydration) produisent un HTML qui contient \u003Cstrong>deux fois\u003C/strong> le contenu : une fois dans le DOM HTML, une fois sérialisé dans un \u003Ccode>&#x3C;script>\u003C/code> pour l'hydration côté client. C'est le cas de Next.js avec \u003Ccode>__NEXT_DATA__\u003C/code>, de Nuxt avec \u003Ccode>__NUXT__\u003C/code>, et de tout framework qui injecte le state initial en JSON.\u003C/p>\n\u003Cp>Sur un catalogue de 200 produits par page catégorie, le JSON sérialisé dans \u003Ccode>__NEXT_DATA__\u003C/code> peut facilement atteindre 2-3 MB. Ajoutez le HTML du DOM, les scripts inline, le CSS critique — et vous flirtez avec les limites.\u003C/p>\n\u003Cp>La solution : paginer agressivement (50 produits max par page), utiliser \u003Ccode>getServerSideProps\u003C/code> avec des projections strictes (ne sérialisez que les champs nécessaires à l'affichage), et implémentez la pagination côté serveur, pas côté client. C'est un cas typique de \u003Ca href=\"/blog/hydration-mismatch-le-bug-invisible-qui-tue-votre-seo\">mismatch entre SSR et hydration\u003C/a> qui peut avoir des conséquences sur l'indexation.\u003C/p>\n\u003Ch3>Configuration serveur : servir des réponses optimisées à Googlebot\u003C/h3>\n\u003Cp>Vous ne voulez pas servir une version différente à Googlebot (c'est du cloaking). Mais vous pouvez optimiser la compression et les headers pour que la plateforme de crawl travaille efficacement :\u003C/p>\n\u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># nginx.conf — Optimisations pour le crawl\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">server\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    # Compression Brotli pour réduire le transfert\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    # (la limite s'applique au décompressé, mais un transfert plus rapide\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    # réduit les risques de timeout de la plateforme)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    brotli\u003C/span>\u003Cspan style=\"color:#79B8FF\"> on\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    brotli_types\u003C/span>\u003Cspan style=\"color:#E1E4E8\"> text/html text/css application/javascript application/json;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    brotli_comp_level\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 6\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    # Headers de taille pour permettre à la plateforme de planifier le fetch\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    location\u003C/span>\u003Cspan style=\"color:#F97583\"> ~*\u003C/span>\u003Cspan style=\"color:#DBEDFF\"> \\.(js|css)$ \u003C/span>\u003Cspan style=\"color:#E1E4E8\">{\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        add_header \u003C/span>\u003Cspan style=\"color:#E1E4E8\">Content-Length $content_length always;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">        # Cache long pour que la plateforme ne re-fetche pas les assets stables\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        add_header \u003C/span>\u003Cspan style=\"color:#E1E4E8\">Cache-Control \u003C/span>\u003Cspan style=\"color:#9ECBFF\">\"public, max-age=31536000, immutable\"\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    # Limiter la taille des pages HTML dynamiques côté applicatif\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    # Si une page dépasse 10 MB, c'est un bug — renvoyez une 500\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    location\u003C/span>\u003Cspan style=\"color:#B392F0\"> / \u003C/span>\u003Cspan style=\"color:#E1E4E8\">{\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        proxy_pass \u003C/span>\u003Cspan style=\"color:#E1E4E8\">http://backend;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        proxy_buffer_size \u003C/span>\u003Cspan style=\"color:#79B8FF\">16k\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        proxy_buffers \u003C/span>\u003Cspan style=\"color:#79B8FF\">8\u003C/span>\u003Cspan style=\"color:#79B8FF\"> 1m\u003C/span>\u003Cspan style=\"color:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">        proxy_max_temp_file_size \u003C/span>\u003Cspan style=\"color:#79B8FF\">10m\u003C/span>\u003Cspan style=\"color:#E1E4E8\">; \u003C/span>\u003Cspan style=\"color:#6A737D\"># Sécurité : coupe à 10 MB\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">}\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\n\u003Ch2>Implications pour le crawl budget et la priorisation\u003C/h2>\n\u003Cp>Les byte limits interagissent directement avec le \u003Ca href=\"/blog/google-core-update-crawl-limits-gemini-traffic-data-seo-pulse-via-sejournal-mattgsouthern\">crawl budget et les limites de crawl documentées par Google\u003C/a>. La plateforme de crawl centralisée alloue un budget en bytes, pas seulement en requêtes.\u003C/p>\n\u003Ch3>Chaque byte compte dans l'allocation\u003C/h3>\n\u003Cp>Si votre page catégorie pèse 8 MB en HTML et que Googlebot doit crawl 350 pages catégorie, c'est 2.8 GB de données rien que pour les pages catégorie. Ajoutez les ressources JS et CSS nécessaires au rendering, et vous pouvez facilement multiplier par 3. Comparez avec un site concurrent dont les pages catégorie pèsent 200 KB en HTML avec du contenu SSR : il consomme 14 fois moins de bande passante pour le même nombre de pages.\u003C/p>\n\u003Cp>La plateforme de crawl va naturellement favoriser le site le moins coûteux à crawler. Ce n'est pas une pénalité — c'est de la gestion de ressources.\u003C/p>\n\u003Ch3>Prioriser les pages légères pour un crawl complet\u003C/h3>\n\u003Cp>Pour un site volumineux, la stratégie est claire :\u003C/p>\n\u003Cul>\n\u003Cli>Les pages à haute valeur SEO (catégories, fiches produit principales) doivent être les plus légères possible en HTML initial. SSR avec des payloads minimaux.\u003C/li>\n\u003Cli>Les pages secondaires (CGV, FAQ longues, documentation technique) peuvent être plus lourdes — elles sont crawlées moins fréquemment.\u003C/li>\n\u003Cli>Les sitemaps doivent être segmentés par priorité et type de contenu, comme expliqué dans notre article sur \u003Ca href=\"/blog/google-answers-why-some-seos-split-their-sitemap-into-multiple-files-via-sejournal-martinibuster\">le découpage des sitemaps en fichiers multiples\u003C/a>.\u003C/li>\n\u003C/ul>\n\u003Ch3>Surveiller les signaux de truncation\u003C/h3>\n\u003Cp>Quand Googlebot tronque une page, il n'y a pas de signal explicite dans Search Console. Les seuls indices :\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Inspection URL\u003C/strong> : comparez le HTML brut et le HTML rendu. Si le rendu s'arrête au milieu d'une section, c'est un signe de truncation JS.\u003C/li>\n\u003Cli>\u003Cstrong>Logs serveur\u003C/strong> : des requêtes avec des réponses partielles (connexion fermée avant la fin du transfert).\u003C/li>\n\u003Cli>\u003Cstrong>Couverture d'index\u003C/strong> : des pages qui oscillent entre \"indexée\" et \"découverte, pas encore indexée\" sans raison apparente.\u003C/li>\n\u003Cli>\u003Cstrong>Données structurées\u003C/strong> : des rich results qui apparaissent et disparaissent de manière cyclique. Si votre JSON-LD est en fin de page et que le HTML est tronqué, le markup disparaît.\u003C/li>\n\u003C/ul>\n\u003Cp>Un outil de monitoring continu comme Seogard détecte automatiquement ce type de régression — quand des \u003Ca href=\"/blog/meta-tags-seo-le-guide-complet-2025\">meta tags\u003C/a> ou des éléments structurels disparaissent soudainement du rendu, c'est souvent le signe d'un problème de byte limit ou de rendering incomplet.\u003C/p>\n\u003Ch2>Les edge cases que cette architecture révèle\u003C/h2>\n\u003Ch3>Les soft 404 géantes\u003C/h3>\n\u003Cp>Certains sites génèrent des pages d'erreur personnalisées extrêmement lourdes (navigation complète, recommandations produit, recherche intégrée). Si votre page 404 pèse 3 MB et que Googlebot crawle des milliers d'URLs en 404, vous gaspillez du budget de crawl en bytes sur des pages que vous ne voulez même pas indexer.\u003C/p>\n\u003Ch3>Les pages d'API consommées par le WRS\u003C/h3>\n\u003Cp>Quand le WRS exécute votre JavaScript et que celui-ci fait des appels API pour récupérer du contenu, chaque réponse API consomme du budget bytes. Un appel qui retourne 500 KB de JSON pour afficher 10 lignes de texte est un gaspillage que la plateforme n'a aucune raison de tolérer indéfiniment.\u003C/p>\n\u003Cp>Si votre architecture repose sur du client-side rendering avec des appels API, considérez sérieusement le passage à du SSR ou du \u003Ca href=\"/blog/prerendering-quand-et-comment-l-utiliser-pour-le-seo\">prerendering\u003C/a> pour éliminer cette couche de fetches supplémentaires. Le \u003Ca href=\"/blog/dynamic-rendering-solution-temporaire-ou-piege-seo\">dynamic rendering\u003C/a> peut aussi être une solution transitoire, avec les réserves habituelles sur sa maintenance.\u003C/p>\n\u003Ch3>Le cas des single-page applications\u003C/h3>\n\u003Cp>Les SPA sont les plus exposées. Le HTML initial est quasi vide (quelques KB), mais le rendering dépend entièrement de bundles JS lourds. Si un seul de ces bundles est tronqué, l'intégralité du contenu disparaît. C'est la raison pour laquelle \u003Ca href=\"/blog/ssr-vs-csr-impact-reel-sur-le-seo\">le choix entre SSR et CSR\u003C/a> n'est pas qu'une question de performance — c'est une question de résilience face aux contraintes de la plateforme de crawl.\u003C/p>\n\u003Cp>Pour \u003Ca href=\"/blog/comment-tester-ce-que-google-voit-reellement-sur-votre-site\">vérifier ce que Google voit réellement\u003C/a> sur vos pages critiques, l'inspection URL de Search Console reste le premier réflexe. Mais elle ne teste qu'un snapshot à un instant T. Les variations de comportement de la plateforme de crawl (charge, priorisation) font que le résultat peut différer du crawl réel en production.\u003C/p>\n\u003Ch2>Ce que Google ne dit pas (encore)\u003C/h2>\n\u003Cp>Le billet de Gary Illyes est une avancée significative en termes de transparence. Mais plusieurs zones d'ombre persistent.\u003C/p>\n\u003Cp>La limite exacte par fichier JS/CSS n'est pas documentée publiquement. Les 15 MB pour le HTML sont confirmés, mais le seuil pour les ressources individuelles reste flou. L'expérience de la communauté SEO suggère un seuil autour de 15 MB par ressource également, selon la \u003Ca href=\"https://developers.google.com/search/docs/crawling-indexing/googlebot#size-limits\">documentation Google sur les tailles maximales\u003C/a>, mais le comportement réel peut varier selon la charge de la plateforme.\u003C/p>\n\u003Cp>La question de la \u003Cstrong>priorisation interne\u003C/strong> entre les clients de la plateforme (Googlebot Search vs. Googlebot News vs. autres) n'est pas abordée. Si votre site est intensivement crawlé par Google Shopping, cela affecte-t-il le budget disponible pour l'indexation organique ? Le billet ne le dit pas.\u003C/p>\n\u003Cp>Enfin, l'impact du rendering JavaScript sur le budget bytes global reste opaque. Quand le WRS fetch 20 fichiers JS pour rendre une page, est-ce que le coût bytes total (HTML + JS + CSS + API calls) est agrégé et limité ? Ou chaque ressource est-elle indépendante ? La réponse a des implications majeures pour les architectures frontend modernes.\u003C/p>\n\u003Chr>\n\u003Cp>Les révélations de Gary Illyes sur l'architecture de crawl de Google transforment des intuitions de la communauté SEO en faits documentés. Le takeaway principal : votre site est en compétition pour un budget en bytes autant qu'un budget en requêtes, et chaque mégaoctet superflu dans vos pages réduit la capacité de Google à crawler et indexer l'ensemble de votre site. Intégrez les checks de poids dans votre CI/CD, migrez le contenu critique vers du SSR, et surveillez en continu les régressions de rendering — un outil comme Seogard automatise cette détection pour que vous ne découvriez pas le problème trois semaines après la chute de trafic.\u003C/p>\n\u003Cpre>\u003Ccode>\u003C/code>\u003C/pre>",null,12,[18,19,20,21,22],"googlebot","crawl budget","byte limits","architecture crawl","SEO technique","Googlebot : limites en bytes et architecture de crawl décryptées","Sun Apr 05 2026 02:17:40 GMT+0000 (Coordinated Universal Time)",[26,41,53],{"_id":27,"slug":28,"__v":6,"author":7,"canonical":29,"category":10,"createdAt":30,"date":31,"description":32,"image":15,"imageAlt":15,"readingTime":33,"tags":34,"title":39,"updatedAt":40},"69d481e1f4fa19862862f691","how-to-design-content-that-ai-systems-prefer-and-promote","https://seogard.io/blog/how-to-design-content-that-ai-systems-prefer-and-promote","2026-04-07T04:02:41.265Z","2026-04-07","Comment le passage-level retrieval fonctionne et pourquoi un contenu answer-first, structuré par blocs, maximise vos chances d'être surfacé par les IA.",14,[35,36,37,38,22],"AI content design","passage retrieval","answer-first","structured content","Structurer le contenu pour les systèmes IA : passage retrieval et answer-first","Tue Apr 07 2026 04:02:41 GMT+0000 (Coordinated Universal Time)",{"_id":42,"slug":43,"__v":6,"author":7,"canonical":44,"category":10,"createdAt":45,"date":31,"description":46,"image":15,"imageAlt":15,"readingTime":16,"tags":47,"title":51,"updatedAt":52},"69d4ba23f4fa19862878e7ce","chatgpt-now-crawls-3-6x-more-than-googlebot-what-24m-requests-reveal","https://seogard.io/blog/chatgpt-now-crawls-3-6x-more-than-googlebot-what-24m-requests-reveal","2026-04-07T08:02:43.199Z","Analyse technique de 24M de requêtes de crawl : pourquoi ChatGPT-User dépasse Googlebot et comment adapter votre infrastructure serveur.",[48,18,19,49,50],"chatgpt","log analysis","AI crawlers","ChatGPT crawle 3.6x plus que Googlebot : analyse de 24M de requêtes","Tue Apr 07 2026 08:02:43 GMT+0000 (Coordinated Universal Time)",{"_id":54,"slug":55,"__v":6,"author":7,"canonical":56,"category":10,"createdAt":57,"date":58,"description":59,"image":15,"imageAlt":15,"readingTime":16,"tags":60,"title":66,"updatedAt":67},"69d3db11f4fa19862809a070","seo-in-2026-higher-standards-ai-influence-and-a-web-still-catching-up","https://seogard.io/blog/seo-in-2026-higher-standards-ai-influence-and-a-web-still-catching-up","2026-04-06T16:10:57.670Z","2026-04-06","Analyse technique des évolutions SEO 2026 : gestion des bots IA, LLMs.txt, structured data avancé et monitoring des régressions critiques.",[61,62,63,64,65],"seo 2026","AI SEO","structured data","LLMs.txt","technical SEO","SEO en 2026 : standards relevés, IA omniprésente, web en retard","Mon Apr 06 2026 16:10:57 GMT+0000 (Coordinated Universal Time)"]