Approbation AdSense

Contenu de faible valeur AdSense : trouver les pages fautives

Un script qui mesure chaque URL du sitemap, une matrice garder, enrichir, fusionner ou supprimer, et une page mince réécrite, pour corriger les bonnes pages.

Un carnet ouvert et un stylo posés sur une surface sombre
Photo: Miguel Á. Padriñán sur Pexels

Un refus ne dit pas quelle page AdSense a écartée. Il revient sous la forme d’une courte étiquette, que les éditeurs rapportent comme « contenu de faible valeur », alors que la page d’aide de Google pour un site qui n’est pas prêt parle de contenu utile insuffisant et d’expérience médiocre (nous passons ses 4 causes en revue dans le décodage des motifs de refus). Aucune des deux ne cite d’URL. Les pages faibles se trouvent donc par la mesure : d’abord avec un script, ensuite avec une lecture attentive.

Ce que vise la règle de Google

La règle de Google sur les annonces affichées sur des écrans sans contenu éditeur, que le programme appelle l’inventaire de valeur (« valuable inventory »), est précise sur ce qu’elle exclut. Pas d’annonces sur les écrans sans contenu ou à contenu de faible valeur, sur les pages en construction, ni sur les écrans d’alerte, de navigation ou à but comportemental. Les impasses comme les pages de remerciement, de sortie et d’erreur sont citées, votre contenu doit être le point focal de la page, et le contenu généré automatiquement que personne n’a relu ni soumis à une curation figure parmi les problèmes. La page d’autoévaluation destinée aux nouveaux candidats est moins technique : elle nomme 3 zones, un contenu unique, une navigation utilisable et une expérience qui donne envie de lire.

À notre connaissance, Google ne publie ni nombre de mots, ni part de pages minces qu’un site peut compter, ni liste des URL qu’un examinateur ouvre. La longueur seule ne prouve rien : une page de contact de 120 mots peut très bien servir un visiteur, alors qu’une page de 900 mots tirée d’un gabarit pour la 40e ville peut enfreindre la règle. Ce qui compte, c’est que la page donne à une personne ce qu’elle venait chercher. Voici les types de pages que nous cherchons en premier.

Type de pagePourquoi elle échoueCe qu’il faut vérifier
Archives de tags et de catégories videsUne liste de titres sans texte, construite pour le site et non pour un lecteurOuvrez chaque archive : présente-t-elle le sujet et liste-t-elle assez d’articles pour justifier une visite ?
Pages quasi identiques par ville ou par mot-cléMême texte avec un mot changé, donc aucune information nouvelle d’une page à l’autre ; générer du contenu en masse par automatisation pour manipuler le classement enfreint les règles anti-spam de GooglePlacez 2 pages côte à côte et changez le mot-clé de tête. Si rien d’autre ne change, une seule page suffit
Pages de remerciement, de sortie et d’erreurCitées par Google comme des impassesGardez-les sans annonces et hors du sitemap
Pages « bientôt disponible » ou en constructionCitées par GoogleTerminez-les ou retirez-les
Pages qui ne font que lister des liensLes écrans de navigation sont exclusAjoutez du vrai texte, ou intégrez-les à une page qui en a déjà
Texte copié ou généré sans relectureLe contenu généré automatiquement sans relecture figure sur la liste de Google, et un texte copié peut enfreindre la règle sur la propriété intellectuelleCherchez une phrase singulière entre guillemets, puis lisez la page à voix haute

Extraire chaque URL du sitemap et la mesurer

Votre sitemap est la liste des pages que vous avez déclarées importantes aux moteurs de recherche : auditez-le en premier. Le script ci-dessous lit un sitemap ou un index de sitemaps, interroge chaque URL 4 à la fois, puis affiche les pages les plus courtes d’abord, avec le statut HTTP, le nombre de mots, le nombre de balises H1, les alertes éventuelles et le titre. Il demande Node 18 ou plus récent, rien d’autre. Enregistrez-le sous le nom audit-sitemap.mjs.

// audit-sitemap.mjs
// Liste chaque URL d'un sitemap avec son statut HTTP, son nombre de mots visibles, son nombre de H1 et son titre,
// les pages les plus courtes d'abord. Node 18 ou plus récent, aucune dépendance.
//
//   node audit-sitemap.mjs https://example.com/sitemap.xml [minWords]

const [sitemap, minArg] = process.argv.slice(2);
const MIN_WORDS = Number(minArg) || 300; // notre propre seuil de « page courte », pas un chiffre de Google

