Wie ein Astro-Plugin im Build-Prozess kaputte Links, fehlende Alt-Texte, SEO-Probleme und Privacy-Risiken findet – ohne Browser, ohne Netzwerk, in unter einer Sekunde.
Wer Websites baut, kennt das: die Seite sieht gut aus, der Build läuft durch — und trotzdem fehlt auf drei Unterseiten das Canonical-Tag, ein interner Link zeigt ins Leere, und das Logo hat kein width/height. Fehler, die man erst bemerkt, wenn der Lighthouse-Score einbricht oder ein Crawler sie meldet.
Genau dafür habe ich astro-post-audit gebaut. Das Tool entstand aus dem eigenen Bedarf bei CASOON: Kundenprojekte, die sauber ausgeliefert werden müssen — barrierefrei, fehlerfrei, mit korrekten Meta-Daten. Kein Audit-Tool, das alles abdeckt, aber eines, das einem schnell ein paar Sachen abnimmt — automatisch, bei jedem Build, ohne extra Aufwand.
Warum nach dem Build?
Die meisten Audit-Tools arbeiten gegen eine laufende Website. Das bedeutet: Deploy, warten, prüfen, Fehler finden, fixen, neu deployen. astro-post-audit dreht die Reihenfolge um. Der Audit läuft als letzter Schritt im Build-Prozess — nach astro build, vor dem Deploy. Fehler tauchen auf, bevor sie live gehen.
Das funktioniert, weil Astro statische HTML-Dateien erzeugt. Alles, was im Browser ankommt, liegt als Datei vor. Und alles, was als Datei vorliegt, lässt sich prüfen — ohne HTTP-Requests, ohne DOM-Rendering, ohne JavaScript-Ausführung.
Installation und Grundkonfiguration
npm i -D @casoon/astro-post-audit
// astro.config.mjs
import postAudit from '@casoon/astro-post-audit';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://example.com',
integrations: [
sitemap(),
postAudit(),
],
});
Das reicht bereits. Wichtig ist die Reihenfolge: postAudit() sollte nach sitemap() stehen, damit die Sitemap-Datei schon existiert, wenn der Audit sie prüft. Beide Plugins nutzen Astros astro:build:done-Hook und werden in Array-Reihenfolge ausgeführt.
Ab jetzt läuft der Audit automatisch nach jedem astro build und prüft Canonical-Tags, interne Links, HTML-Grundlagen, Heading-Hierarchie, Accessibility-Basics und Sitemap-Konsistenz.
Presets: Schnellstart ohne Konfigurationsarbeit
Wer nicht jede Regel einzeln konfigurieren will, kann mit einem Preset starten:
// Alle Checks aktiv, strenge Bewertung
postAudit({ preset: 'strict' })
// Nur Core-SEO und Links — gut für bestehende Sites
postAudit({ preset: 'relaxed' })
strict aktiviert alles: Canonical Self-Reference, Fragment-Validierung, Orphan-Detection, Skip-Link, Open Graph, JSON-LD, Hreflang, Sitemap, robots.txt, Content Quality, Inline-Script-Warnungen. Dazu strict: true, womit Warnings zu Errors werden.
relaxed beschränkt sich auf die Kernprüfungen: SEO-Basics und Link-Checks. Fortgeschrittene Checks wie Fragment-Validierung, Heading-Gaps, Open Graph oder Structured Data sind deaktiviert. Kaputte Links sind nur Warnings.
Presets lassen sich mit individuellen Regeln überschreiben:
postAudit({
preset: 'strict',
rules: {
external_links: { enabled: true },
headings: { no_skip: false }, // eine Regel lockern
},
})
Praxisnahe Konfiguration
Für die meisten Projekte will man mehr als die Defaults, aber nicht unbedingt ein volles Preset. So sieht eine praxisnahe Konfiguration aus:
// astro.config.mjs
import postAudit from '@casoon/astro-post-audit';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://example.com',
integrations: [
sitemap(),
postAudit({
benchmark: true,
rules: {
filters: { exclude: ['404.html', 'drafts/**'] },
canonical: { self_reference: true },
headings: { no_skip: true },
html_basics: {
meta_description_required: true,
title_max_length: 100,
meta_description_max_length: 220,
},
opengraph: {
require_og_title: true,
require_og_description: true,
require_og_image: true,
},
a11y: { require_skip_link: true },
assets: { check_broken_assets: true, check_image_dimensions: true },
structured_data: { check_json_ld: true },
security: { check_target_blank: true },
content_quality: { detect_duplicate_titles: true },
links: { check_fragments: true },
sitemap: { require: true },
external_links: { enabled: true, timeout_ms: 5000 },
},
}),
],
});
Alles, was hier steht, ist optional — aber jede Zeile hat einen Grund. Die Konfiguration folgt dem Prinzip: alles läuft über rules, jede Prüfkategorie hat ihren eigenen Block. Mit benchmark: true zeigt der Audit zusätzlich eine Timing-Aufschlüsselung pro Check — nützlich, um langsame Prüfungen auf großen Sites zu identifizieren.
Die Konfiguration im Detail
Limits anpassen: Title und Meta-Description
Der Standard für title_max_length liegt bei 60 Zeichen — das ist konservativ. Google zeigt in der Praxis oft 70 bis 100 Zeichen an, je nach Pixelbreite. Wer Service-Seiten mit beschreibenden Titeln hat, wird mit 60 Zeichen ständig Warnungen bekommen.
html_basics: {
title_max_length: 100,
meta_description_max_length: 220,
}
220 Zeichen für die Meta-Description gibt Spielraum für aussagekräftige Beschreibungen, ohne dass jede zweite Seite eine Warnung wirft. Die Werte lassen sich jederzeit anpassen — das Tool gibt den Rahmen vor, nicht die Dogmen.
Canonical Self-Reference erzwingen
canonical: { self_reference: true }
Standardmäßig prüft astro-post-audit nur, ob ein Canonical-Tag vorhanden und absolut ist. Mit self_reference: true wird zusätzlich geprüft, ob das Canonical auf die eigene Seite zeigt — nicht auf eine andere URL. Klingt selbstverständlich, geht aber bei dynamisch generierten Seiten oder kopierten Templates gern schief.
Open Graph als Pflicht
opengraph: {
require_og_title: true,
require_og_description: true,
require_og_image: true,
}
Jede Seite, die in sozialen Netzwerken geteilt werden könnte, braucht Open-Graph-Tags. Ohne og:image zeigt LinkedIn ein leeres Vorschaubild, ohne og:title wird der HTML-Title genommen — oder etwas Schlimmeres. Mit dieser Regel fällt jede Seite ohne OG-Tags beim Build auf.
Heading-Hierarchie und Skip-Links
headings: { no_skip: true },
a11y: { require_skip_link: true },
no_skip meldet, wenn Heading-Ebenen übersprungen werden — etwa von <h2> direkt auf <h4>. Das ist einer der häufigsten Accessibility-Fehler auf Content-Seiten und schadet der Dokumentstruktur für Screenreader.
require_skip_link prüft, ob ein Skip-Navigation-Link vorhanden ist (<a href="#main-content">). Für Tastaturnutzer essenziell, wird aber bei reinen Maus-Tests nie bemerkt.
Fragment-Validierung
links: { check_fragments: true }
Interne Links wie /artikel/mein-post/#fazit werden nicht nur gegen die Zielseite geprüft, sondern auch gegen die tatsächlich vorhandene ID. Existiert #fazit nicht im Zieldokument, gibt es eine Warnung. Besonders wertvoll bei Blog-Artikeln, wo Überschriften sich ändern, aber Ankerlänks in älteren Artikeln stehen bleiben.
Dateien ausschließen
Nicht jede Seite muss alle Regeln bestehen. Die 404-Seite braucht kein Canonical, Draft-Seiten sollen den Build nicht blockieren:
rules: {
filters: {
exclude: ['404.html', 'drafts/**'],
},
}
Glob-Patterns, die gegen den relativen Pfad im dist/-Ordner matchen. So bleiben die Regeln streng für Produktionsseiten, ohne dass Sonderfälle den Build aufhalten.
Einzelne Regeln unterdrücken oder verschärfen
Manchmal passt eine Regel grundsätzlich, aber ein spezifischer Check ist im Projekt nicht relevant:
rules: {
severity: {
'html/title-too-long': 'off',
'a11y/generic-link-text': 'warning',
'canonical/not-self': 'error',
},
}
Jede Regel hat eine ID (die im Audit-Output in eckigen Klammern steht). Per severity lässt sich jede einzelne auf error, warning, info oder off setzen. So kann man etwa:
- Title-Längen-Warnungen komplett abschalten, wenn man eigene Grenzen hat
- Generischen Linktext von Error auf Warning herabstufen
- Self-Referencing-Canonicals von Warning auf Error hochstufen
Externe Links prüfen
Standardmäßig prüft astro-post-audit nur interne Links — ohne Netzwerkzugriff. Wer auch externe Links validieren will, kann das explizit aktivieren:
external_links: {
enabled: true,
timeout_ms: 5000,
max_concurrent: 10,
fail_on_broken: false,
block_domains: ['example.com'],
}
Das Tool schickt HEAD-Requests an alle externen URLs und meldet alles, was nicht mit 2xx antwortet. max_concurrent begrenzt die parallelen Requests, block_domains schließt bestimmte Domains aus. Mit fail_on_broken: true werden kaputte externe Links zu Errors statt Warnings — sinnvoll für Dokumentationsseiten, wo jeder Link funktionieren muss.
Neue dist-only Audits: I18n, Crawl Budget, Privacy und mehr
Seit Version 0.2.5 enthält astro-post-audit fünf neue Prüfkategorien, die gezielt die statischen HTML-Dateien im dist/-Ordner analysieren. Diese Audits sind bewusst heuristisch: Sie liefern Hinweise, keine absoluten Wahrheiten, und sollten über severity an das eigene Projekt angepasst werden.
i18n_audit— Prüft Konsistenz zwischen lokalisierten Routen,html[lang],hreflang-Tags und Canonicals. Findet Fälle, in denen eine Sprachvariante existiert, aber die Hreflang-Verlinkung fehlt oder die Lang-Attribute nicht zum Routenpräfix passen.crawl_budget— Erkennt URL-Varianten und Duplikat-Cluster, die das Crawl-Budget verwässern. Meldet außerdem Indexierbarkeits-Widersprüche — etwa Seiten, die pernoindexausgeschlossen sind, aber intern stark verlinkt werden.render_blocking— Findet synchrone Scripts im<head>und fehlendepreload/preconnect-Hints für kritische Ressourcen.privacy_security— Inventarisiert Third-Party-Domains, prüft auf fehlende Subresource Integrity (SRI) bei externen Scripts und Stylesheets, und bewertet die CSP-Bereitschaft der Seite.structured_data_graph— Prüft JSON-LD-Entitäten seitenübergreifend auf Konsistenz. Erkennt Konflikte bei@id, Typ-/Name-/URL-Widersprüche und fehlende interne Entity-URLs.
Alle fünf sind standardmäßig deaktiviert und werden einzeln per rules eingeschaltet:
rules: {
i18n_audit: { enabled: true },
crawl_budget: { enabled: true },
render_blocking: { enabled: true },
privacy_security: { enabled: true },
structured_data_graph: { enabled: true },
}
Production Rollout: Warn-first, dann Strict
Weil die neuen Audits heuristisch arbeiten, empfiehlt sich ein zweistufiges Rollout.
Schritt 1: Beobachten — Alle fünf aktivieren, aber als Warnings laufen lassen:
postAudit({
strict: false,
throwOnError: false,
output: 'audit-report.json',
rules: {
i18n_audit: { enabled: true },
crawl_budget: { enabled: true },
render_blocking: { enabled: true },
privacy_security: { enabled: true },
structured_data_graph: { enabled: true },
severity: {
'render-blocking/missing-style-preload': 'info',
'privacy-security/third-party-domains': 'info',
'crawl-budget/noindex-with-internal-demand': 'info',
},
},
})
Schritt 2: Verschärfen — Nach ein paar Builds, wenn die Findings bekannt sind und die wichtigsten gefixt wurden:
postAudit({
strict: true,
throwOnError: true,
maxErrors: 50,
rules: {
i18n_audit: { enabled: true },
crawl_budget: { enabled: true },
render_blocking: { enabled: true },
privacy_security: { enabled: true },
structured_data_graph: { enabled: true },
severity: {
'privacy-security/missing-sri-script': 'error',
'privacy-security/missing-sri-stylesheet': 'error',
'structured-data-graph/type-conflict': 'error',
'crawl-budget/redirect-target-missing': 'error',
},
},
})
So wird aus einem informativen Beobachtungstool schrittweise ein Quality Gate, das den Build blockiert, wenn echte Probleme vorliegen.
Was der Audit-Output zeigt
Ein typischer Durchlauf:
──▶ about/index.html
× error[canonical/not-self] Canonical URL does not match page URL
╰─▶ link[rel='canonical']
help: Set canonical href to the page's own URL
──▶ blog/index.html
⚠ warning[a11y/generic-link-text] Generic link text: "mehr"
╰─▶ a[href='/blog/post-1/']
help: Use descriptive link text instead of "mehr"
──▶ kontakt/index.html
⚠ warning[assets/img-dimensions] Image missing width/height: src='/logo.svg'
╰─▶ img[src='/logo.svg']
help: Add explicit width and height to prevent layout shift (CLS)
× 1 error, 2 warnings (42 files checked)
Jedes Finding enthält die Datei, die Regel-ID, einen Selektor-Hint und einen konkreten Fix-Vorschlag.
Was in der Praxis überrascht: wie viele Fehler sich unbemerkt einschleichen. Ein kopiertes Template ohne angepasstes Canonical, ein Bild-Refactoring, bei dem width/height verloren gehen, ein Linktext, der seit drei Iterationen nur noch “mehr” sagt. Dinge, die einzeln harmlos wirken, aber in der Summe die Qualität einer Site schleichend unterwandern.
Der Output ist bewusst so gestaltet, dass er direkt umsetzbar ist. Datei, Selektor, Fix-Vorschlag — das lässt sich in wenigen Minuten abarbeiten. Wer mit KI-gestützten Coding-Tools arbeitet, kann den Audit-Output direkt übergeben: Die Findings sind strukturiert genug, dass ein Tool wie Claude Code sie ohne Rückfragen versteht und die Fixes selbstständig umsetzt. Bequemer geht es nicht.
Mit output: 'audit-report.json' lässt sich der Report zusätzlich als maschinenlesbare JSON-Datei speichern — praktisch für CI-Dashboards oder eigene Auswertungen.
Build-Verhalten: Warnen oder abbrechen
Standardmäßig warnt astro-post-audit nur — der Build läuft durch. Für Produktions-Pipelines, die keine kaputten Links oder fehlenden Alt-Texte tolerieren:
postAudit({
strict: true, // Warnings werden zu Errors
throwOnError: true, // Build bricht bei Errors ab
maxErrors: 20, // Nach 20 Errors stoppen
})
Mit throwOnError: true wirft das Plugin einen AstroError. Der Build schlägt fehl, der Deploy wird verhindert. maxErrors begrenzt die Ausgabe auf die ersten N Fehler — bei großen Sites mit vielen Findings hilfreich, um nicht den gesamten Build-Log zu fluten. Empfehlenswert für CI/CD-Pipelines, wo Qualitätsprobleme nicht erst im Review auffallen sollten.
Audit überspringen
Nicht jeder Build braucht den vollen Audit. Mit der Umgebungsvariable SKIP_AUDIT lässt sich der Audit für einen einzelnen Build abschalten:
SKIP_AUDIT=1 astro build
Praktisch für schnelle Dev-Builds, wenn etwa die externe Link-Prüfung den Build verlangsamen würde. Wer den Audit dauerhaft deaktivieren will, kann das auch per Config:
postAudit({ disable: true })
Warum so schnell?
Unter der Haube arbeitet ein kompiliertes Rust-Binary. HTML-Parsing über Hunderte Dateien mit paralleler Verarbeitung — das erledigt Rust in Bruchteilen von Sekunden. Auf einer Site mit 50 Seiten liegt die Laufzeit unter 500 Millisekunden, bei mehreren hundert Seiten unter zwei Sekunden. Schnell genug, um bei jedem Build mitzulaufen, ohne den Entwicklungsfluss zu bremsen.
Einordnung: Was das Tool leistet — und was nicht
astro-post-audit ersetzt keinen vollständigen WCAG-Audit. Es prüft keine Farbkontraste, keine Keyboard-Navigation, kein Screen-Reader-Verhalten. Was es tut: die häufigsten Fehler finden, die bei statischen HTML-Dateien erkennbar sind — fehlende Alt-Texte, kaputte Links, strukturelle SEO-Probleme, ungültige JSON-LD-Blöcke, Privacy-Risiken durch Third-Party-Scripts, Crawl-Budget-Verschwendung.
Die fünf neuen dist-only Audits erweitern den Radius: I18n-Konsistenz, Render-Blocking-Analyse, SRI-Prüfung und seitenübergreifende Structured-Data-Validierung — alles Dinge, die man sonst erst bei einem manuellen Review oder mit teuren externen Tools findet.
Das deckt nicht alles ab — aber die häufigsten Probleme landen so direkt im Build-Output, nicht erst nach dem Deploy.
astro-post-audit vs. auditmysite
Für vollständige Accessibility-Audits gegen laufende Websites entsteht parallel auditmysite. Das Tool wird aktuell grundlegend überarbeitet: Eine neue Rust-Version ersetzt den bisherigen Stand und wird künftig WCAG-2.1-Prüfungen über das Chrome DevTools Protocol liefern — inklusive Contrast Checks und Accessibility-Tree-Analyse. Die beiden Tools haben unterschiedliche Ansätze und ergänzen sich:
| astro-post-audit | auditmysite | |
|---|---|---|
| Arbeitet auf | Statische HTML-Dateien (dist/) | Laufende Website (Browser) |
| Technologie | Rust-Binary, HTML-Parsing | Rust + Chrome DevTools Protocol |
| SEO-Checks | Canonical, Meta, OG, Sitemap, Links, Crawl Budget | — |
| Structured Data | JSON-LD-Validierung + seitenübergreifende Graph-Analyse | — |
| I18n | Hreflang, lang-Konsistenz, Locale-Routen | — |
| Privacy/Security | Third-Party-Inventory, SRI, CSP-Readiness | — |
| A11y-Heuristiken | Alt-Texte, Headings, Skip-Links, Labels | Vollständiger Accessibility Tree |
| Farbkontraste | Nein (keine CSS-Berechnung) | Ja (Computed Styles via CDP) |
| Keyboard-Navigation | Nein | Ja (Fokus-Simulation) |
| WCAG-Abdeckung | Teilweise (strukturelle Checks) | WCAG 2.1 (AA/AAA) |
| Laufzeit | Unter 1 Sekunde | Sekundenbereich pro Seite |
| Integration | Astro-Build-Hook | CLI |
astro-post-audit fängt Fehler im Build ab, bevor sie live gehen. auditmysite prüft das Ergebnis im echten Browser — mit allem, was erst zur Laufzeit sichtbar wird: berechnete Kontraste, Fokus-Reihenfolge, Accessibility-Tree-Struktur. Zusammen decken sie den Audit-Zyklus ab — vom Build bis zum Live-Test.
Quellen
- astro-post-audit auf GitHub — Source Code, Issues, Releases
- astro-post-audit auf npm — Installation und Versionshistorie
- auditmysite auf GitHub — WCAG-2.1-Auditor (in Überarbeitung)
- Astro Integration API — Astro-Hooks für Build-Integrationen
- post-audit Landingpage — Übersicht, Features und Installationsanleitung