Outils · contribution

Le kit d'outils de Tech Everywhere

Un script shell, écrit par Victor Auffray, qui accompagne sa méthode de vérification du <head> publiée ici. Il est utile et il n'est jamais obligatoire : les six étapes de la méthode s'exécutent entièrement à la main, avec curl et grep.

Télécharger le kit · 8,3 Ko Téléchargement direct, sans formulaire et sans adresse email.

Le script n'est pas servi seul, en téléchargement direct : notre serveur refuse tous les fichiers .sh, et nous n'avons pas voulu ouvrir cette règle pour un fichier. Un script qui fait des requêtes réseau se lit avant de se lancer, et une archive qu'on ouvre y invite mieux qu'une commande qu'on colle.

Version 1.4 du 10/09/2026 · Licence MIT · Auteur Victor Auffray · Publié par MarkenTIQ avec son accord écrit, les quatre ajouts du 10/09 validés par lui le même jour · ce qui a changé depuis la 1.0
Ce que contient le kit

Un fichier, trois modes.

head-check.sh répond à une seule question : vos balises canonical, hreflang et <title> tombent-elles avant </head> dans le HTML que Googlebot reçoit vraiment ? Shell POSIX, aucune dépendance en dehors de curl et grep. macOS, Linux, WSL et Git Bash.

01

Une URL

./head-check.sh https://example.com/page

La page demandée avec l'user agent de Googlebot. Le script imprime la position en octets de </head> et celle de chaque balise, puis dit si elle tombe du bon côté. Pour la canonique, il dit aussi si elle pointe sur la page testée elle-même ou ailleurs. Ce sont les étapes 01 à 03 de la méthode.

02

--compare

./head-check.sh --compare https://example.com/page

La même URL demandée deux fois, en Googlebot puis en Chrome. Si les deux lectures ne donnent pas le même rapport, le serveur sert deux HTML différents pour la même adresse. C'est le mode qui rend le problème visible, et c'est l'étape 04.

03

--sitemap

./head-check.sh --sitemap https://example.com/sitemap.xml --limit 20

Toutes les URL d'un sitemap, une ligne chacune, OK, FAIL ou ERR. --limit est facultatif, et n'accepte qu'un nombre entier. C'est l'étape 05.

Codes de sortie

CodeCe qu'il veut dire
0Tout est en place : réponse 200, title et canonical dans le <head>, canonique auto-référente
1Une balise est absente, elle tombe après </head>, la canonique pointe ailleurs, ou la réponse n'est pas un 200
2Erreur d'usage, ou le sitemap lui-même n'a pas pu être lu
3La passe n'a pas pu être menée jusqu'au bout : des URL sont restées illisibles. À rejouer

Le code 1 permet de brancher le script sur une intégration continue et de casser le build quand une page perd sa canonique, ou quand une URL du sitemap cesse de répondre 200.

Une page injoignable n'est pas une page en défaut, et une passe muette n'est pas une passe verte. Les deux sont vrais, et ils ont longtemps semblé s'exclure : la version 1.2 sortait en code 2 dès qu'une URL était illisible, ce qui traitait une coupure réseau comme un problème de page ; la 1.3 ne changeait plus le code du tout, ce qui laissait une passe entièrement muette se terminer en vert. La 1.4 sépare les deux constats au lieu d'en choisir un. Une page qui ne répond pas sort en ERR, jamais en FAIL : rien n'a été lu, donc rien n'est su de son <head>. Et en fin de passe, les URL restées illisibles sont rebalayées une fois de plus ; ce qui répond rentre dans le décompte normal, ce qui ne répond toujours pas laisse la passe incomplète et sort en code 3, avec la consigne de la rejouer. Une intégration continue voit ainsi trois états distincts : les pages sont en défaut, la passe n'a pas eu lieu, ou tout va bien.

Avant de lire un résultat

Cinq choses à savoir avant de conclure.

hreflang absent ne compte pas comme un échec. Une page monolingue n'en a pas.

Une page 404 sort en FAIL, et il a fallu deux versions pour que ce soit vrai de toutes. En mode --sitemap, une ligne FAIL signale souvent une URL morte dans le sitemap plutôt qu'un défaut de <head>. Le cas à connaître est la « soft 404 » : une page d'erreur qui répond 404 mais sert un <head> complet, ce que font beaucoup de CMS. Une 404 vide n'a ni titre ni canonique et tombe donc d'elle-même ; une soft 404, elle, a tout ce que le script cherchait, et sortait conforme. Cette page a annoncé le contraire le 09/09, et c'était faux pour cette forme-là. Depuis la 1.3, le code HTTP est lu, et une réponse qui n'est pas un 200 échoue quel que soit son corps. Ce qu'il faut en retenir pour d'autres outils que celui-ci : un vérificateur de <head> qui ne regarde pas le code de réponse rend un verdict sur une page qui n'existe pas.

