--- name: seo-audit description: Audit a page or the whole site for technical and on-page SEO — crawlability, metadata, heading outline, structured data, internal linking, Core Web Vitals, and the animation-specific crawlability risks this starter carries. Produces ranked findings and fixes them in code. Use when the user asks to "audit SEO", "check my SEO", "why isn't this ranking", "fix meta tags", or before a launch. allowed-tools: Bash, Read, Grep, Glob, Edit, Write, WebFetch --- # SEO audit Audit the **code**, and the live URL when one exists. Lessons from ~50 production sites built from this starter are folded in; the defects it used to seed (placeholders, localhost canonicals, a hidden page for non-JS crawlers, missing privacy route) are fixed upstream — check they stayed fixed. Rank findings by impact and fix them; do not hand back a lecture. ## 1. Indexability — nothing else matters if this is broken - `src/app/robots.ts` — allows crawlers, points at the sitemap, no accidental `disallow: /`. - `src/app/sitemap.ts` — **every public route is listed.** This is the one that rots: adding a route without a sitemap entry is the most common miss in this starter. Cross-check `find src/app -name 'page.tsx'` against the sitemap array. - Canonical URL on each page; no duplicate content across trailing-slash or parameter variants. - **The origin.** `siteConfig.url` = `NEXT_PUBLIC_SITE_URL` → the host's production domain (`VERCEL_PROJECT_PRODUCTION_URL`) → localhost; set `NEXT_PUBLIC_SITE_URL` for a custom domain. `origin-sync.ts` (first in ``) rewrites canonical / og:url / og:image to `location.origin` **after hydration** for JS-running readers on any other host — never before (React 19 matches head tags by URL and inserts a second copy; one site served two canonicals). Link-preview scrapers don't run JS, so the build-time origin decides their cards. **Check on the deployed site:** canonical is the real domain, and `curl -sI` of the `og:image` URL returns 200 — 11 of 12 live deploys once served `http://localhost:3000` as canonical, og:image and every sitemap ``. - No route accidentally opted out of static rendering. Any `headers()` / `cookies()` read — including `await isBot()` in a page or layout — forces dynamic rendering for every visitor. The UA is read in `src/proxy.ts`. Check: `curl -sI / | grep -i 'cache-control\|x-vercel-cache'` → `PRERENDER`/`HIT`. - **Crawlers see the whole page.** `curl -s -A "Googlebot/2.1" /` and `-A "GPTBot/1.0"`: the robot form (``), the `

` and the body copy present and **not under a `hidden` ancestor**. The starter's old empty `app/loading.tsx` wrapped the streamed page in `