Back to articles

Fügen Sie ein SEO-Audit in Ihre Deploy-Pipeline ein: API, Exit-Codes und CI-Rezepte

SEOReport Team·
apici-cdautomationgithub-actionstechnical-seodevops

Ein funktionierendes Rezept zur Auditt Ihres Sites aus CI: drei REST-Endpunkte, die Score-Felder, die ein Build-Gate lesen kann, und der Unterschied zwischen einer echten Regression und einem Infrastruktur-Fehler.

Die technischen Regressionen, die am meisten Sichtbarkeit kosten, sind in einem Code-Review unsichtbar. Ein Canonical-Tag, der auf den falschen Host zeigt, ein hreflang-Block, der seine Selbstreferenz verliert, ein Content‑Security‑Policy-Header, der aus einer Reverse‑Proxy-Konfiguration entfernt wurde, eine Route, die ihren Inhalt hinter Hydration verschiebt — jeder dieser Fälle erscheint in einem Diff, der gut aussieht und jeden Ihrer Tests besteht. Sie treten Wochen später auf, in einem Traffic-Diagramm, lange nachdem der Commit, der sie verursacht hat, aus dem Blickfeld gerutscht ist.

Die Lösung ist strukturell: führen Sie das Audit beim Deploy aus, nicht an dem Tag, an dem sich jemand erinnert. Jeder SEOReport-Bericht ist über REST verfügbar, die Antwort trägt einen numerischen Score-Block, und ein Shell‑Skript kann diesen Block in einen Exit‑Code umwandeln. Das ist der gesamte Mechanismus.

Vier Regressionstypen, die nur eine Maschine rechtzeitig erkennt

Vier Fehlermodi überleben menschliche Reviews besonders gut, und es lohnt sich, sie konkret zu benennen, denn sie sind das, was ein Gate tatsächlich schützt.

Hreflang. Ein Cluster hängt von Selbstreferenz, Rücklinks, einem sinnvollen x-default, gut formatierten URLs und Alternativen ab, die indexierbar sind und auf sich selbst kanonisieren. Eine CMS‑Migration, die URL Muster neu schreibt, kann Rücklinks über alle Lokale gleichzeitig brechen, und kein Locale sieht isoliert fehlerhaft aus.

Canonicalisierung. Ein Canonical muss vorhanden sein, auf ein indexierbares Ziel zeigen, das nicht weiterleitet, und auf dem eigenen Host der Website bleiben. Die schlimmste Version dieses Fehlers ist ein Staging‑Canonical, der in Produktion ausgeliefert wird — eine Seite, die stillschweigend einen Host nominieren, den Sie nicht indexiert haben möchten.

Sicherheitsheader. Content‑Security‑Policy, HSTS, Referrer‑Policy, X‑Frame‑Options, X‑Content‑Type‑Options und Permissions‑Policy. Diese leben in der Infrastruktur‑Konfiguration, nicht im Anwendungscode, was genau der Grund ist, warum sie bei einer Proxy‑ oder Edge‑Config‑Änderung verschwinden und niemand es bemerkt.

Render‑Parität. Eine Seite kann in ihrer HTML-Antwort eine Version ausliefern und nach der Ausführung der Skripte eine andere: einen anderen Titel, fehlende Überschriften, einen spät eingefügten Canonical oder Body‑Text, der erst nach der Hydration existiert. Als unsere veröffentlichten render-parity Daten ein Urteil fällten, boten die meisten Seiten eine materiell dünnere Seite für alles, was HTML als geliefert liest. Ein ssr: false in einer Konfigurationsdatei reicht aus, um eine Seite in diese Gruppe zu verschieben.

Alle vier sind deterministisch, alle vier sind günstig zu prüfen, und alle vier sind die Art von Sache, die eine Person nur untersucht, wenn sie bereits verdächtig ist.

Drei Endpunkte und eine Schleife

Die API Oberfläche, die ein Pipeline benötigt, ist klein. API-Zugriff ist in kostenpflichtigen Plänen enthalten und wird mit einem Bearer-Token authentifiziert: erteile einen Schlüssel vom Dashboard und sende ihn als Authorization: Bearer sr__live_your_api_key. Der gleiche Schlüssel öffnet REST und MCP; die vollständige Referenz befindet sich auf der Entwicklerseite.

