SEO + AEO Audit
Produce an audit a developer can act on the same day: concrete findings, each with evidence
(the actual tag, the actual HTTP response) and a fix, ranked by impact. Do not hand back a
generic "add alt text and use keywords" listicle — those are worthless. The value is in checking
this site and reporting what is actually broken.
SEO (ranking in search results) and AEO (getting quoted by AI answer engines) share the same
foundation: a crawler must be able to fetch your URL and read real content and metadata in the
raw HTML response, without running your JavaScript. Most of the highest-impact findings come
from that one fact, so establish it first.
Step 0 — Establish what you're auditing
Before checking anything, figure out three things. Guessing here wastes the whole audit.
- Is there a live URL? If yes, you audit what crawlers actually receive (best signal). If
not, audit the build output / source and say so — some checks (real HTTP status, redirects)
can't be verified without a deployment.
- What renders the HTML? Static file, server-rendered (SSR), statically generated (SSG), or
client-rendered (CSR/SPA)? Look at the framework: a plain Vite/CRA React or Vue SPA is CSR by
default; Next/Nuxt/Astro/SvelteKit are usually SSR/SSG; a
.html file is static. This single
fact drives the most important check below.
- What are the routes/pages? Home, product/landing pages, blog posts, legal pages. You'll
sample across types — a per-page problem (e.g. every route serving the same title) only shows
up when you compare pages.
Step 1 — The rendering test (do this first, it finds the biggest problems)
A crawler for Google can execute JS, but it does so on a delay and imperfectly. Bing,
DuckDuckGo, and nearly every AI crawler (GPTBot, PerplexityBot, ClaudeBot, Bytespider) and every
social/link-preview scraper (Slack, Discord, iMessage, X/Twitter, LinkedIn, Facebook, WhatsApp)
do not run JS at all. They read only the raw HTML. So the question that decides half your
findings is: what is in the raw HTML before JavaScript runs?
Test it. Fetch the raw HTML of several different routes — a JS-free fetch (curl -sL <url>, or
WebFetch, which does not execute page JS) — and inspect, per route:
- Is the
<title> specific to that page, or does every route return the same (usually the
homepage's) title?
- Are
<meta name="description">, Open Graph (og:title/og:description/og:image),
and <link rel="canonical"> present and correct for that route?
- Is the main body content (article text, product copy, headings) present in the HTML, or is
the body essentially
<div id="root"></div> with nothing inside?
- Is the JSON-LD (
<script type="application/ld+json">) present in the raw HTML?
Interpreting the result:
- CSR trap (very common, very damaging). If every route returns the same title/meta and an
empty body shell, the site is client-rendered and its per-page SEO exists only after JS runs.
Consequences to report explicitly, because the user usually doesn't realize the scope:
- Every shared link (blog post, product page) shows the generic homepage preview — killing
social CTR.
- Bing / AI engines index an empty shell for every URL → the content is invisible to them.
- The canonical trap: if the static
index.html hardcodes rel="canonical" to the
homepage, every route ships that canonical in raw HTML. Crawlers can collapse all pages
into the homepage and drop them from the index. This is severe — flag it as P0.
- Fix: prerender or server-render the routes. Name concrete options for their stack (e.g. for a
Vite SPA: a prerender plugin /
react-snap / migrate to SSG; for Next.js: ensure pages aren't
forced client-only). Prerendering the static, public routes usually solves it with the least
change.
- SSR/SSG/static. If each route already ships correct, unique metadata and real content, note
it as a strength and move on to the finer checks — the foundation is sound.
If there is no live URL, reason from the source: an index.html with a single root div and a
client router is CSR; check whether the build step prerenders.
Step 2 — Run the category checks
Work through references/checklist.md (indexability, metadata, structured data, social,
sitemap/robots, content/semantics, performance signals, AEO). Read it now — it is the substance
of the audit. For each category, report what you actually observed on this site with the evidence,
not a definition of the category.
Structured data (JSON-LD) has its own reference because it's where rich results and AI citations
are won or lost: read references/structured-data.md when checking schema, and validate any
JSON-LD you find (correct @type, required properties present, values matching visible content,
no fabricated aggregateRating/review data — fake ratings get manual actions).
Prefer real tools over eyeballing where available: fetch robots.txt and sitemap.xml and
confirm they parse and the URLs 200; check that sitemap.xml matches the actual routes (stale or
missing entries are common); verify redirects and status codes with the live URL.
Step 3 — Report
Lead with what matters. Use this structure:
# SEO + AEO Audit — <site>
**Verdict:** <2-3 sentences: overall health + the single most important thing to fix>
## 🔴 P0 — Critical (silently breaking indexing/sharing)
- **<finding>** — <evidence: the actual tag/response>. Impact: <what it costs>. Fix: <concrete step>.
## 🟡 P1 — Should fix
...
## 🟢 P2 — Nice to have
...
## Strengths (already done well)
- <don't just list problems — note what's correct so the user doesn't "fix" working things>
Rules that keep the report useful:
- Rank by impact, not by category. A wrong canonical outranks a missing
twitter:site. The
user acts top-down, so order must reflect real cost.
- Every finding carries evidence. Quote the actual title that repeated, the canonical URL that
was wrong, the HTTP status. "Meta descriptions could be improved" is noise; "all 12 blog posts
return
<title>Homepage Title</title> in raw HTML — verified via curl" is a finding.
- Every finding carries a fix specific to their stack, not a textbook definition.
- Note strengths. It calibrates trust and stops the user from breaking things that work.
- Don't invent problems. If the foundation is solid, say so. A short honest audit beats a
padded one.
What NOT to do
- Don't keyword-stuff recommendations or suggest doorway/cloaking tactics — modern search
penalizes them and AEO ignores them.
- Don't recommend fake reviews, fake ratings, or fabricated structured data.
- Don't produce a generic checklist with no reference to what this site actually does.
- Don't over-index on tiny wins (a missing
theme-color) while a CSR/canonical problem is
silently deindexing the whole site.
1---2name: seo-audit3description: Audit a website or web app for SEO and AEO (answer-engine optimization) and produce a prioritized, evidence-backed report. Use this whenever the user asks to "audit SEO", "check SEO", "improve search ranking", "review meta tags / Open Graph / structured data", "why isn't my site showing up on Google", "make my site rank", "check my sitemap/robots.txt", or mentions crawlability, indexing, rich results, JSON-LD, canonical tags, social share previews, or getting cited by AI assistants (ChatGPT, Perplexity, Google AI Overviews). Also trigger when someone ships a marketing site, landing page, blog, or docs site and wants it "found" — even if they don't say the word "SEO". Framework-agnostic: works on React/Vue/Svelte SPAs, Next/Nuxt/Astro, static HTML, WordPress, or any live URL.4---56# SEO + AEO Audit78Produce an audit a developer can act on the same day: concrete findings, each with evidence9(the actual tag, the actual HTTP response) and a fix, ranked by impact. Do **not** hand back a10generic "add alt text and use keywords" listicle — those are worthless. The value is in checking11*this* site and reporting what is actually broken.1213SEO (ranking in search results) and AEO (getting quoted by AI answer engines) share the same14foundation: a crawler must be able to fetch your URL and read real content and metadata in the15**raw HTML response**, without running your JavaScript. Most of the highest-impact findings come16from that one fact, so establish it first.1718## Step 0 — Establish what you're auditing1920Before checking anything, figure out three things. Guessing here wastes the whole audit.21221. **Is there a live URL?** If yes, you audit what crawlers actually receive (best signal). If23 not, audit the build output / source and say so — some checks (real HTTP status, redirects)24 can't be verified without a deployment.252. **What renders the HTML?** Static file, server-rendered (SSR), statically generated (SSG), or26 client-rendered (CSR/SPA)? Look at the framework: a plain Vite/CRA React or Vue SPA is CSR by27 default; Next/Nuxt/Astro/SvelteKit are usually SSR/SSG; a `.html` file is static. This single28 fact drives the most important check below.293. **What are the routes/pages?** Home, product/landing pages, blog posts, legal pages. You'll30 sample across types — a per-page problem (e.g. every route serving the same title) only shows31 up when you compare pages.3233## Step 1 — The rendering test (do this first, it finds the biggest problems)3435A crawler for Google *can* execute JS, but it does so on a delay and imperfectly. **Bing,36DuckDuckGo, and nearly every AI crawler (GPTBot, PerplexityBot, ClaudeBot, Bytespider) and every37social/link-preview scraper (Slack, Discord, iMessage, X/Twitter, LinkedIn, Facebook, WhatsApp)38do not run JS at all.** They read only the raw HTML. So the question that decides half your39findings is: **what is in the raw HTML before JavaScript runs?**4041Test it. Fetch the raw HTML of several different routes — a JS-free fetch (`curl -sL <url>`, or42WebFetch, which does not execute page JS) — and inspect, per route:4344- Is the **`<title>`** specific to that page, or does every route return the same (usually the45 homepage's) title?46- Are **`<meta name="description">`**, **Open Graph** (`og:title`/`og:description`/`og:image`),47 and **`<link rel="canonical">`** present and *correct for that route*?48- Is the **main body content** (article text, product copy, headings) present in the HTML, or is49 the body essentially `<div id="root"></div>` with nothing inside?50- Is the **JSON-LD** (`<script type="application/ld+json">`) present in the raw HTML?5152Interpreting the result:5354- **CSR trap (very common, very damaging).** If every route returns the same title/meta and an55 empty body shell, the site is client-rendered and its per-page SEO exists *only after JS runs*.56 Consequences to report explicitly, because the user usually doesn't realize the scope:57 - Every shared link (blog post, product page) shows the generic homepage preview — killing58 social CTR.59 - Bing / AI engines index an empty shell for every URL → the content is invisible to them.60 - **The canonical trap:** if the static `index.html` hardcodes `rel="canonical"` to the61 homepage, *every* route ships that canonical in raw HTML. Crawlers can collapse all pages62 into the homepage and drop them from the index. This is severe — flag it as P0.63 - Fix: prerender or server-render the routes. Name concrete options for their stack (e.g. for a64 Vite SPA: a prerender plugin / `react-snap` / migrate to SSG; for Next.js: ensure pages aren't65 forced client-only). Prerendering the static, public routes usually solves it with the least66 change.67- **SSR/SSG/static.** If each route already ships correct, unique metadata and real content, note68 it as a strength and move on to the finer checks — the foundation is sound.6970If there is no live URL, reason from the source: an `index.html` with a single root div and a71client router is CSR; check whether the build step prerenders.7273## Step 2 — Run the category checks7475Work through `references/checklist.md` (indexability, metadata, structured data, social,76sitemap/robots, content/semantics, performance signals, AEO). Read it now — it is the substance77of the audit. For each category, report what you actually observed on this site with the evidence,78not a definition of the category.7980Structured data (JSON-LD) has its own reference because it's where rich results and AI citations81are won or lost: read `references/structured-data.md` when checking schema, and validate any82JSON-LD you find (correct `@type`, required properties present, values matching visible content,83no fabricated `aggregateRating`/review data — fake ratings get manual actions).8485Prefer real tools over eyeballing where available: fetch `robots.txt` and `sitemap.xml` and86confirm they parse and the URLs 200; check that `sitemap.xml` matches the actual routes (stale or87missing entries are common); verify redirects and status codes with the live URL.8889## Step 3 — Report9091Lead with what matters. Use this structure:9293```94# SEO + AEO Audit — <site>9596**Verdict:** <2-3 sentences: overall health + the single most important thing to fix>9798## 🔴 P0 — Critical (silently breaking indexing/sharing)99- **<finding>** — <evidence: the actual tag/response>. Impact: <what it costs>. Fix: <concrete step>.100101## 🟡 P1 — Should fix102...103104## 🟢 P2 — Nice to have105...106107## Strengths (already done well)108- <don't just list problems — note what's correct so the user doesn't "fix" working things>109```110111Rules that keep the report useful:112113- **Rank by impact, not by category.** A wrong canonical outranks a missing `twitter:site`. The114 user acts top-down, so order must reflect real cost.115- **Every finding carries evidence.** Quote the actual title that repeated, the canonical URL that116 was wrong, the HTTP status. "Meta descriptions could be improved" is noise; "all 12 blog posts117 return `<title>Homepage Title</title>` in raw HTML — verified via curl" is a finding.118- **Every finding carries a fix** specific to their stack, not a textbook definition.119- **Note strengths.** It calibrates trust and stops the user from breaking things that work.120- **Don't invent problems.** If the foundation is solid, say so. A short honest audit beats a121 padded one.122123## What NOT to do124125- Don't keyword-stuff recommendations or suggest doorway/cloaking tactics — modern search126 penalizes them and AEO ignores them.127- Don't recommend fake reviews, fake ratings, or fabricated structured data.128- Don't produce a generic checklist with no reference to what this site actually does.129- Don't over-index on tiny wins (a missing `theme-color`) while a CSR/canonical problem is130 silently deindexing the whole site.