const get = (url, redirect = 'follow') =>
  fetch(url, {
    redirect, // 'manual' laisse voir les 301/302 au lieu de les suivre
    headers: { 'user-agent': 'sitemap-content-audit/1.0' },
    signal: AbortSignal.timeout(15000),
  });

// Lit un sitemap, ou un index de sitemaps, et renvoie toutes les URL de pages qu'il liste.
async function urlsFrom(url, depth = 0) {
  const res = await get(url);
  const xml = await res.text();
  if (!res.ok) throw new Error(`${url} answered HTTP ${res.status}`);
  const locs = [...xml.matchAll(/<loc>\s*([^<]+?)\s*<\/loc>/g)].map((m) => m[1].replaceAll('&amp;', '&'));
  if (!/<sitemapindex/i.test(xml)) return locs;
  if (depth >= 2) return [];
  return (await Promise.all(locs.map((l) => urlsFrom(l, depth + 1)))).flat();
}

function readHtml(html, url) {
  const first = (re) => (html.match(re) || [])[1] || '';
  const title = first(/<title[^>]*>([\s\S]*?)<\/title>/i)
    .replace(/&#39;|&apos;/g, "'")
    .replace(/&quot;/g, '"')
    .replace(/&amp;/g, '&')
    .replace(/\s+/g, ' ')
    .trim();
  const h1s = (html.match(/<h1[\s>]/gi) || []).length;
  const robotsTag = (html.match(/<meta[^>]+name=["']robots["'][^>]*>/i) || [''])[0];
  const canonicalTag = (html.match(/<link[^>]+rel=["']canonical["'][^>]*>/i) || [''])[0];
  const href = (canonicalTag.match(/href=(["'])(.*?)\1/i) || [])[2];
  // Compte les mots dans <main> s'il existe, sinon dans <body>, sans les menus ni les scripts.
  const region = (html.match(/<main[\s\S]*?<\/main>/i) || html.match(/<body[\s\S]*<\/body>/i) || [html])[0];
  const words = region
    .replace(/<(script|style|noscript|svg|nav|header|footer|aside)\b[\s\S]*?<\/\1>/gi, ' ')
    .replace(/<[^>]+>/g, ' ')
    .replace(/&[a-z#0-9]+;/gi, ' ')
    .split(/\s+/)
    .filter(Boolean).length;
  return {
    title,
    h1s,
    words,
    noindex: /noindex/i.test(robotsTag),
    canonicalElsewhere: href ? new URL(href, url).href !== url : false,
  };
}

async function inspect(url) {
  const row = { url, status: 0, words: 0, h1s: 0, title: '', flags: [] };
  try {
    const res = await get(url, 'manual');
    row.status = res.status;
    const body = await res.text();
    if (res.status >= 300 && res.status < 400) row.flags.push('redirect');
    else if (res.status !== 200) row.flags.push('error');
    else if (!(res.headers.get('content-type') || '').includes('html')) row.flags.push('not-html');
    else {
      const page = readHtml(body, url);
      Object.assign(row, { words: page.words, h1s: page.h1s, title: page.title });
      if (page.words < MIN_WORDS) row.flags.push('short');
      if (page.h1s !== 1) row.flags.push(page.h1s ? 'multi-h1' : 'no-h1');
      if (page.noindex || /noindex/i.test(res.headers.get('x-robots-tag') || '')) row.flags.push('noindex');
      if (page.canonicalElsewhere) row.flags.push('canonical-elsewhere');
    }
  } catch (err) {
    row.status = 'ERR';
    row.flags.push(err.name === 'TimeoutError' ? 'timeout' : 'unreachable');
  }
  return row;
}

async function main() {
  if (!sitemap) throw new Error('Usage: node audit-sitemap.mjs <sitemap-url> [min-words]');
  const urls = [...new Set(await urlsFrom(sitemap))];
  const rows = [];
  let next = 0;
  // Quatre requêtes à la fois : la charge sur le site reste négligeable.
  await Promise.all(
    Array.from({ length: 4 }, async () => {
      while (next < urls.length) rows.push(await inspect(urls[next++]));
    })
  );

  // Un même titre sur plusieurs URL trahit des pages générées depuis un gabarit.
  const seen = new Map();
  for (const r of rows) if (r.title) seen.set(r.title, (seen.get(r.title) || 0) + 1);
  for (const r of rows) if (seen.get(r.title) > 1) r.flags.push('duplicate-title');

  rows.sort((a, b) => a.words - b.words);
  console.log('STATUS  WORDS  H1  FLAGS'.padEnd(54) + 'URL  |  TITLE');
  for (const r of rows) {
    const cols = [String(r.status).padEnd(7), String(r.words).padStart(5), String(r.h1s).padStart(2)];
    console.log(`${cols[0]} ${cols[1]}  ${cols[2]}  ${r.flags.join(',').padEnd(34)} ${r.url}  |  ${r.title.slice(0, 50)}`);
  }
  console.log(`\n${rows.length} URLs, ${rows.filter((r) => r.flags.length).length} with at least one flag.`);
}

main().catch((err) => {
  console.error(err.message);
  process.exitCode = 1;
});

Lancez-le avec node audit-sitemap.mjs https://example.com/sitemap.xml 300. Le dernier argument, facultatif, fixe le nombre de mots en dessous duquel une page reçoit l’alerte short. La valeur par défaut, 300, est notre propre seuil et non un chiffre de Google : relevez-le pour des articles, abaissez-le pour des pages de contact. Voici une version abrégée de ce qu’il a affiché le 7 octobre 2026 sur notre propre site, avant la refonte :

STATUS  WORDS  H1  FLAGS                              URL  |  TITLE
200       118   1  short                              https://greenlightmysite.com/contact/  |  Contact GreenlightMySite
200       137   1  short                              https://greenlightmysite.com/fr/contact/  |  Contacter GreenlightMySite
200       210   1  short                              https://greenlightmysite.com/terms/  |  Terms of use
200       232   1  short                              https://greenlightmysite.com/fr/terms/  |  Conditions d'utilisation
200       300   1                                     https://greenlightmysite.com/about/  |  About GreenlightMySite
...
14 URLs, 4 with at least one flag.

Les 4 pages signalées étaient les pages de contact et de conditions dans les deux langues, à 118 et 210 mots en anglais. Le script ne compte que la zone de contenu principale : ses chiffres sont donc plus bas qu’un décompte de la page entière. Comparez vos pages entre elles, pas à un chiffre issu d’un autre outil. Ces pages ont rejoint notre liste de réécriture pour la refonte.

Au-delà de short, chaque alerte appelle une action précise. redirect, error et noindex signifient que l’URL n’a rien à faire dans le sitemap. canonical-elsewhere signifie que le sitemap liste une page qui désigne une autre URL comme son original. duplicate-title marque en général des pages tirées d’un gabarit, qui méritent une comparaison côte à côte, et no-h1 ou multi-h1 sont des problèmes de structure plutôt que de contenu.

Le script a des limites à connaître. Il lit le HTML que renvoie votre serveur : une page qui fabrique son texte en JavaScript après le chargement lui paraît presque vide, alors vérifiez-la à la main avant d’agir. Le nombre de mots ne dit presque rien non plus pour le chinois ou le japonais, qui ne mettent pas d’espaces entre les mots. Lancez-le sur des sites qui vous appartiennent ou que vous gérez : 4 requêtes à la fois, c’est discret, mais cela reste un robot d’exploration.

Lire les pages signalées, et un échantillon des autres

Le script produit des candidates, rien de plus. Une page peut passer toutes les alertes et n’avoir rien à dire : consacrez donc une heure à la lecture. Prenez les pages signalées, les 5 plus longues et 5 autres au hasard, et posez 3 questions à chacune. Elles sont de nous, adaptées de l’autoévaluation de Google.

  1. Quelqu’un qui arrive par une recherche sur le sujet de cette page repart-il avec ce qu’il venait chercher ?
  2. La page dit-elle quelque chose que les premiers résultats sur ce sujet ne disent pas : vos propres mesures, un exemple que vous avez construit, une erreur que vous avez commise ? C’est là que l’expertise réelle se voit, ou ne se voit pas.
  3. Seriez-vous à l’aise si c’était la première page qu’un examinateur ouvrait ?

Demandez ensuite à quoi sert chaque page. Si la réponse honnête est « à porter une annonce » ou « à porter un lien », elle va dans la colonne des pages à supprimer. Terminez avec le vérificateur SEO sur les pages que vous comptez garder : il audite une page à la fois, titre, intertitres et balise canonical compris.

Décider : garder, enrichir, fusionner, passer en noindex ou supprimer

Donnez à chaque URL signalée un verdict tiré de cette matrice, et notez-le dans un tableur à côté de l’URL.

VerdictÀ utiliser quandCommentAttention à
GarderLa page sert un lecteur et dit quelque chose de précis, même si elle est courte (contact, mentions légales)Laissez-la. Ajoutez une phrase si son rôle n’est pas clairCourte ne veut pas dire mince
EnrichirLe sujet est bon, mais la page s’arrête à l’évidentAjoutez ce que vous seul pouvez apporter : des mesures, un exemple détaillé, un tableau, les erreurs que vous avez vuesRallonger pour atteindre un nombre de mots ajoute de la longueur, pas de l’information
FusionnerPlusieurs pages traitent un même sujet par fragments, ou sont quasi identiquesRéunissez le meilleur dans une seule page et envoyez les autres vers elle par une 301Redirigez vers l’équivalent le plus proche, pas vers l’accueil
NoindexLa page aide les visiteurs mais n’a aucune raison d’apparaître dans les résultats : remerciements, résultats de recherche interneAjoutez <meta name="robots" content="noindex">, laissez la page explorable et retirez-la du sitemapUne balise noindex n’est vue que si Google peut récupérer la page
SupprimerPersonne n’en a besoin : pages provisoires, archives vides, copies périméesRenvoyez 404 ou 410, retirez les liens internes et l’entrée du sitemapVérifiez d’abord ce qui pointe vers elle

Deux points de la documentation de Google structurent ce tableau. Une 301 est un signal fort qu’une page a déménagé, ce qui explique que la fusion fonctionne, et 404 comme 410 retirent une URL de l’index. Ensuite, bloquer une page dans robots.txt ne la masque pas : l’URL peut rester indexée, affichée sans extrait, et un robot qui ne peut pas lire la page ne peut pas non plus y lire un noindex.

Google ne dit pas comment les pages en noindex comptent quand un site est examiné, et nous n’avons aucune preuve dans un sens ni dans l’autre. Notre ordre de préférence en découle : supprimez ou fusionnez les pages qui ne servent personne, enrichissez celles qui ont un vrai sujet, et réservez le noindex aux pages utiles aux visiteurs mais pas à la recherche. Après une fusion, mettez à jour le maillage interne pour qu’il pointe vers la page conservée plutôt que vers une redirection.

Une page mince, réécrite

Imaginez un site de calculateurs avec une page pour son simulateur d’intérêts composés. L’exemple est inventé, mais le schéma est bien réel. La page telle qu’elle est, 40 mots :

Utilisez notre simulateur gratuit d’intérêts composés pour voir comment votre épargne grandit avec le temps. Saisissez votre capital de départ, le taux d’intérêt et le nombre d’années. Notre simulateur est rapide, simple et gratuit. Essayez-le et obtenez votre résultat instantanément.

Elle décrit l’outil et n’apprend rien. N’importe quel site de calculateurs pourrait la coller sur sa propre page, et elle ne dit jamais quoi faire du résultat. Voici le plan que nous donnerions à la réécriture, puis 3 phrases qui en sont tirées, toujours à titre d’illustration :

  • Ce que calcule le simulateur, avec la formule A = P(1 + r/n)^(nt) posée et chaque lettre expliquée.
  • Un exemple chiffré.
  • La façon dont la fréquence de capitalisation change le résultat.
  • Les erreurs qui faussent les résultats : mauvaises unités, frais ignorés, inflation oubliée.
  • Ce que le simulateur ne couvre pas, comme l’impôt.

Avec 1 000 € placés à 5 % par an, les intérêts étant ajoutés une fois par an, le capital au bout de 10 ans est de 1 628,89 €. La première année rapporte 50,00 € d’intérêts et la dixième 77,57 €, parce que les intérêts rapportent eux-mêmes des intérêts. Passez le même 5 % en capitalisation mensuelle et le capital à 10 ans devient 1 647,01 €, soit un gain de 18,12 € pour avoir changé le seul calendrier.

N’importe qui peut vérifier ces chiffres avec un tableur, et c’est le but : la page contient maintenant une information que la version générique ne pouvait pas contenir, et un lecteur peut la contrôler. La réécriture n’a pas eu besoin d’une longue page, seulement d’un contenu que le gabarit n’avait pas. Quand votre inventaire est propre, le plan sur 3 semaines prend le relais pour la seconde candidature.

Sources

Qui est derrière ce site

GreenlightMySite est tenu par Stéphane Cardon, ingénieur réseaux et cybersécurité. Nous écrivons à partir de vrais sites passés par de vraies candidatures, et chaque affirmation technique est soit liée à une source primaire, soit mesurée avec nos propres outils.

À propos · Politique éditoriale · LinkedIn