graph TD A[Bereitstellung abgeschlossen] --> B["POST /api/v1/reports"] B --> C["GET /api/v1/reports/:id/ready"] C -->|"ready: false"| C C -->|"error.code present"| D[Infrastruktursignal, exit 75] C -->|"ready: true"| E["GET /api/v1/reports/:id"] E --> F{Score-Block innerhalb des Budgets?} F -->|Ja| G[exit 0] F -->|Nein| H[exit 1, Build schlägt fehl]

Einen Bericht einzureichen ist ein 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')

Zwei Dinge über diesen Körper sind in CI speziell wichtig.

forceRerun existiert, weil die API ein vorhandenes Snapshot wiederverwendet, wenn eines verfügbar ist — sinnvoll für eine Person, die einen Button klickt, falsch für ein Deploy-Gate, das sonst die vorherige Version bewerten würde. Die Antwort sagt dir, was in submission.reusedSnapshot passiert ist. Erzwingte frische Läufe sind eine kostenpflichtige Kontofunktion; ein Schlüssel ohne diese Berechtigung erhält ein 403, das den Grund nennt, anstatt stillschweigend veraltete Daten zurückzugeben.

Der Prüfungsumfang wird aus dem URL Pfad abgeleitet. Ein einfacher Ursprung führt eine vollständige Site-Prüfung durch; ein URL mit einem Pfad prüft diese Seite isoliert. So kann ein Pipeline die Startseite bei jedem Deploy sperren und die spezifische Route hinzufügen, die geändert wurde, ohne einen zusätzlichen Parameter.

Dann poll. GET /api/v1/reports/:id/ready ist der günstige Status-Endpunkt — er liefert ready, status, stage, pollAfterMs, und wenn ein Lauf fehlschlägt, ein error Objekt:

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

Dieses exit 75 ist absichtlich, und es ist der Teil, den die meisten Integrationen falsch verstehen.

Unterscheide eine Regression von einem Lauf, der nie stattgefunden hat

error.code trägt einen maschinenlesbaren Grund und ein retryable boolesches Feld. DNS_FAILURE, CRAWLER_BLOCKED, REDIRECT_LOOP, TLS_ERROR, NO_CONTENT, und STALE_ENGINE_VERSION sind als nicht wiederholbar gekennzeichnet, weil ein erneutes Ausführen die gleiche Antwort liefert. Der Großteil dieser Liste beschreibt eine Bedingung auf der Seite, und mehrere Einträge — eine Redirect-Schleife, ein TLS-Fehler, eine Bot-Regel, die jetzt Crawler blockiert — sind echte Deploy-Defekte, die einen Build-Fehler rechtfertigen. Codes außerhalb dieses Satzes sind vorübergehend und verdienen einen weiteren Versuch.

Ein Gate, das „die Seite regressiert“ und „die Prüfung konnte nicht ausgeführt werden“ in dieselbe rote Build zusammenführt, wird innerhalb eines Monats deaktiviert. Halte sie in deinen Exit-Codes getrennt: 1 für ein Urteil, das du vom Gate erzwingen lassen willst, 75 — das konventionelle EX_TEMPFAIL — für einen Lauf, der überhaupt kein Urteil geliefert hat. Die meisten CI-Systeme können so konfiguriert werden, dass sie den zweiten Lauf wiederholen und den ersten an einen Menschen weiterleiten.

Den Score-Block lesen

Sobald ready wahr ist, liefert GET /api/v1/reports/:id das Bericht-Objekt. Der score Block ist der Teil, den ein Gate liest:

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}'

Drei dieser Felder tragen den größten Teil des Signals.

failingCriticalChecks ist die Anzahl der Fehler, die der Engine als kritisch eingestuft werden — die, die in der Lage sind, Seiten aus einem Index zu entfernen oder Inhalte vollständig vor einem Crawler zu verbergen. Für ein erstes Gate ist dies die einzige Zahl, die du brauchst, und > 0 ist ein verteidigbarer Schwellenwert am ersten Tag.

