A couple of weeks ago I built a Claude Code skill that measures why a page is slow (SpeedKit). While I was auditing all those "roast my site" pages, I kept noticing something: the SEO problems were as invisible as the speed ones — and just as mechanical to check.
Not keyword strategy. Not backlinks. The boring, technical stuff that quietly keeps a page out of search or makes it look broken in the results:
- an accidental
noindexin the robots meta — the page is literally telling Google to drop it, and nobody notices - a canonical pointing at a different URL than the one you want ranked
- a 195-character meta description that gets truncated with an ellipsis in the SERP
- a title cut off at 60 chars, or zero/multiple H1s
- missing Open Graph tags, so the link renders bare and unclickable-looking when shared on Slack/X/LinkedIn
- JSON-LD structured data that's present but malformed, so it does nothing
Every one of these is readable straight from the page's HTML. You don't need a crawler subscription or an API key — you need to actually look. Which is exactly the kind of mechanical, every-time checklist Claude Code is good at.
So I made it read the page
onpage-seo-audit is a SKILL.md plus a seo-measure.js that runs in the page context and reads: the title and its length, the meta description length, H1 count and heading order, the canonical (and whether it points at the current URL), the robots meta, Open Graph + Twitter cards, JSON-LD blocks and their @type, image alt coverage, lang/viewport/charset, internal-vs-external links, body word count, and it fetches /robots.txt and /sitemap.xml to check crawlability.
It returns a ranked report — indexability blockers first — where every finding is a real value read from the page, not a guess.
Here's the shape of the canonical check, which is my favorite because it catches a genuinely sneaky bug:
const canonicalHref = document.querySelector('link[rel="canonical"]')?.href;
const self = canonicalHref
? canonicalHref.replace(/\/$/, '').split('#')[0] === location.href.replace(/\/$/, '').split('#')[0]
: null;
// self === false → the page says the "real" version is a *different* URL.
// Sometimes that's correct (www → non-www consolidation). Sometimes you've
// just told Google to de-index the page you're looking at. Worth a human look.
The part that mattered most: keeping it honest
The whole thing only works if the model reports what it measured and nothing else. The SKILL.md is explicit about it: report the real character count, the real flag, the real ratio — never invent a metric you didn't read. And when a "problem" is plausibly intentional (a canonical consolidating www → non-www, a deliberately short landing page), say "verify this" rather than "this is broken."
It also refuses to promise rankings. On-page SEO removes the technical reasons a page can't be indexed or looks wrong in search. That's the honest scope — no keyword-density myths, no "submit to 500 directories."
Try the pattern on your own site
Open DevTools → Console on any page and check the list at the top by hand — you'll probably find at least one. That's most of the value right there.
If you'd rather not remember to, I packaged it (audit + a fix skill that applies the corrections and re-reads the page) as SEO Audit for Claude Code ($19): https://noguchilin.gumroad.com/l/gywwrj
It's the sibling of the speed one — SpeedKit reads why a page is slow, this one reads why it can't be found. Two halves of the same "is this site technically healthy" question.
Happy to run the audit on your page if you drop a URL in the comments.
Top comments (0)