Skip to content

How to Run a Technical SEO Audit With Claude Code

Knowing what to check is the easy half. This is the execution: writing checks that are capable of failing, four false passes from this site and what caused each, and what the submission layer actually does.

Claude Code Guides: How to Run a Technical SEO Audit With Claude Code. A column of checks where one has failed and one is unresolved.

Knowing what to check is the easy half

There is no shortage of technical SEO checklists, including one on this site. The list is not the hard part. The hard part is running it in a way that produces evidence rather than reassurance — and the failure mode is specific enough to name: checks that report success on exactly the condition they exist to catch.

This article is about the execution. If you want the reasoning behind what to audit, in dependency order across thirteen areas, that is the SEO workflow guide. This one assumes you already know what to look at and covers how to look, how to prove you looked, and what to do with the result.

The rule: a check you have not seen fail is not a check

Four outcomes of a check. A pass where the check ran and the condition held. A pass where the check ran against the wrong thing. A pass where the check never ran at all. And a fail where the check ran and the condition broke. The middle two are indistinguishable from the first, and only the fail proves the check works.
Three of these produce the same output. Only the fourth proves anything about the check itself.

Take any check that just passed and ask what else would produce that same output. Usually: the check ran correctly; the check ran against the wrong page, file, or attribute; the check never executed. All three print the same green line.

The remedy is a minute of work per check. Break the thing on purpose and confirm the check goes red. Delete a canonical tag from a local copy. Point one internal link at a URL that does not exist. Rename the file the script reads. If the output does not change, you have learned something far more valuable than a passing result.

Do this the moment you write the check, while you still remember what it was supposed to catch. Every example in this article is a check that was written, passed, and turned out to be incapable of failing.

The crawler, and the partial-run trap

Start here, because almost everything downstream needs the URL inventory.

Write a crawler for this site that starts at the homepage, follows
only same-host links, and records per URL: status, final URL after
redirects, title, meta description, canonical, h1 count, word count.

Hard requirements:
- Pause between requests; honour Retry-After on 429.
- Retry with exponential backoff, up to a minute.
- Separate "throttled" failures from real ones in the summary.
- If any URL could not be fetched, print INCOMPLETE and exit non-zero.
- Never summarise a partial crawl as a clean one.

Those last two lines exist because of a specific failure. An earlier version of this crawler reported zero broken links across a run in which nine of twenty-eight seed URLs had returned 429. It had retried four times over about fourteen seconds, given up, and treated an unreachable page as nothing to report.

After the fix — six attempts backing off to sixty seconds, honouring Retry-After, and a distinct exit code for a partial run — the same crawl reached 28 pages and found 44 URLs where the throttled run had found 25. The link report had not been wrong so much as based on two-thirds of the site.

Sort the resulting table three ways before doing anything else: by title (duplicates), by description (duplicates and blanks), and by status (anything not 200 or an intended redirect). On this site, sorting by title is what surfaced eight blog tag archives all sharing the blog index's exact title tag.

Rendered HTML beats template source

An assistant with repository access will happily audit your templates. Templates describe intentions. Search engines read responses.

Fetch the live URL for one page of each template type. From the
response body only — not from any template file — report:
title, meta description, canonical href, any robots meta or
X-Robots-Tag header, count of JSON-LD blocks, and whether each
JSON-LD block parses as valid JSON.

If a value comes from a file rather than a response, do not report it.

Categories that only exist in the response:

  • Platform fallbacks. A homepage whose <title> silently becomes the raw platform domain when a setting is empty.
  • Injected directives. A noindex added by a plugin, a header, or a platform preference that appears nowhere in the theme.
  • Double-escaping. A title captured with an HTML entity in it, then escaped again for og:title, printing &amp;ndash; into the tag. Correct in the template, wrong in the output.
  • Request-time canonicals. The canonical for ?variant= or ?utm_source= is decided at request time, so it can only be measured by requesting it.

Validating structured data on the live URL

Two separate questions, and people usually only ask the second.

Does it parse? Extract every application/ld+json block from the rendered HTML and run it through a JSON parser. A trailing comma from a Liquid or JSX conditional is invisible to the eye and fatal to the markup. This is the check most often skipped, and the cheapest to run.

Does it describe the page honestly? Harder, and not automatable. Compare each property against what a visitor sees: the price in the markup against the price on the page, the author in the markup against the byline, the breadcrumb list against the visible trail.

Two things worth being firm about:

  • Never add review, rating, or aggregate markup without real reviews. It is a guidelines violation and a manual-action risk, and an assistant asked to "improve the structured data" will suggest it, because the pattern is common in its training data. Say no.
  • Do not invent a value to satisfy a validator. A missing recommended property is a warning. A fabricated one is a lie in machine-readable form. If a return policy is decided case by case, no schema.org return category describes it, and the correct action is to omit the property and write down why.

Four false passes, and what caused each

All four are from auditing this store. The root cause is the same every time: the failure path was never exercised.

What it reported What was true Cause
0 broken links Nine of 28 pages never fetched 429s counted as nothing to report
Checkout healthy Store could not take payments Notice injected by JavaScript, absent from initial HTML
Checkout accepts email Never actually checked Read innerText; the wording is in a placeholder
Page has a Liquid error Page was fine Regex matched prose about Liquid errors in an article

