pagetrace records your site's SEO and AEO surface as a committed lockfile, then diffs every build against it. Plenty of tools score a page's current state. This one tells you what this deploy changed — and fails the build over it.
(site-wide) error GPTBot is now blocked. aeo.crawler.newly_blocked /pricing error Canonical was removed. canonical.removed error Page became noindex. robots.noindex.added warn Links to /plans-old. link.broken.added /blog/launch error Article entity removed. jsonld.entity.removed Failing: 4 error, 1 warning. exit 1
Why a diff
Every auditor answers "how is this page doing?" — a score, out of context, on a day you happened to look. That question cannot see the thing that actually hurts: a template edit that silently changed 400 pages between Tuesday and Wednesday.
True yesterday, true today, true after the canonical disappeared — because the score was never compared against anything. You find out from the traffic graph, six weeks later.
Severity comes from the transition, not the state. A reworded title is info. A removed
canonical is error, and the build stops. Routine content churn stays quiet, which is the
only reason anyone leaves the gate switched on.
Playground · no install
This is not a demo of the tool — it is the tool. extract, audit and
diff are pure functions with no I/O, so the exact code that ships on npm is compiled into
this page. Nothing you paste leaves your browser.
Browsers cannot read another site directly, so this route goes through a public CORS proxy — your URL is visible to that service. Paste the HTML instead for anything private.
What the browser cannot do, the CLI does: dead internal and external links, redirect drift, sitemap
health, duplicate titles across a site, hreflang reciprocity. Those need a crawl —
npx pagetrace audit --url https://your-site.com.
The CLI
Every crawling command takes --dir <build> or --url <origin>. Exit
codes are the same everywhere: 0 clean, 1 findings at or above
--fail-on, 2 the run itself broke — so CI can tell a regression from an outage.
| Command | Answers | Crawls |
|---|---|---|
init | Get me set up | Once, to write the first lockfile |
snapshot | Record what the site looks like now | Whole site |
check | What did this deploy change? | Whole site |
audit | What is wrong with this site? | Whole site |
links | Are any links dead? | Whole site |
page <url> | Is this one page sound? | That URL alone |
update | Am I on the latest pagetrace? | Nothing |
Useful flags: --external checks outbound links, --verify-all adds assets like
PDFs, --ignore-robots crawls a staging origin that disallows everything, and
--format takes pretty, json, markdown,
github or sarif.
Finding codes
Codes are the public contract — integrations key on canonical.removed, never on the prose.
A selection; the full set covers hreflang, AEO signals, structured data and llms.txt.
| Code | Severity | Fires when |
|---|---|---|
canonical.removed | error | A page lost its canonical tag |
robots.noindex.added | error | A page became noindex |
jsonld.entity.removed | error | A structured data entity disappeared |
jsonld.property.removed | error | An entity lost a property it used to have |
content.dropped | error | Word count fell by more than half — usually a render failure |
link.broken.added | error | A page started linking to a URL that does not exist |
sitemap.dead | error | The sitemap lists a URL that answers 404 |
aeo.crawler.newly_blocked | error | robots.txt started blocking an AI crawler |
redirect.added | warn | A route that used to answer directly now redirects |
canonical.redirects | warn | A canonical points at a URL that redirects |
link.external.dead | warn | An outbound link answers 404 |
title.changed | info | Ordinary copy edit — never fails a build |
Continuous integration
The GitHub Action diffs each pull request against the lockfile committed on your default branch and leaves the result as a comment, updating that same comment on every push instead of stacking new ones. Feature branches carry no lockfile, so there is no churn to review.
- uses: actions/checkout@v5
- run: npm ci && npm run build
- uses: shyamexe/pagetrace@v1
with:
dir: ./out
baseline-branch: main
Prefer the Security tab? --format sarif piped into
github/codeql-action/upload-sarif turns findings into pull request annotations that outlive
any comment.