Introduction : quand Google ignore simplement votre blog
Il y a quelques semaines, j'ai pris conscience d'un problème gênant : les articles publiés sur ce même blog n'apparaissaient pas sur Google. Ils n'étaient pas mal positionnés, ils ne recevaient pas peu de trafic, ils n'étaient tout simplement pas indexés. En ouvrant Google Search Console, la situation était claire : sur 32 URL envoyées dans le plan du site, seules 4 étaient indexées (accueil, fiche blog, projets, contacts). Zéro article.
Ce qui suit est le véritable chemin que j'ai emprunté pour diagnostiquer et résoudre le problème, depuis le premier soupçon technique jusqu'à la cause cachée que personne ne vérifie jamais : le contenu en double. Si vous gérez un blog technique, notamment sur une application Angular avec rendu côté serveur, vous reconnaîtrez probablement au moins un de ces problèmes.
Premier soupçon : pages rendues côté client au lieu de côté serveur
Dans ces cas-là, la première vérification est toujours la même : que voit réellement Googlebot lorsqu’il visite une page ? Sur une application Angular avec SSR, si le rendu côté serveur ne fonctionne pas comme prévu, le robot d'exploration ne reçoit que le shell HTML vide - pas de spécifique, pas de méta-description, pas de contenu textuel réel, le tout rempli via JavaScript après les bootstraps angulaires.
Dans mon cas, la cause était dans angulaire.json : le projet utilisait toujours l'ancien indicateur booléen "prerender": true au lieu du nouveau "outputMode": "server". Avec l'indicateur booléen, Angular pré-rend uniquement les routes statiques et ignore silencieusement getPrerenderParams() — la fonction qui développe les routes dynamiques comme /blog/:slug en récupérant tous les slugs publiés par le backend. Résultat : toutes les pages statiques (accueil, projets, contacts) étaient pré-rendues correctement, mais chaque article de blog restait une pure page côté client.
// angular.json — prima (sbagliato)
"prerender": true
// angular.json — dopo (corretto)
"outputMode": "server"
Avec outputMode: "server", Angular appelle en fait getPrerenderParams() pour chaque route dynamique configurée dans app.routes.server.ts, qui dans mon cas interroge GET /blog/posts sur le backend et génère une page statique pour chaque slug publié. Après le correctif, la version a finalement produit chaque article au format HTML réel, avec le titre, la méta description et l'article JSON-LD prêts dans le premier octet.
Le bug caché d'Apache : 301 redirections sur chaque page pré-rendue
Après avoir corrigé le pré-rendu, une vérification avec un simple curl -I (pas un navigateur, qui suit les redirections de manière transparente et masque le problème) a révélé un deuxième défaut : chaque page pré-rendue répondait avec 301 Moved Permanently vers la même URL avec la barre oblique finale ajoutée.
La cause est le comportement par défaut d'Apache, mod_dir : lorsqu'une requête correspond à un répertoire réel sur le disque (et c'est exactement ce que crée le prérendu : /blog/article-name/index.html), Apache redirige automatiquement en ajoutant la barre oblique finale, avant que les règles mod_rewrite du .htaccess puissent intervenir. Le contenu derrière la redirection était correct, mais l'URL canonique déclarée sur chaque page (sans barre oblique finale, cohérente avec le plan du site) n'a jamais renvoyé un 200 direct - les robots ont toujours reçu une URL différente de celle marquée comme canonique.
<IfModule mod_dir.c>
DirectorySlash Off
</IfModule>
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_FILENAME}/index.html -f
RewriteRule ^(.*)$ $1/index.html [L]
En désactivant la barre oblique automatique et en ajoutant une règle explicite qui sert index.html directement sur l'URL sans barre oblique, chaque page pré-rendue a commencé à répondre 200 sur l'URL canonique exacte.
Plan de site statique vs plan de site généré à partir de contenu réel
Un troisième problème, plus banal mais tout aussi bloquant : le plan du site ne contenait que la page listing/blog, pas les articles individuels. Chaque nouvel article publié restait invisible pour Google jusqu'à ce qu'il soit découvert via des liens internes – un processus lent et en aucun cas garanti.
La solution consistait à transformer le script de génération de plan de site d'une liste statique de routes en une fonction qui interroge le backend pour chaque article publié :
async function fetchBlogRoutes() {
const res = await fetch(`${API_BASE_URL}/blog/posts?page=1&limit=50`);
const { data, meta } = await res.json();
const posts = [...data];
for (let page = 2; page <= meta.totalPages; page++) {
const r = await fetch(`${API_BASE_URL}/blog/posts?page=${page}&limit=50`);
const j = await r.json();
posts.push(...j.data);
}
return posts.map(p => ({ loc: `/blog/${p.slug}`, lastmod: p.updatedAt }));
}
Le même script s'exécute désormais également sous GitHub Action, tous les soirs et à chaque poussée, de sorte que chaque nouvel article publié entre automatiquement dans le plan du site sans intervention manuelle.
La cause la plus insidieuse : le contenu dupliqué
Après toutes ces corrections techniques, la situation s'est améliorée mais n'a pas été résolue : la plupart des articles sont restés dans l'état "Détecté, actuellement non indexé" : Google connaissait les URL grâce au plan du site, mais ne les avait pas encore considérées comme suffisamment pertinentes pour être indexées. En analysant la liste article par article, j'ai trouvé deux articles publiés à 28 secondes d'intervalle, sur exactement le même sujet, avec des titres presque impossibles à distinguer et un contenu presque superposé – probablement une double publication accidentelle depuis le panneau de l'éditeur.
Le contenu dupliqué ne bloque pas l'indexation d'une seule page : c'est un signal de qualité évalué au niveau de l'ensemble du domaine. Sur un nouveau site, avec très peu d'autorité externe, quelques publications en double peuvent suffire à rendre Google plus méfiant, même à l'égard du contenu complètement original sur le même domaine. J'ai supprimé la version avec le moins de vues et régénéré le plan du site : c'est probablement la seule intervention ayant le plus grand impact sur l'indexation globale du site.
Comment lire réellement le rapport « Indexation des pages » de la Search Console
Le rapport Google Search Console regroupe les pages non indexées par raison, et chaque étiquette nécessite une action différente :
- Détecté, actuellement non indexé : Google connaît l'URL (généralement à partir du plan du site) mais ne l'a pas encore explorée. C'est une question de temps et de budget d'exploration, pas une erreur à corriger.
- Exploré, actuellement non indexé : Google a visité la page mais a choisi de ne pas l'indexer, souvent en raison d'un contenu jugé de faible valeur ou en double.
- Page alternative avec la balise canonique appropriée : normal pour les versions traduites du même contenu qui pointent correctement vers la version principale – pas une erreur.
- Erreur de redirection : faites attention à la date de la dernière analyse. Si le bug a été corrigé après cette date, le rapport affiche simplement des données obsolètes : vérifiez simplement avec curl -I que l'URL répond 200 aujourd'hui, puis utilisez "Validate fix" pour forcer une nouvelle vérification.
Liste de contrôle pratique
- Vérifiez avec curl -I (pas avec le navigateur) que chaque page répond directement à l'URL canonique, sans redirections cachées.
- Vérifiez que le contenu reçu par un robot est identique à celui d'un vrai navigateur : curl -Un "Googlebot" doit renvoyer du HTML complet, pas un shell vide attendant du JavaScript.
- Générez le plan du site de manière dynamique à partir du contenu publié, et non à partir d'une liste statique d'itinéraires.
- Recherchez des titres ou des sujets presque identiques dans vos publications : le contenu en double est un problème de domaine, pas celui d'une seule page.
- Utilisez « Demander l'indexation » dans la Search Console sur les publications les plus récentes au lieu d'attendre l'exploration naturelle, qui peut prendre des semaines sur un domaine jeune.
Des délais réalistes
Sur un nouveau domaine, avec peu de signes d'autorité externe, il est normal que la première indexation prenne de quelques jours (avec requête manuelle) à plusieurs semaines (via crawl naturel). Ce qui compte vraiment, c'est d'éliminer les obstacles techniques et de qualité du contenu : une fois supprimés, il suffit de laisser à Google le temps d'examiner et de faire confiance au domaine.