Three of the four were false negatives — a real problem reported as fine. The fourth was a false positive, which is less dangerous but erodes trust in the whole report until someone stops reading it.

The corrective habit that came out of this: control-test in both directions. Feed the check a case that should pass and one that should fail, and confirm it says so. For the Liquid detector that meant one page with a real Liquid error and one containing the phrase in prose, and tightening the pattern until the two were distinguished.

Turning findings into a diff

Audit and repair are separate phases, and mixing them loses the record of what was wrong. Once the findings are written down, work through them one at a time:

Finding: /blogs/guides/tagged/* returns eight pages that all share
the blog index's title and meta description.

Before changing anything:
1. Show me the template code that produces the title on those URLs.
2. Tell me which condition distinguishes a tag archive from the index.
3. Propose the smallest change, and say what it does to the index page.
Then make the change, and show me the rendered title of both a tag
page and the index afterwards.

Step three prevents the classic regression where fixing the archive breaks the page it was copied from. The final instruction is the important one: it forces the assistant to demonstrate both branches of the condition it just wrote, which is the same control-test discipline applied to a fix rather than a check.

Submission: Search Console, Bing, IndexNow

The audit ends by telling the search engines what changed. Three systems, and they behave differently enough to be worth stating plainly.

Google Search Console. Verify the property for the exact production hostname, then submit the sitemap. Submission is a hint, not an instruction — Google decides what to crawl and when, and a submitted sitemap can sit unfetched for days. It also ignores <priority> and <changefreq> entirely, so neither is worth tuning. What is worth watching is the coverage report: "Discovered — currently not indexed" is a quality signal, not a technical fault, and no amount of resubmission changes it.

Bing Webmaster Tools. Faster and more literal. Submitted feeds get crawled quickly, and URL submission is quota-limited, which gives you a clean way to confirm a call actually did something: check the quota before and after and see it decrement. That is a real verification rather than a 200 response you hope meant something.

IndexNow. Used by Bing, Yandex, Seznam and Naver. Google does not use it. It requires a key file served as plain text from the site root, which is awkward on a hosted platform that does not let you write arbitrary root files — on Shopify the working route is to upload the key to Files and map the root path to it with a URL redirect. Whether IndexNow accepts a key behind a cross-host redirect is the kind of thing worth measuring rather than assuming: it does, and the submission returns 202 (validation pending) before settling to 200 (key accepted).

One discipline for all three: submit changed and new URLs only. A job that resubmits every URL on a timer wastes quota, tells search engines nothing, and on IndexNow specifically is exactly the behaviour the protocol asks you not to exhibit.

Making it run on every deploy

An audit run once is a snapshot. The value is in the second run, when it catches a regression somebody introduced without noticing.

The subset worth putting in CI is small and fast:

  • Crawl every URL; fail on any 404, 500, redirect chain, or incomplete run
  • Fail on a duplicate title or duplicate meta description
  • Fail on a missing or non-self-referencing canonical
  • Fail on any JSON-LD block that does not parse
  • Fail on an unexpected noindex

Everything else — content quality, cannibalisation, Core Web Vitals field data — is judgement or needs traffic, and belongs in a monthly review rather than a build gate. Running Claude Code in GitHub Actions covers the mechanics; the rule that the check must be able to fail applies to the CI job too, so break something once and watch the build go red.

What not to automate

Three things stay manual, and pretending otherwise is where automated SEO reports go wrong.

Anything requiring external data. Search volume, keyword difficulty, competitor backlinks. An assistant with no data source that produces a specific number has invented it. Get these from Search Console, Bing Webmaster Tools, or a keyword API — and if the number did not come from a request, do not use it.

Whether the content is any good. A script can tell you a page has a unique title and valid markup. It cannot tell you the page is worth ranking. That judgement is yours.

Intent overlap. Deciding that two pages compete for one query, and which one should win, needs someone who understands why both were written. A tool can flag the similarity; it should not resolve it.

Where to go next

For what to check and why, in dependency order, read the Claude Code SEO workflow. For auditing a whole site rather than its SEO layer, the website audit guide covers the four-pass method this borrows from. On a hosted storefront, Shopify SEO with Claude Code covers what the platform already handles. The checklists themselves are free: SEO and website audit. Rules about never inventing metrics belong in CLAUDE.md.

Sources and further reading

More Claude Code guides

Free download

The CLAUDE.md Starter Kit, free

Four working CLAUDE.md files you can drop into a project today, plus the one-page checklist for what belongs in one and how to tell whether yours is actually working.

  • CLAUDE.md for a static marketing site
  • CLAUDE.md for a web application, with security and migration rules
  • CLAUDE.md for a Shopify theme, including the gotchas that cost hours
  • CLAUDE.md for a shared package in a monorepo
  • A one-page checklist, and how to test the file is actually working

The download appears here as soon as you submit. I will also email you when there is a new guide worth reading. No fixed schedule, no selling your address, unsubscribe from any email. See the privacy policy.