Une ligne ERR n'est pas un défaut de <head> : c'est une requête qui n'a pas abouti deux fois de suite. Le réseau, un pare-feu, un hébergeur qui ferme la porte. Ces lignes sont comptées à part de celles qui échouent vraiment.

Les positions sont des octets, pas des caractères, et elles changent à chaque build. Seul leur ordre compte.

Le script lit le HTML brut, avant JavaScript. C'est voulu : une balise canonical placée hors du <head> n'est pas prise en compte par Google, qu'elle soit dans la source ou ajoutée au rendu. Et si un pare-feu applicatif ou un CDN traite l'user agent Googlebot autrement que les autres, le script mesure cette réponse-là, ce qui est en général exactement ce qu'on veut savoir.

Licence

MIT, et ce que cela veut dire ici.

Copyright (c) 2026 Victor Auffray. Usage, copie, modification, intégration et redistribution libres, y compris commercialement, à la seule condition de conserver la mention de licence et le nom de l'auteur. Fourni sans aucune garantie.

Ce script ne modifie rien : il fait une requête HTTP vers l'URL que vous lui donnez, écrit un fichier temporaire, le lit et l'efface. Nous l'avons lu ligne par ligne avant de l'exécuter, dans un conteneur isolé, avec un fichier témoin déposé à côté pour vérifier qu'il n'y touchait pas. Il n'y a pas touché. Aucune commande destructive, aucun envoi vers un tiers, aucun jeton, aucune écriture en dehors du fichier temporaire.

Une réserve qui reste, et elle est dans la nature de l'outil. L'option -L suit les redirections sans restriction d'hôte. C'est le comportement voulu pour un vérificateur, et curl plafonne à cinquante sauts, mais l'URL finalement mesurée peut ne pas être celle que vous avez saisie.

Empreintes

Ce que vous téléchargez, vérifiable.

Empreintes SHA-256 des trois fichiers de l'archive v1.4, relevées le 10/09/2026. Elles vous permettent de contrôler que ce que vous téléchargez est bien ce qui est décrit ici. Le script d'origine, tel que son auteur nous l'a transmis le 08/09, portait l'empreinte d6192bde57176c0d pour 4 707 octets. Sa version 1.3 du 09/09, telle qu'il l'a écrite, porte l'empreinte 7c74fbb8acb0c125 pour 9 835 octets : c'est elle que la 1.4 prolonge, et l'écart entre les deux est décrit plus bas.

FichierTailleEmpreinte SHA-256seize premiers caractères
head-check.sh11 264 o4edf1ece65421127
README.md8 110 oa9ec1100465e024c
LICENSE1 071 o141a5357124c5f7a
archive complète8 494 oc0a56922ad557b80

Pour contrôler chez vous : shasum -a 256 kit-head-check-tech-everywhere-v1.4.zip, et comparez les seize premiers caractères avec la dernière ligne.

D'où vient ce kit

Une contribution, et son histoire.

Victor Auffray
J'accompagne les e-commerçants sur les chantiers techniques que les agences sous-traitent, thèmes Shopify sur-mesure, migrations sans perte de SEO et performance.

Auteur de la méthode « Vérifier ce que Googlebot lit vraiment dans votre page », publiée ici sous sa signature. Ce script est celui qu'il utilise pour l'exécuter, et il l'a écrit après avoir perdu neuf jours sur le défaut que la méthode décrit.

tech-everywhere.fr · LinkedIn

Reçu le 8 septembre 2026 avec sa méthode, son exemple, son README et sa licence MIT, en une seule fois. Rien n'a été exécuté avant d'avoir été lu. Le script a été relu ligne par ligne, puis passé sur notre propre site : 58 URL du sitemap, 58 conformes une fois rejouées les six qu'un incident réseau avait fait échouer (correction n° 2).

Deux défauts fabriqués pour éprouver l'outil. Un contrôle qui n'a jamais échoué sur un défaut fabriqué n'est pas prouvé. Un serveur monté pour l'occasion lui a présenté une page dont la canonique tombe après </head>, puis une page servant deux HTML selon l'user agent. Il trouve les deux, et il nomme le second explicitement.

Puis l'auteur a rejoué ces six corrections sur son propre site, et trois défauts en sont sortis. Le 09/09 au soir, il renvoie une version 1.3 qui porte les six et qui en répare trois de plus, dont deux introduits par les nôtres. Neuf défauts fabriqués, rejoués le 10/09 pour vérifier chaque point plutôt que le croire : les trois tiennent, un quatrième est apparu au passage, et quatre ajouts de plus sont partis chez lui pour validation. Chaque correction du kit a donc été exécutée des deux côtés, sur deux sites qui n'ont pas les mêmes défauts. Le détail est plus bas.

