Back to articles

Intégrez un audit SEO dans votre pipeline de déploiement : API, codes de sortie et recettes CI

SEOReport Team·
apici-cdautomationgithub-actionstechnical-seodevops

Une recette fonctionnelle pour auditer votre site depuis CI : trois points de terminaison REST, les champs de score qu'un portail de construction peut lire, et la différence entre une vraie régression et un léger incident d'infrastructure.

Les régressions techniques qui coûtent le plus de visibilité restent invisibles lors d'une revue de code. Une balise canonical qui commence à pointer vers le mauvais hôte, un bloc hreflang qui perd sa référence propre, un en-tête Content‑Security‑Policy supprimé d'une configuration de reverse‑proxy, une route qui déplace son contenu derrière l'hydratation — chacun de ces éléments apparaît dans un diff qui semble correct et passe tous les tests que vous avez. Ils apparaissent des semaines plus tard, dans un graphique de trafic, longtemps après le commit qui les a causés, défilé hors de vue. La solution est structurelle : exécuter l'audit au déploiement, pas le jour où quelqu'un se souvient de. Chaque rapport SEOReport est disponible sur REST, la réponse contient un bloc de score numérique, et un script shell peut transformer ce bloc en code de sortie. C'est tout le mécanisme.

Quatre classes de régression que seule une machine détecte à temps

Les vérifications de notre moteur correspondent proprement aux modes d'échec qui survivent à la revue humaine, et il vaut la peine de les nommer concrètement, car c'est ce qu'un portail protège réellement. Hreflang. L'ensemble de vérifications couvre la référence propre, les liens de retour, la x-default exactitude, les URLs bien formées, si chaque cible alternative est indexable, et si le canonique de la cible est conforme. Une migration CMS qui réécrit les motifs URL peut casser les liens de retour dans chaque langue simultanément, et aucune langue ne semble cassée isolément. Canonicalisation. Présence, indexabilité de la cible, cibles sans redirection, et canoniques hors site. La pire version de cette défaillance est un canonique de staging expédié en production — une page qui nomme discrètement un hôte que vous ne voulez pas indexer. En-têtes de sécurité. Content‑Security‑Policy, HSTS, Referrer‑Policy, X‑Frame‑Options, X‑Content‑Type‑Options, et Permissions‑Policy. Ils résident dans la configuration d'infrastructure, pas dans le code d'application, ce qui explique exactement pourquoi ils disparaissent lors d'un changement de proxy ou de configuration edge et que personne ne les remarque. Render parity. Chaque audit récupère la page d'accueil deux fois — plain HTTP, puis un vrai navigateur — et compare le titre, les H1, le canonique, la méta‑description, JSON‑LD, et le volume de texte du corps entre les deux. Lorsque nos données render‑parity publiées ont atteint un verdict, la plupart des sites servaient une page matériellement plus fine à tout ce qui lisait HTML tel qu'il était livré. Un ssr: false dans un fichier de configuration suffit à déplacer un site dans ce groupe. Les quatre sont déterministes, les quatre sont peu coûteux à vérifier, et les quatre sont le genre de chose qu'une personne n'inspecte que lorsqu'elle est déjà suspectée.

Trois points de terminaison et une boucle

La surface API dont un pipeline a besoin est petite. L'authentification est un jeton bearer : créez un compte, émettez une clé depuis le dashboard, et envoyez-la comme Authorization: Bearer sr__live_your_api_key. La même clé ouvre REST et MCP ; la référence complète se trouve sur la page des développeurs [/developers].