overall ist der zusammengesetzte Score, nützlich als Riegel: speichere den Wert des vorherigen Deploys und fehlschlage, wenn der neue um mehr als eine von dir gewählte Toleranz fällt. Das fängt langsame Erosion ein, die kein einzelner Check meldet.

domainScores zerlegt den Score nach seo, ai, performance, security und brand, jeweils mit eigenen pass, fail und warn Zählungen. Per-Domain-Grenzen lassen verschiedene Teams unterschiedliche Budgets besitzen — der Sicherheits-Score ist ein sinnvoller Gate für jeden, der die Edge-Konfiguration besitzt, unabhängig davon, was das Content-Team liefert.

inconclusiveChecks verdient seine eigene Regel: gate niemals darauf. Eine Prüfung meldet unentschieden, wenn der Motor keinen verteidigbaren Urteil erreichen konnte, und das als Misserfolg zu behandeln, trainiert jeden dazu, die Tür zu ignorieren.

Der Score-Block ist Teil der Berichtszusammenfassung, sodass ein Build-Gate ihn lesen kann, ohne die vollständigen Ergebnisse abzurufen. Wenn Sie die vollständige Nutzlast für die Archivierung benötigen, liefert GET /api/v1/reports/:id/result die vollständigen Ergebnisse, und GET /api/v1/reports/:id/download?format=json liefert dasselbe Objekt als herunterladbares Artefakt – es lohnt sich, es dem Build anzuhängen, damit der Unterschied zwischen zwei Deploys später überprüfbar ist.

In einen Workflow einbinden

Alles oben ist nicht CI-Anbieter-spezifisch. In GitHub Actions ist die gesamte Tür ein Schritt, der das Skript aufruft, das Sie bereits haben:

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

Wo seo-gate.sh einreicht, abfragt und mit der Entscheidung endet:

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"

Führen Sie es aus, nachdem der Deploy abgeschlossen ist, anstatt gegen eine Vorschauumgebung. Eine Vorschau URL ist in der Regel hinter Basic Auth oder einer Bot-Herausforderung, und ein Audit, das die Seite nicht abrufen kann, meldet CRAWLER_BLOCKED statt eines Scores. Post-Deploy gegen die echte Herkunft ist sowohl einfacher als auch näher an dem, was ein Crawler erlebt.

Zwei benachbarte Formen sind es wert, bekannt zu sein. Wenn Ihre Orchestrierung bereits auf einer Scraping-Plattform läuft, führt unser Apify Actor denselben Motor nach Zeitplan aus, ohne diese Klebefolie. Und für Agenten authentifiziert derselbe Bearer-Key eine MCP-Verbindung bei https://seoreport.dev/mcp, wo die Berichtswerkzeuge auf connect beworben werden – ein Agent, der einen Traffic-Abfall untersucht, kann das Audit ausführen und die Ergebnisse selbst lesen, was neben der REST Oberfläche auf der Entwicklerseite dokumentiert ist.

Was die Tür nicht abdeckt

Ein Deploy-Gate beantwortet eine Frage: hat diese Version eine Regression eingeführt. Es bleibt still über alles, was sich ohne Deploy ändert – ein ablaufendes Zertifikat, eine CDN Konfiguration, die in einem Dashboard bearbeitet wurde, ein Drittanbieter-Skript, das das Rendern blockiert, oder die Migration eines Konkurrenten, die ändert, womit Ihre kanonische Seite kollidiert. Per-Domain-Monitoring ist das immer‑an-der‑Stelle-Ergänzung zum Gate: das SEO-Monitoring API führt jede Woche ein erneutes Audit eines registrierten Domains mit demselben Konto und demselben Schlüssel durch.

Beginnen Sie mit der schmalen Version. Ein Endpunkt, ein Feld, failingCriticalChecks > 0, und ein ehrlicher exit 75, wenn das Audit überhaupt nicht ausgeführt werden konnte. Ein Gate, das zweimal im Jahr auslöst und beide Male vertrauenswürdig ist, ist mehr wert als ein umfassendes, das jeder gelernt hat zu überspringen.

Erhalten Sie die vollständige Diagnose Ihrer Website

Ein belegter Bericht und ein priorisierter Aktionsplan – in einem Plan mit monatlichen Credits.