Lire la méthode que cet outil accompagne → · Voir l'exemple complet de son auteur →

Versions · ce qui a changé depuis la 1.0

Treize corrections, et chacune vient de l'exécution de la précédente.

La version servie ici est la 1.4. Elle s'est construite en trois temps, et chaque temps est né de l'exécution du précédent : six corrections après notre passage sur 58 URL, trois de l'auteur après qu'il les a rejouées chez lui, quatre de plus après que nous avons rejoué les siennes. Les quatre premières sont nées de notre exécution de la 1.0 sur les 58 URL de ce site, la cinquième d'un défaut fabriqué rejoué après coup, la sixième de l'exécution du kit sur cette page-ci. Elles ont été proposées à l'auteur, écrites dans son style, shell POSIX et sans dépendance, et appliquées après son accord.

1. La canonique est contrôlée sur sa valeur, pas seulement sur sa position. Sa checklist demande qu'elle pointe sur l'URL testée elle-même ; la 1.0 ne comparait que des positions, et déclarait OK une canonique bien placée qui renvoyait ailleurs. Un contrôle qui n'attrape pas ce que sa propre checklist exige n'est pas fini.

2. Une requête qui échoue est retentée une fois, dans les trois modes. Six de nos pages ont été rendues en échec par un incident réseau, et répondaient 200 au deuxième essai. Une requête qui n'aboutit pas deux fois sort désormais en ERR, comptée à part, et le mode sitemap se termine en code 2 plutôt qu'en 0.

3. Le mode sitemap ne laisse plus le shell développer les URL qu'il lit (set -f, IFS limité au saut de ligne). Sur votre propre sitemap, aucun risque ; sur celui d'un tiers, ces URL sont une entrée non maîtrisée.

4. --limit refuse une valeur non numérique avec un message d'usage, au lieu d'une erreur shell.

5. --compare décide sur les deux rapports, pas sur les deux codes de sortie. Celle-là ne vient pas de l'auteur, elle répare un effet de bord des quatre autres. Sur notre page de test qui sert deux HTML selon l'user agent, les deux lectures échouaient désormais toutes les deux, pour des raisons différentes : codes identiques, et le message le plus important du mode ne sortait plus. Nous ne l'avons pas vue en relisant, nous l'avons vue en rejouant le défaut fabriqué.

6. canonical et hreflang sont cherchés comme des balises, pas comme des mots. Celle-là non plus ne vient pas de l'auteur, et elle a été trouvée d'une façon qui vaut d'être racontée : en lançant le kit sur la page qui publie sa méthode. Cette page cite la commande grep -abo 'hreflang=' dans son étape 03. Le kit a lu ce mot comme une balise hreflang tombée après </head>, et a rendu FAIL. Il n'y avait aucune balise. Une page qui explique des balises était mise en défaut par le mot qu'elle explique. Les motifs exigent désormais un <link, ce qui est aussi plus juste : un hreflang posé sur un <a> n'est pas ce que Google lit pour les alternances de langue.

Puis trois corrections nées de son exécution · version 1.3, 09/09/2026

Les six corrections ci-dessus ont été rejouées par l'auteur chez lui, sur son propre site, avant d'être reprises. Les trois qui suivent en sont sorties, et deux d'entre elles réparent les précédentes. Elles ont été rejouées à leur tour, sur neuf pages fabriquées pour l'occasion : réponse en 200 et en soft 404, balise coupée sur trois lignes, canonique relative, canonique sans href, canonique dont l'hôte change de casse, page qui redirige, page citant le mot hreflang dans sa prose. Chaque correction porte donc deux exécutions, une de chaque côté.

7. Notre correction sur <link avait un angle mort, et il était silencieux. Une balise coupée sur plusieurs lignes, ce que fait n'importe quel formateur de code, ne correspond plus à un motif qui exige <link[^>]*rel= sur une seule ligne. Le faux positif que nous avions supprimé revenait en faux négatif : la page passait de OK à « canonique absente ». Vérifié : sur une page dont la balise tient sur trois lignes, la 1.2 annonce « absent », la 1.3 la trouve. Elle remplace d'abord les retours à la ligne par des espaces, un octet pour un octet, ce qui laisse les positions exactes.

