/seo-check · SEO checker skill for Claude Code · MIT · zero dependencies
Most SEO disasters ship as innocent‑looking diffs.
/seo-check is an open‑source SEO checker skill for Claude Code. It reads your uncommitted changes for SEO regressions, then crawls the routes they touch the way Googlebot does — and gives you one verdict before you commit.
/plugin marketplace add AnTIdoTe003/claude-seo-check
/plugin install seo-check@claude-seo-check
noindex, nofollow on / and /tables. This is the root layout: every route inherits it.crawl · googlebot-smartphone · http://localhost:3000 · 2 routes affected by this layout
Fix before committing — 1 blocker
app/layout.tsx:11 — robots: { index: false } added to the root layout. Crawler confirms NOINDEX_META on 2 of 2 routes. This deindexes the whole site.
Fix: remove it, or gate it on process.env.VERCEL_ENV !== 'production'.
VERIFIED CLEAN title, description, canonical, metadataBase, structured data.
@@ what an innocent diff can do @@
Seven one‑line changes that cost rankings
Each one looks harmless in code review. The checklist the audit reads ranks them by what they actually cost: a blocker deindexes pages, blocks crawling, 404s live URLs or destroys structured data; a warning is a likely loss.
+robots: { index: false },app/layout.tsx · left over from stagingDeindexes every route under the layout. The #1 accidental SEO kill. Often gated on an env check that no longer matches.
Blocker+Disallow: /public/robots.txt · app/robots.tsTells every crawler to skip the whole site. A Disallow: meant for one path matches by prefix, so /guide also blocks /guides/*.
−app/guides/[slug]/page.tsxdeleted · no redirect addedEvery URL under it starts returning 404. Rankings and backlinks go with it unless a 301/308 lands in next.config, vercel.json or middleware.
+"aggregateRating": { "ratingValue": 4.8 },trailing comma before the closing braceInvalid JSON-LD silently drops every rich result. Stars, FAQs, breadcrumbs — gone, with nothing in the build log.
Blocker−alternates: { canonical: '/pricing' },or pointed at a different pageGoogle may index some other URL instead. Removed canonicals let duplicates compete; wrong ones hand the ranking to the wrong page.
Warning → blocker+'use client'top of app/blog/[slug]/page.tsxThe article text leaves the initial HTML. Google renders JS eventually; Bing, AI crawlers and link previews never see it.
Warning permanent: true → falsenext.config redirects()308 becomes 307. Temporary redirects stop consolidating link equity into the new URL.
Warning@@ how it works @@
Two passes, then a verdict
The audit runs inside Claude Code against whatever is uncommitted. It reports; it doesn't change your code unless you ask.
1 static audit
Read the diff the way an SEO would
Scopes git diff to SEO‑relevant files — pages, layouts, metadata, robots, sitemaps, next.config, vercel.json, middleware, redirects — and maps each changed file to a URL route. Then greps the diff against the regression checklist and reads the full context of every hit, so a noindex behind a staging guard isn't a false alarm.
2 live crawl
Fetch the routes as Googlebot
Requests every affected route from your dev server with a Googlebot‑smartphone user agent and records exactly what a search engine receives from the initial HTML. Pass a production URL and it fetches each route on both origins and diffs every signal — the most direct evidence of “did my change break anything”.
3 verdict
One line first, evidence after
Blockers come with file:line, the crawler flag, and a concrete fix. Pre‑existing issues are listed separately from regressions. Checks it couldn't run are named, never omitted, so a clean result is credible.
@@ what the crawler reports @@
Everything a search engine receives
Per route, from the initial HTML response — the part indexing depends on most, and the only part non‑Google crawlers ever see.
- Redirect chains, final status,
X‑Robots‑Tag, meta refreshREDIRECT_CHAIN · REDIRECT_LOOP
NOINDEX_HEADER · META_REFRESH - robots.txt, evaluated with Google's longest‑match rulesROBOTS_BLOCKED
- Meta robots — index / follow directivesNOINDEX_META · NOFOLLOW_META
- Title and description — presence and lengthNO_TITLE · TITLE_LONG
NO_DESCRIPTION · DESC_SHORT - Canonical — present, self‑referencing, on the right hostNO_CANONICAL
CANONICAL_CROSS_HOST - Heading outline — h1 count and skipped levelsoutline
- Word count of the initial HTML — catches content moving behind client‑side JSTHIN_INITIAL_HTML
- Open Graph and Twitter cardsNO_OG · OG_NO_IMAGE
- hreflang,
html lang, viewportNO_HTML_LANG · NO_VIEWPORT - JSON‑LD — extracted, parsed,
@typeinventoryJSONLD_INVALID - Images without alt — decorative
alt=""counted separatelyIMG_MISSING_ALT - Links — internal, external and nofollow countsNOFOLLOW_INTERNAL
- Sitemap membershipNOT_IN_SITEMAP
- Local vs production — every signal above, diffed--compare
@@ install @@
Install in two lines
Needs Node ≥ 18 on your PATH for the crawler. Works with Next.js (app and pages router), Astro and plain HTML sites out of the box.
Pluginrecommended · inside Claude Code
/plugin marketplace add AnTIdoTe003/claude-seo-check
/plugin install seo-check@claude-seo-check
Manualpersonal skill · available in every project
git clone https://github.com/AnTIdoTe003/claude-seo-check.git
ln -s "$(pwd)/claude-seo-check/skills/seo-check" ~/.claude/skills/seo-check
- /seo-check
- audit whatever is uncommitted
- /seo-check https://your-site.com
- …and diff every route against production
- /seo-check /pricing /blog/some-post
- force specific routes into the audit
- /seo-check --staged
- only staged changes
- /seo-check --branch main
- diff against the merge‑base with main
- /seo-check --full
- also crawl the homepage and key sitemap routes
It auto‑detects your dev server on common ports, or starts and stops one itself.
@@ crawler, standalone @@
The crawler works without Claude
crawl.mjs is one zero‑dependency Node script. Point it at any URL, or wire it into a git hook or CI with its exit codes.
# audit three routes on your dev server
node skills/seo-check/scripts/crawl.mjs --base http://localhost:3000 / /about /blog/my-post
# diff local against production
node skills/seo-check/scripts/crawl.mjs --base http://localhost:3000 --compare https://prod.com /
# any public URL, machine-readable
node skills/seo-check/scripts/crawl.mjs https://example.com/page --json
- --base <origin>
- origin that bare route paths resolve against
- --compare <origin>
- fetch each route on a second origin and diff all signals
- --ua <name>
googlebot-mobile(default),googlebot-desktop,chrome, or any string- --timeout <ms>
- per‑request timeout, default 20000
- --no-robots
- skip robots.txt and sitemap checks
- --json
- machine‑readable output
# .husky/pre-commit — block the commit on blockers
node skills/seo-check/scripts/crawl.mjs \
--base http://localhost:3000 / /pricing \
|| { echo "seo-check: fix blockers before committing"; exit 1; }
@@ limits, stated plainly @@
What it doesn't do
- No JavaScript execution. The crawler reads the initial HTML only — deliberately. That's what indexing depends on most and what every non‑Google crawler sees. A page whose initial HTML is an empty shell gets
THIN_INITIAL_HTML; that's a real finding, not a tooling gap. - Tolerant parsing. HTML is parsed with regex tuned for real‑world markup, not a spec‑complete parser.
- Sampled sitemaps. Checks cap at 12 sitemap fetches and 5,000 URLs per sitemap; very large sites are sampled, not exhaustively verified.
- The static pass is Claude reading your diff against a checklist — so it benefits from context a grep can't judge (frameworks, conditionals, env guards), and it needs Claude Code to run.
@@ questions @@
Questions people ask
What is seo-check?
seo-check is an open-source SEO checker skill for Claude Code, installable as a plugin. Run /seo-check and it audits the files you've changed but not yet committed for SEO regressions, then crawls the affected routes with a Googlebot user agent and returns a commit verdict: safe, caution, or fix first.
How is it different from a normal SEO audit tool?
Most SEO tools audit a live site after the damage is done. seo-check runs on the diff, before the commit, so a stray noindex, a broadened Disallow:, a deleted route or a broken JSON-LD block is caught before it reaches production. With a production URL it also diffs every signal between local and live, so you see exactly what your change altered.
Which frameworks does it support?
Next.js (app and pages router), Astro and plain HTML sites out of the box — it maps changed files to URL routes for those. The crawler itself works on any URL, whatever the stack.
Does it send my code anywhere or need an API key?
No API key. The crawler is a zero-dependency Node script that only fetches your own URLs. The static pass runs inside your existing Claude Code session, so your diff goes only where your Claude Code conversations already go.
Can I use it without Claude Code?
Yes. crawl.mjs runs standalone and exits 0, 1 or 2, so it drops straight into a git pre-commit hook or a CI step.
Is it free?
MIT-licensed, free, no sign-up. Install it from the repository's marketplace in two lines, or star and fork it on GitHub.