An audit is not a launch checklist
A launch checklist asks is this ready to ship? An audit asks what is wrong with what already shipped, and which of it matters? The second question is harder, because the site in front of you looks finished. Somebody signed it off. Everything that is broken is broken quietly.
The distinction changes the method. A launch checklist is worked through in order, because the work is sequenced. An audit is an inspection: you gather evidence first and prioritise afterwards, and the order you inspect in is chosen to stop you from being told what to think.
This guide is the method. The list of things to look at is a separate document — the website audit checklist — which is free, ungated, and designed to be handed to Claude Code section by section. Read this for how to run an audit; open that for what to check.
The order that matters
Four passes, and the order is the single most useful thing in this article.
Most audits are run in exactly the wrong order: open the repository, read the components, form a mental model, then check the site against it. By the time you look at the live site you are no longer able to see it. You see your model of it, and your model is built from source that describes intentions.
Claude Code has the same failure mode, more strongly. Give it the repository first and it will describe an excellent site. Give it the responses first and it will describe the one that exists.
Setting Claude up to audit, not to fix
The default behaviour of a coding assistant is to fix things. During an audit that is actively harmful: findings disappear into commits before anyone has decided whether they matter, and the record of what was wrong is lost.
Say so explicitly at the start of the session:
We are auditing this site, not fixing it. For this session:
- Do not edit any file.
- Do not commit, push, or open a pull request.
- Record findings as: URL, what you observed, how you observed it,
why it matters, severity.
- If you cannot verify something, say so and mark it unverified.
Do not infer it from the source.
That last rule does most of the work. Without it you get findings phrased as "the canonical tag appears to be self-referencing", which means the template looked right. With it you get "fetched https://example.com/x, canonical is https://example.com/x" or an explicit gap.
If the audit is something you run regularly, this belongs in a skill rather than being retyped, and the "do not edit" half belongs in a hook, where it is enforced rather than requested.
Pass 1: crawl and record
Start with a table. One row per URL, columns for status code, final URL after redirects, title, meta description, canonical, H1 count, and word count. Nothing clever — the point is coverage, not analysis.
Write a script that crawls this site starting from the homepage,
following only internal links, and records for each URL:
status, final URL, title, meta description, canonical, h1 count.
Requirements:
- Pause between requests. Honour Retry-After.
- If any request fails after retries, print an INCOMPLETE banner
and exit non-zero. Do not summarise a partial crawl as clean.
- Write the results to CSV.
The failure requirement is not decoration. A crawler that hits rate limiting, silently drops nine of twenty-eight pages, and then reports "0 broken links" has told you nothing while sounding authoritative. That happened on this site. The fix was longer backoff and a distinct exit code for a partial run; the crawl then found nineteen more URLs than the throttled one had.
What falls straight out of the table:
- Duplicate titles and descriptions — sort the column and look for repeats. This is where the most common real finding lives.
- Missing descriptions — blank cells.
- Broken links — anything that is not 200 or an intended redirect.
- Redirect chains — where the final URL is two or more hops away.
- Thin pages — word counts far below the rest.
- Orphans — anything in the sitemap that the crawl never reached.
Pass 2: rendered HTML, not templates
Fetch one live page per template type — home, product, article, index, a policy page, the 404 — and read what actually came back.
Things that are only visible here:
| Check | Why the template will not tell you |
|---|---|
noindex in the response |
Often injected by a plugin, a platform setting, or a header |
| Structured data validity | Liquid or JSX that looks fine can emit malformed JSON |
| Entity leakage in meta tags | Double-escaping only appears in the output |
| Canonical target | Depends on request-time values, not the template |
The real <title>
|
Platform fallbacks fire when a setting is empty |
That last row is worth an example. This store shipped with its homepage <title> set to the raw .myshopify.com domain, because the theme fell back to it when a setting was never filled in. In a browser tab it looked entirely normal. It was visible only by reading the response.
Validate every JSON-LD block by parsing it, not by looking at it. A block with a trailing comma is invisible to the eye and worthless to a search engine.
Pass 3: use the site
Open the site on a phone, on mobile data, and try to do the thing it wants you to do. Buy something. Send the form. Find the pricing.
This is the pass people skip, and it is the one that catches the expensive problems, because the expensive problems are usually not in the HTML at all. Some categories that only appear here:
- A call-to-action that is present, valid, and invisible — text at low contrast against its own background
- A button that links to a fragment which only exists on another page, so clicking it does nothing at all
- A payment provider that is configured but not activated, where the failure notice is injected by JavaScript and absent from the initial HTML
- A form that submits successfully and delivers nothing
- A sticky bar that covers the primary action at 320 pixels
Claude Code can help here with a headless browser, but do not let it replace the human pass. A script checks the conditions you thought of. A person notices the thing nobody thought of.
Pass 4: read the code
Now, with a table of URLs, a set of rendered responses, and a list of things that felt wrong, the source becomes useful. You are not looking for problems any more; you are looking for the cause of problems you have already observed.
Here is the finding: on /pages/about the primary CTA renders with
text and background at the same colour.
Find the cause in the stylesheet. Show me the two rules involved and
which one wins, with the specificity of each. Do not change anything.
Asking for the mechanism rather than the fix is what keeps the audit an audit. It also produces a better fix later, because "a generic link rule outranks the button rule by one point of specificity" tells you something that "changed the colour" does not.
Prompts that produce evidence
Four patterns that reliably return facts instead of impressions.
Ask for the observation, not the verdict. "Is the SEO good?" produces an essay. "Fetch these twelve URLs and print status, title, description and canonical as a table" produces a table.
Ask what would have to be true. "What would have to be true for this check to pass while the feature is broken?" surfaces the gap between what you measured and what you concluded.
Ask for the counter-case. "Find a page where this does not hold." An assistant looking for confirmation finds it every time; one looking for a counter-example finds the exception.
Ask it to break its own check. Covered below, and the single highest-value habit in this entire method.
There is a longer set of these in the prompt library, including the discovery and debugging patterns this leans on.
Severity, and writing it up
Findings sorted by the section they came from are almost useless. Sort by severity.
| Severity | Test | Examples |
|---|---|---|
| Critical | Money, data, or reachability | Cannot buy, cannot contact, data exposed, site not indexable |
| High | A real user is blocked or misled | Dead primary CTA, no delivery, keyboard trap, fabricated reviews |
| Medium | Measurable cost, no blockage | Duplicate titles, missing structured data, slow LCP, thin pages |
| Low | Noticeable to you, not to them | Spacing, copy nits, cosmetic validator warnings |
Each finding gets five fields: URL, what was observed, how it was observed, why it matters, severity. The third field is the one that makes an audit reproducible, and the one most reports omit.
State plainly what you could not check and why. An audit that hides its own gaps is worse than a shorter one that admits them, because the reader assumes the gaps were covered.
Five findings from this site
This storefront was built with the method it sells, and audited the same way. Five real findings, all invisible to someone looking at the site:
- A primary call-to-action rendered invisible. Text at 1:1 contrast against its own background, because a generic link rule outranked the button rule by a single point of CSS specificity. Found by computing contrast in a rendered DOM, not by reading the CSS.
-
The homepage title was the raw
.myshopify.comdomain. A theme fallback that fires when a setting is empty. Found by fetching the homepage and reading the title out of the response. -
Every call-to-action on every non-home page was dead. They linked to
#buy, a fragment that only exists on the homepage. No error, no navigation. Found by resolving fragment links against the page they appear on. - Checkout could not accept payments. The notice is injected by JavaScript and absent from the initial HTML, so every fetch-based check reported a healthy checkout. Found by rendering the page in a browser.
- A fulfilled order delivered nothing. The order was placed with a phone number and no email address, and the delivery app sends by email only. Every status the platform reported said success. Found by checking whether the order actually had an email field.
The pattern across all five: nothing was visibly wrong, and every one was found by fetching something and measuring what came back.
The audit that lies to you
The most dangerous output of an audit is a green check that was never capable of being red.
Every one of these happened while auditing this site, and each was caught only because the check was tested against a failing case afterwards:
- A link crawler reported zero broken links across a run where nine of twenty-eight pages had returned 429.
- A checkout check looked for wording in
document.body.innerTextthat only ever appears in an input'splaceholderattribute. It could not have fired. - A Liquid-error detector matched prose about Liquid errors in an article, and reported a clean page as broken.
- A digital-delivery check passed on an order that delivered nothing, because it never looked at whether the order had an email address.
So: after a check passes, break the thing it checks and confirm it goes red. Delete a canonical tag. Point a link at a 404. Rename the file the script reads. If the check stays green, it was never a check.
This costs a minute per check and it is the difference between an audit and a document that looks like one.
How often to run it
The full four-pass audit is a project, not a routine. A sensible cadence:
| When | What to run |
|---|---|
| Every deploy | The crawl, in CI, failing the build on a new 404 or duplicate title |
| Every template change | Rendered HTML and structured data for the affected template |
| Monthly | Search Console coverage, Core Web Vitals field data, dependency audit |
| Quarterly, or on inheriting a site | All four passes |
Moving the crawl into GitHub Actions is the highest-value single step, because it turns a thing you remember to do into a thing that happens.
Where to go next
The checklist this article describes running is at the website audit checklist, free and ungated, alongside a launch checklist for sites that have not shipped yet. For the underlying build method, start with how to build a website with Claude Code. For the SEO half specifically, the SEO workflow covers what to check and running a technical SEO audit covers how to prove you checked it. The rules that stop an assistant editing during an audit belong in CLAUDE.md.
Sources and further reading
- WCAG 2.2 quick reference — the success criteria behind the accessibility pass
- Google: structured data gallery — which types are eligible for what
- web.dev: Core Web Vitals — the thresholds used in the performance pass
- OWASP Top Ten — the reference behind the security pass