8. Notre comparaison de canonique portait sur la mauvaise adresse. Elle comparait à l'URL demandée, pas à celle qui a répondu : toute URL de sitemap qui redirige devenait un échec, alors que sa canonique est correcte à l'arrivée. Vérifié : la 1.2 rend FAIL sur une page qui redirige, la 1.3 imprime la redirection et conclut sur l'adresse d'arrivée. Nos 58 URL ne redirigent pas, le défaut ne pouvait pas se voir ici ; l'auteur en a dix-huit dans son sitemap.

9. Le code HTTP n'était pas regardé du tout. Une page d'erreur qui sert un <head> complet, une « soft 404 », sortait en OK : le script rendait un verdict de conformité sur une page qui n'existe pas. La 1.3 lit le code, et une réponse qui n'est pas un 200 échoue quel que soit son corps. C'est aussi la correction qui invalide une phrase publiée ici le 09/09, réécrite plus haut.

Et un quatrième défaut, que personne n'avait cherché. En rejouant les siens, nous avons trouvé que la 1.2 mettait aussi en échec toute canonique écrite en relatif, href="/page", faute de la résoudre avant comparaison. Nos 58 URL portent des canoniques absolues ; le défaut ne pouvait pas se voir ici non plus. La 1.3 le corrige au passage, sans que son auteur l'ait visé.

Ce que cette seconde passe enseigne. Nos six corrections avaient été relues, rejouées sur des défauts fabriqués, et publiées avec leurs mesures. Deux d'entre elles étaient fausses ou incomplètes, et c'est l'auteur qui les a trouvées en les exécutant chez lui. Un compte rendu d'exécution n'est pas un verdict : c'est un tour de plus dans une boucle qui n'a pas de dernier tour.

Enfin trois correctifs et un contrôle · version 1.4, 10/09/2026, validée par l'auteur le même jour

La 1.4 est la 1.3 de l'auteur, plus quatre ajouts écrits par MarkenTIQ dans son style, shell POSIX et sans dépendance. Il les a validés le 10/09, sans les rejouer et en le disant plutôt que de laisser croire à une relecture ; l'empreinte de sa 1.3, telle qu'il l'a écrite, est donnée plus haut pour que l'écart reste vérifiable ligne par ligne. Chacun est éprouvé chez nous, et les neuf pages fabriquées rendent les mêmes verdicts qu'avant.

Sa réserve sur le code 3, et elle est dans le README. La plupart des runners d'intégration continue échouent sur tout code non nul : le 3 fera rougir le build exactement comme le 1. C'est le comportement voulu par défaut, une passe incomplète n'étant pas une passe verte, mais celui qui préfère tolérer l'incomplétude a une ligne à écrire pour ça. Le script n'a pas bougé pour autant : son empreinte est la même qu'à midi, seule la documentation a gagné ce point.

10. \L est une extension GNU, et le kit annonce macOS. La normalisation de casse de l'hôte s'écrivait sed -E 's|...|\L\1|'. Hors GNU, un sed BSD écrit un L littéral : la casse n'est plus normalisée, et une canonique dont l'hôte ne diffère que par la majuscule échoue sur macOS et passe sur Linux. Le défaut est étroit et silencieux, et il dépend de la machine, ce qui est le pire des trois. Réécrit avec tr, qui donne le même résultat partout.

11. Une canonique sans href le dit. En mode --sitemap, elle sortait en « canonical in head but points to » suivi de rien. Le verdict était juste, la ligne était illisible. Le mode simple, lui, disait déjà « no href » : les deux modes disent maintenant la même chose.

12. Le sitemap lui-même doit répondre 200. La correction n°9 portait sur les pages, pas sur le fichier qui les liste. Un sitemap qui répond 404 avec une page d'erreur sortait « no <loc> found », ce qui décrit le symptôme et laisse chercher la cause. Il dit maintenant le code reçu.

13. Et le contrôle qui réconcilie les deux lectures d'une URL illisible. En fin de passe, les URL restées illisibles sont rebalayées une fois de plus, après une pause. Ce qui répond au second balayage rentre dans le décompte normal ; ce qui ne répond toujours pas laisse la passe incomplète, et le script sort en code 3 avec la consigne de la rejouer. Aucun des deux constats n'est sacrifié : une coupure réseau n'est pas un défaut de la page, et une passe muette n'est pas une passe verte. Éprouvé sur un serveur qui échoue aux deux premières requêtes puis répond : la passe se termine en code 0 sans intervention, et les six faux échecs de notre exécution du 09/09 se seraient rattrapés tout seuls.

Le script d'origine reste vérifiable : les empreintes de la version reçue le 08/09 et de la 1.3 du 09/09 sont données plus haut, et le détail des modifications est écrit dans le README de l'archive.

Cette page vous a servi ? Partagez-la sur LinkedIn. Pour retrouver nos publications dans vos résultats Google, ajoutez MarkenTIQ à vos sources préférées.