graph TD A[Déploiement terminé] --> B["POST /api/v1/reports"] B --> C["GET /api/v1/reports/:id/ready"] C -->|"ready: false"| C C -->|"error.code present"| D[Signal d'infrastructure, sortie 75] C -->|"ready: true"| E["GET /api/v1/reports/:id"] E --> F{Bloc de score dans le budget?} F -->|Oui| G[sortie 0] F -->|Non| H[sortie 1, build échoue]

Soumettre un rapport est un POST :

bash
JOB_ID=$(curl -sS -X POST https://seoreport.dev/api/v1/reports \
-H "Authorization: Bearer $SEOREPORT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "forceRerun": true}' \
| jq -r '.report.jobId')

Deux points concernant ce corps sont importants en CI spécifiquement. forceRerun existe parce que le API réutilise un instantané existant lorsqu'un est disponible — logique pour une personne cliquant sur un bouton, incorrect pour une porte de déploiement, qui autrement noterait la version précédente. La réponse vous indique ce qui s'est passé dans submission.reusedSnapshot. Les exécutions fraîches forcées sont une capacité payante ; une clé sans cette autorisation reçoit un 403 indiquant la raison plutôt que de retourner silencieusement des données obsolètes. La portée de l'audit est déduite du chemin URL. Un origine vierge exécute un audit complet du site ; un URL avec un chemin audit ce page en isolation. Ainsi un pipeline peut bloquer la page d'accueil à chaque déploiement et ajouter la route spécifique touchée par un changement, sans aucun paramètre supplémentaire. Ensuite poll. GET /api/v1/reports/:id/ready est le point de terminaison de statut bon marché — il renvoie ready, status, stage, pollAfterMs, et, lorsqu'une exécution échoue, un objet error :

bash
for _ in $(seq 1 60); do
READY=$(curl -sS -H "Authorization: Bearer $SEOREPORT_API_KEY" \
"https://seoreport.dev/api/v1/reports/$JOB_ID/ready")
CODE=$(echo "$READY" | jq -r '.error.code // empty')
if [ -n "$CODE" ]; then
echo "audit did not complete: $CODE"
exit 75
fi
[ "$(echo "$READY" | jq -r '.ready')" = "true" ] && break
sleep 5
done

Ce exit 75 est intentionnel, et c'est la partie que la plupart des intégrations confondent.

Différenciez une régression d’une exécution qui n’a jamais eu lieu

error.code porte une raison lisible par machine et un booléen retryable. DNS_FAILURE, CRAWLER_BLOCKED, REDIRECT_LOOP, TLS_ERROR, NO_CONTENT et STALE_ENGINE_VERSION sont marqués non réessayables, car réessayer produit la même réponse. La plupart de cette liste décrit une condition sur le site, et plusieurs entrées — une boucle de redirection, une erreur TLS, une règle de bot qui bloque désormais les crawlers — sont de véritables défauts de déploiement qui méritent d’échouer un build. Les codes hors de cet ensemble sont transitoires et méritent une autre tentative. Un portail qui fusionne « le site a régressé » et « l’audit n’a pas pu s’exécuter » en un même build rouge est désactivé dans un mois. Gardez-les séparés dans vos codes de sortie : 1 pour un verdict que vous avez demandé au portail d’appliquer, 75 — le conventionnel EX_TEMPFAIL — pour une exécution qui n’a produit aucun verdict. La plupart des systèmes CI peuvent être configurés pour réessayer le second et alerter un humain pour le premier.

Lire le bloc de score

Une fois ready vrai, GET /api/v1/reports/:id renvoie l’objet de rapport. Le bloc score est la partie que le portail lit :

bash
REPORT=$(curl -sS -H "Authorization: Bearer $SEOREPORT_API_KEY" \
"https://seoreport.dev/api/v1/reports/$JOB_ID")
echo "$REPORT" | jq '.report.score
| {overall, totalChecks, passedChecks, failedChecks,
warnChecks, inconclusiveChecks, failingCriticalChecks}'

Trois de ces champs portent la plupart du signal. failingCriticalChecks est le compte des échecs que le moteur classe comme critiques — ceux capables de retirer des pages d’un index ou de cacher du contenu à un crawler entièrement. Pour un premier portail, c’est le seul nombre dont vous avez besoin, et > 0 est un seuil défendable le premier jour. overall est le score composite, utile comme crémone : stockez la valeur du dernier déploiement et échouez lorsque le nouveau chute de plus qu’une tolérance que vous choisissez. Cela attrape l’érosion lente que aucune vérification unique ne signale. domainScores décompose le score par seo, ai, performance, security et brand, chacun avec son propre pass, fail et warn comptes. Les planchers par domaine permettent à différentes équipes de posséder des budgets différents — le score de sécurité est un portail significatif pour quiconque possède la configuration edge, indépendamment de tout ce que l’équipe contenu déploie. inconclusiveChecks mérite sa propre règle : ne jamais le mettre sous portail. Une vérification signale inconclusive lorsque le moteur ne peut pas atteindre un verdict défendable, et traiter cela comme échec entraîne tout le monde à ignorer le portail. Le bloc de score provient de la section héroïque libre du rapport, donc un portail de build le lit sans débloquer l’ensemble des conclusions. Lorsque vous voulez la charge utile complète pour l’archivage, GET /api/v1/reports/:id/result renvoie les résultats complets pour un rapport déverrouillé, et GET /api/v1/reports/:id/download?format=json renvoie le même objet comme artefact téléchargeable — méritant d’être attaché au build afin que la différence entre deux déploiements soit inspectable plus tard.

Intégrer dans un flux de travail

Rien ci-dessus n’est spécifique au fournisseur CI. Dans GitHub Actions, tout le portail est une étape appelant le script que vous avez déjà :

yaml
- name: SEO audit gate
env:
SEOREPORT_API_KEY: ${{ secrets.SEOREPORT_API_KEY }}
AUDIT_URL: https://example.com
run: ./scripts/seo-gate.sh

seo-gate.sh soumet, vote et se termine par la décision :

bash
CRITICAL=$(echo "$REPORT" | jq -r '.report.score.failingCriticalChecks // 0')
SECURITY=$(echo "$REPORT" | jq -r \
'.report.score.domainScores[] | select(.domain == "security") | .score')
if [ "$CRITICAL" -gt 0 ]; then
echo "::error::$CRITICAL critical checks failing"
exit 1
fi
echo "clean — security domain at $SECURITY"

Exécutez-le après la fin du déploiement plutôt que contre un environnement de prévisualisation. Un aperçu URL est généralement protégé par une authentification de base ou un défi de bot, et un audit qui ne peut pas récupérer la page signale CRAWLER_BLOCKED plutôt qu'un score. Post-déploiement contre l'origine réelle est à la fois plus simple et plus proche de ce que vit un crawler. Deux formes adjacentes valent la peine d'être connues. Si votre orchestration vit déjà sur une plateforme de scraping, notre acteur Apify exécute le même moteur sur un planning sans aucune de cette colle. Et pour les agents, la même clé porteuse authentifie une connexion MCP à https://seoreport.dev/mcp, où les outils de rapport sont annoncés sur connect — un agent enquêtant sur une baisse de trafic peut exécuter l'audit et lire les conclusions lui-même, ce qui est documenté à côté de la surface REST sur la page développeurs.

Ce que la porte ne couvre pas

Une porte de déploiement répond à une question : cette version a-t-elle introduit une régression. Elle reste silencieuse sur tout ce qui change sans déploiement — un certificat expirant, une configuration CDN modifiée dans un tableau de bord, un script tiers qui commence à bloquer le rendu, une migration d'un concurrent qui change ce avec quoi votre canonique entre en collision. La surveillance par domaine est le complément toujours actif à la porte, et elle lit depuis le même compte et la même clé décrits sur la page développeurs. Commencez par la version étroite. Un point de terminaison, un champ, failingCriticalChecks > 0, et un honnête exit 75 lorsque l'audit n'a pas pu s'exécuter du tout. Une porte qui se déclenche deux fois par an et est fiable à chaque fois vaut plus qu'une porte complète que tout le monde a appris à ignorer.

Voir comment votre site se classe

Get a free IA-powered SEO report with actionable findings and priority fixes for your website.

Aucune inscription requise.