1 # SEO Audit Methodology
2
3 This is the shared, audience-neutral SEO audit methodology. It applies whether
4 you are auditing an external, third-party site (the `seo-auditor` skill) or the
5 user's own Replit app (the SEO Agent scanner mode). Where the two differ, the
6 distinction is called out inline.
7
8 ## Critical First Step: SPA vs SSR Reality Check
9
10 Before anything else, determine whether the site is a React/Vue/Angular SPA or server-side rendered. **This is the single most important distinction in a modern SEO audit.**
11
12 - **SPA (React, Vue, Angular):** Googlebot cannot reliably execute JavaScript. Any page that requires the JS bundle to render is **effectively invisible** to search engines. The `<title>` and `<meta>` in `index.html` are all Google sees.
13 - **SSR (Express, Next.js, Nuxt, SvelteKit):** Full HTML is returned to the crawler. Everything is indexable.
14 - **Hybrid:** Many apps are SPA for authenticated pages but SSR for public/marketing pages. Identify which routes are which.
15
16 **How to detect (external sites, live URL available):** `curl -s https://domain.com/some-page | grep "<h1"` — if no H1 is in the curl output but is visible in the browser, the page is SPA-rendered and invisible to Googlebot.
17
18 **How to detect (own-app scan, source only):** read the framework config and the public HTML entry (`index.html`, `app/layout.tsx`, `pages/_document.tsx`, `src/app.html`, `src/layouts/*.astro`) and classify each public route as SPA / SSR / pre-rendered from the source — do not rely on hitting a live URL.
19
20 **For SPA+SSR hybrids:** Audit only the SSR pages deeply. Note which routes are SPA (and therefore invisible) without alarming the user — authenticated dashboards being SPA is expected and fine.
21
22 ## Methodology
23
24 ### Audit Priority Order
25
26 1. **Crawlability & Indexation** (can Google find and index it?)
27 2. **Technical Foundations** (is the site fast and functional?)
28 3. **On-Page Optimization** (is content optimized?)
29 4. **Content Quality** (does it deserve to rank?)
30 5. **Authority & Links** (does it have credibility?)
31
32 ### Step 1: Crawlability & Indexation
33
34 #### Robots.txt
35
36 - Check for unintentional blocks
37 - Verify important pages allowed
38 - Check sitemap reference
39 - **⚠️ Verify actual content, not just HTTP status.** A React SPA returns 200 for any URL — including `/robots.txt` if no Express route handles it. Always `curl -s /robots.txt | head -5` and confirm the output is `User-agent:` plain text, not `<!DOCTYPE html>`.
40
41 ##### XML Sitemap
42
43 - Exists and accessible
44 - Contains only canonical, indexable URLs
45 - Updated regularly
46 - **⚠️ Same SPA trap applies to `/sitemap.xml`.** `curl /sitemap.xml` and confirm the first line is `<?xml version=`. If it returns `<!DOCTYPE html>`, the sitemap route is missing and the SPA is serving as fallback.
47 - For Express/Node backends with pSEO content: implement sitemap as a dynamic Express route that maps over data arrays (e.g., `BANK_GUIDES`, `GLOSSARY_TERMS`). This way, adding new entries to the arrays automatically updates the sitemap — no manual maintenance needed.
48
49 ###### Site Architecture
50
51 - Important pages within 3 clicks of homepage
52 - Logical hierarchy
53 - No orphan pages (pages with no internal links)
54
55 ###### Index Status
56
57 - site:domain.com check
58 - Compare indexed vs. expected page count
59
60 ###### Indexation Issues
61
62 - Noindex tags on important pages
63 - Canonicals pointing wrong direction
64 - Redirect chains/loops
65 - Soft 404s
66 - Duplicate content without canonicals
67
68 ###### Canonicalization
69
70 - All pages have canonical tags
71 - HTTP → HTTPS canonicals
72 - www vs. non-www consistency
73 - Trailing slash consistency
74
75 ### Step 2: Technical Foundations
76
77 #### Core Web Vitals (2025-2026)
78
79 - **LCP** (Largest Contentful Paint): < 2.5s
80 - **INP** (Interaction to Next Paint): < 200ms — replaced FID in 2025 as the responsiveness metric
81 - **CLS** (Cumulative Layout Shift): < 0.1
82
83 ##### Speed Factors
84
85 - Server response time (TTFB)
86 - Image optimization and modern formats (WebP)
87 - JavaScript execution and bundle size
88 - CSS delivery
89 - Caching headers and CDN usage
90 - Font loading strategy
91
92 ###### Font Bundle Audit — Common Performance Killer
93
94 Check `client/index.html` (the actual source file, not just what Googlebot sees) for bloated Google Fonts requests. A single `<link href="fonts.googleapis.com/css2?family=Inter&family=Roboto&family=Poppins&...">` loading 10+ font families is a render-blocking LCP killer.
95
96 ###### Correct async font loading pattern
97
98 ```html
99 <link rel="preconnect" href="https://fonts.googleapis.com">
100 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
101 <link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap">
102 <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" media="print" onload="this.media='all'">
103 <noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap"></noscript>
104 ```
105
106 Apply this pattern to both `client/index.html` (SPA) and any SSR shared shell functions.
107
108 ###### Mobile-Friendliness
109
110 - Responsive design (not separate m. site)
111 - Tap target sizes
112 - Viewport configured
113 - No horizontal scroll
114 - Mobile-first indexing readiness
115
116 ###### Security
117
118 - HTTPS across entire site
119 - Valid SSL certificate
120 - No mixed content
121 - HSTS header
122
123 ###### URL Structure
124
125 - Readable, descriptive URLs
126 - Keywords where natural
127 - Consistent structure (lowercase, hyphen-separated)
128 - No unnecessary parameters
129
130 **Note:** Google now excludes pages returning non-200 status codes (4xx, 5xx) from the rendering queue entirely — critical for SPAs.
131
132 ### Step 3: On-Page SEO
133
134 #### Title Tags
135
136 - Unique per page, 50-65 characters
137 - Primary keyword near beginning
138 - Compelling and click-worthy
139 - Brand name at end with `|` separator (not `-` or `—`)
140 - Common issues: duplicates, too long/short, keyword stuffing, missing
141 - **Watch for Unicode symbols in H1s** (e.g., `↔`, `→`, `©`). These look odd in SERPs and can be keyword-unfriendly. Use plain text equivalents.
142
143 ##### Meta Descriptions
144
145 - Unique per page, 150-160 characters
146 - Includes primary keyword
147 - Clear value proposition with CTA
148 - Common issues: duplicates, auto-generated, no compelling reason to click
149
150 ###### Heading Structure
151
152 - One H1 per page containing primary keyword
153 - Logical hierarchy (H1 → H2 → H3, no skipping)
154 - Headings describe content, not used just for styling
155
156 ###### Content Optimization
157
158 - Keyword in first 100 words
159 - Related keywords naturally used
160 - Sufficient depth for topic
161 - Answers search intent
162 - Better than current top-ranking competitors
163
164 ###### Image Optimization
165
166 - Descriptive file names and alt text
167 - Compressed file sizes, modern formats (WebP)
168 - Lazy loading, responsive images
169
170 ###### Internal Linking
171
172 - Important pages well-linked with descriptive anchor text
173 - No broken internal links
174 - No orphan pages
175
176 ###### Keyword Targeting (per page)
177
178 - Clear primary keyword target
179 - Title, H1, URL aligned with keyword
180 - Content satisfies search intent
181 - Not competing with other pages (cannibalization)
182
183 ### Step 4: Content Quality — E-E-A-T Signals
184
185 **Experience:** First-hand experience demonstrated, original insights/data, real examples
186
187 **Expertise:** Author credentials visible, accurate and detailed information, properly sourced claims
188
189 **Authoritativeness:** Recognized in the space, cited by others, industry credentials
190
191 **Trustworthiness:** Accurate information, transparent about business, contact info available, privacy policy, HTTPS
192
193 ### Step 5: Structured Data & Social Signals
194
195 #### Schema Markup
196
197 Every SSR page should have the appropriate schema type. Use an array-based approach in your shared shell function so each schema is its own `<script type="application/ld+json">` block — never concatenate JSON strings and inject them inside an existing `<script>` tag, as this creates malformed HTML with nested script tags.
198
199 ##### Correct pattern (Express/Node SSR shell function)
200
201 ```typescript
202 // Accept an array of schema JSON strings
203 schemaJsons?: string[];
204
205 // Render each as its own script block
206 allSchemas.map(s => `<script type="application/ld+json">${s}</script>`).join("\n")
207 ```
208
209 ###### Always-present schemas (add to shared shell)
210
211 - `WebSite` + `Organization` `@graph` — entity identity for Google's Knowledge Graph, sitelinks signals
212 - `BreadcrumbList` — add to every page that has visible breadcrumb navigation, even if the page already has another schema type (HowTo, DefinedTerm, etc.)
213
214 ###### Page-specific schemas
215
216 - HowTo — step-by-step guides
217 - FAQPage — Q&A content (excellent for AI Overviews / featured snippets)
218 - DefinedTerm + DefinedTermSet — glossary terms
219 - ItemList — hub/index pages listing multiple items
220 - Article / BlogPosting — editorial content (include `datePublished`, `dateModified`)
221
222 **Always include `datePublished` and `dateModified`** in schema — freshness signals help maintain rankings on competitive queries.
223
224 **OG Image requirement:** `og:image` requires an actual hosted image URL (1200×630px). Base64-embedded images cannot be OG images. If no hosted image exists, note it as a blocker for social sharing and skip the `og:image` tag rather than pointing to a non-existent URL.
225
226 ###### Minimum social meta set (every page)
227
228 ```html
229 <meta property="og:title" content="..." />
230 <meta property="og:description" content="..." />
231 <meta property="og:url" content="..." />
232 <meta property="og:type" content="website" />
233 <meta property="og:locale" content="..." /> <!-- e.g., es_DO, en_US -->
234 <meta property="og:site_name" content="..." />
235 <meta name="twitter:card" content="summary_large_image" />
236 <meta name="twitter:title" content="..." />
237 <meta name="twitter:description" content="..." />
238 <meta name="twitter:site" content="@handle" />
239 ```
240
241 **Schema Markup Detection Warning:** `webFetch` and `curl` cannot reliably detect structured data — many CMS plugins inject JSON-LD via client-side JavaScript. Never report "no schema found" based solely on webFetch. Recommend using Google Rich Results Test or browser DevTools for accurate schema verification. For SSR pages, curl is reliable.
242
243 ### Step 6: Bot Governance & AI Readiness
244
245 - Review robots.txt to differentiate between beneficial retrieval agents (OAI-SearchBot, Googlebot) and non-beneficial training scrapers
246 - Use structured data (schema.org) as the language of LLMs
247 - Use "BLUF" (Bottom Line Up Front) formatting to help content get cited in AI Overviews
248
249 ### Step 7: Competitor SEO Comparison
250
251 - Search for target keywords and analyze top-ranking pages
252 - Identify content gaps and opportunities
253 - Compare meta tags, content depth, structure, and E-E-A-T signals
254
255 ## SSR Shared Shell Optimization Pattern
256
257 When a site uses a **shared HTML shell function** (common in Express/Node SSR setups), a single change to that function fixes all pages simultaneously. This is the highest-leverage opportunity in an SSR SEO audit.
258
259 ### Audit the shell function for these — fixing once applies to all pages
260
261 1. `og:locale`, `og:site_name` — often missing
262 2. Twitter Card tags — almost always missing
263 3. `<meta name="theme-color">` — small trust/UX signal
264 4. Async font loading — often render-blocking
265 5. `WebSite` + `Organization` JSON-LD — missing from most sites
266 6. Canonical tag structure — verify it's using the correct canonical per page
267
268 #### Per-page additions (must be done individually)
269
270 - `BreadcrumbList` schema — specific to each page's breadcrumb path
271 - `datePublished` / `dateModified` — specific to each content type's schema
272
273 ## Common Issues by Site Type
274
275 **SaaS/Product Sites:** Product pages lack content depth, blog not integrated with product pages, missing comparison/alternative pages, thin feature pages
276
277 ### SPA + SSR Hybrid (React/Node, Next.js, etc.)
278
279 - robots.txt and sitemap.xml not handled by Express — SPA returns 200 with HTML instead of proper plain text / XML
280 - Massive font bundles in `index.html` affecting landing page LCP
281 - Missing OG/Twitter tags in `index.html` (since that's all social crawlers see for SPA pages)
282 - SSR pages missing BreadcrumbList schema despite having breadcrumb HTML
283 - Shared shell function missing Twitter Card, og:locale, og:site_name — fixing once would help all pages
284
285 **E-commerce:** Thin category pages, duplicate product descriptions, missing product schema, faceted navigation creating duplicates
286
287 **Content/Blog Sites:** Outdated content not refreshed, keyword cannibalization, no topical clustering, poor internal linking
288
289 **Local Business:** Inconsistent NAP, missing local schema, no Google Business Profile optimization
290
291 ## Output Format
292
293 ### SEO Audit Report Structure
294
295 ```text
296 # SEO Audit Report: [Website]
297
298 ## Executive Summary
299 - Overall health assessment
300 - Top 3-5 priority issues
301 - Quick wins identified
302
303 ## Critical Issues (Fix Immediately)
304 | Issue | Page | Impact | Evidence | Fix |
305 |-------|------|--------|----------|-----|
306
307 ## High-Impact Improvements
308 | Issue | Page | Impact | Evidence | Fix |
309 |-------|------|--------|----------|-----|
310
311 ## Quick Wins
312 | Opportunity | Page | Potential Impact |
313 |------------|------|-----------------|
314
315 ## Page-by-Page Analysis
316
317 ### [Page URL]
318 - **Title**: Current | Recommended
319 - **Meta Description**: Current | Recommended
320 - **H1**: Current | Recommended
321 - **Content Score**: X/10
322 - **Issues**: [list]
323
324 ## Prioritized Action Plan
325 1. Critical fixes (blocking indexation/ranking)
326 2. High-impact improvements (SSR shell function — fix once, applies to all pages)
327 3. Quick wins (easy, immediate benefit)
328 4. Long-term recommendations (OG image creation, Privacy/Terms SSR, etc.)
329 ```
330
331 ## Tools
332
333 **Free:** Google Search Console, Google PageSpeed Insights, Rich Results Test (use for schema validation — it renders JavaScript), Mobile-Friendly Test, Schema Validator
334
335 **Paid (if available):** Screaming Frog, Ahrefs / Semrush, Sitebulb
336
337 ## Best Practices
338
339 1. **Prioritize by impact** — fix critical issues before optimizing nice-to-haves
340 2. **Write for humans first** — keyword-stuffed content hurts rankings
341 3. **Check actual SERPs** — search for target keywords to understand what Google currently rewards
342 4. **Focus on search intent** — match content type to what users actually want
343 5. **Monitor competitors** — see what top-ranking pages do well and identify gaps
344 6. **Always curl the URL, read the body** — HTTP 200 status means nothing if the SPA is returning HTML for `/robots.txt` or `/sitemap.xml`. Confirm content type and first few lines
345 7. **Find the shared shell function** — in Express/Node SSR apps, a shared HTML shell function is a force multiplier. One change fixes all pages
346 8. **Dynamic sitemaps > static sitemaps** — for apps with content arrays (guides, terms, products), generate the sitemap dynamically from those arrays so it stays current automatically
347
348 ## Limitations
349
350 - Cannot access Google Search Console or Analytics data
351 - Cannot measure actual page speed (use Google Lighthouse separately)
352 - Cannot check backlink profiles (recommend Ahrefs, Semrush, or Moz)
353 - Cannot run full site crawls (recommend Screaming Frog or Sitebulb)
354 - Cannot guarantee ranking improvements — SEO involves many factors
355 - Cannot access pages behind authentication or paywalls