1 ---
2 name: programmatic-seo
3 description: Build SEO-optimized pages at scale using templates and data.
4 ---
5
6 # Programmatic SEO
7
8 Build SEO-optimized pages at scale using templates and data. Create page generators that target keyword patterns and produce unique, valuable content for each variation.
9
10 ## When to Use
11
12 - User wants to create many SEO-driven pages from a template (e.g., "[product] vs [competitor]", "[service] in [city]")
13 - User mentions programmatic SEO, template pages, directory pages, location pages, or comparison pages at scale
14
15 - User wants to build an "alternatives to X" page set, integrations directory, or glossary
16 - User has a data set they want to turn into individual landing pages
17
18 ## When NOT to Use
19
20 - Auditing existing SEO issues (use seo-auditor skill)
21 - Writing a single blog post or landing page (use content-machine skill)
22
23 - One-off competitive analysis (use competitive-analysis skill)
24
25 ## Core Principles
26
27 1. **Information gain is everything** — Every page must contain information the user cannot find on existing top-10 results. Proper H1s, keyword density, and clean structure are baseline hygiene, not a ranking advantage. Pages that "look like SEO" but add no new information are what Google's spam systems now target.
28 2. **Unique value per page** — Every page must provide value specific to that page, not just swapped variables in a template. If you removed the entity name, would two pages be indistinguishable? If yes, don't publish.
29
30 3. **Proprietary data wins** — Hierarchy: proprietary > product-derived > user-generated > licensed > public (weakest). The strongest pSEO pages contain data no one else has — original tests, first-party metrics, real user reviews, proprietary calculations.
31 4. **Subfolders, not subdomains** — `yoursite.com/templates/resume/`not`templates.yoursite.com/resume/`
32
33 5. **Match search intent precisely** — Pages must match the intent behind the query, not just contain the keywords. This means choosing the right page type (blog post vs. comparison table vs. tool vs. directory listing), answering the actual question, and structuring content the way users expect for that query type.
34 6. **Quality over quantity** — 100 great pages beat 10,000 thin ones. Scale without information gain is just industrialized noise. Google has more content than it needs — your pages must earn their place by being genuinely better than what already ranks.
35
36 7. **Topical authority compounds** — A site that demonstrates deep expertise in a specific topic earns trust signals that lift all pages in that topic cluster. Scattered, shallow coverage across unrelated topics builds no authority.
37
38 ---
39
40 ## ⚠️ CRITICAL: Check the App's Rendering Strategy First
41
42 **Before writing a single line of code**, determine whether the existing application is a Single Page Application (SPA).
43
44 **SPAs (React, Vue, Angular, Svelte) are invisible to Googlebot.** Googlebot fetches raw HTML and does not execute client-side JavaScript. A React component that renders `<h1>Currency Converter</h1>`only works in a browser — Googlebot sees an empty`<div id="root"></div>`.
45
46 ### Decision Tree
47
48 ```text
49
50 Is the app a SPA (React/Vue/Angular)?
51
52 │
53
54 ├── YES → SEO pages MUST be server-rendered (SSR)
55
56 │ Options:
57
58 │ A. Express/Fastify route returning a complete HTML string ← simplest for existing SPAs
59
60 │ B. Migrate to Next.js/Nuxt/SvelteKit with SSR support
61
62 │ C. Add a static site generator (Astro) alongside the SPA for SEO pages only
63
64 │
65
66 └── NO (already Next.js, Nuxt, etc.) → Use the framework's built-in SSR/SSG/ISR patterns (see Step 6)
67
68 ```
69
70 **The SPA trap:** The most common pSEO mistake is adding routes to a React Router / Wouter / Vue Router app and assuming they will be indexed. They will not. Always verify by `curl`-ing the URL and checking if the content is present in the raw HTML response:
71
72 ```bash
73
74 curl -s https://yoursite.com/your-seo-page | grep "target keyword"
75
76 # If empty or only returns <div id="root"></div> → not crawlable
77
78 ```
79
80 ---
81
82 ## Content Authenticity — Don't Hallucinate Business Data
83
84 When building programmatic SEO for **the user's own company**, you will not have access to their internal data (customer stories, case studies, testimonials, product metrics, pricing, team bios, etc.). **Do not fabricate this information.**
85
86 ### Before generating any company-specific content, ask the user for
87
88 - Customer names, logos, or testimonials they want featured
89 - Case study data (metrics, outcomes, quotes)
90
91 - Product-specific details (features, pricing tiers, integrations list)
92 - Any proprietary data that should populate template variables
93
94 #### If the user hasn't provided this data, default to safe content patterns
95
96 - Industry research and statistics (sourced via `webSearch`)
97 - General descriptions of the problem/solution category
98
99 - Feature explanations based on what's publicly visible on their site (use `webFetch` on their domain)
100 - Placeholder blocks clearly marked `[INSERT: customer testimonial]`or`[INSERT: case study metrics]`
101
102 - Comparison data pulled from public sources (G2, Capterra reviews via `webSearch`)
103
104 **Never generate:** fake customer quotes, fabricated ROI numbers, invented case studies, made-up testimonials, or fictional company metrics. These damage trust and can create legal liability.
105
106 For **generic/research topics** (e.g., "[city] cost of living", "[tool A] vs [tool B]", glossary terms), use `webSearch` to gather real data and cite sources.
107
108 ---
109
110 ## ⚠️ Strategic Judgment — What AI Cannot Do For You
111
112 **This is the most important section in this skill.** AI excels at scaling execution — drafting content, structuring pages, generating variations. But it cannot make the strategic decisions that determine whether a pSEO campaign succeeds or fails. These decisions must come from the user (or a human SEO strategist), and the agent must actively prompt for them rather than making assumptions.
113
114 ### Decisions that require human judgment
115
116 Before building anything, the user must answer (or the agent must ask):
117
118 1. **Is this keyword worth a page at all?** Not every keyword with search volume deserves a page. If the top 10 results are dominated by high-authority brands and the user's site has no topical authority, building 500 pages won't help.
119 2. **What page type matches the query intent?** The same topic might need a blog post, a comparison table, an interactive tool, a directory listing, or a data profile. Getting the page type wrong means the page will never rank, regardless of content quality. For example:
120
121 - "best CRM for real estate" → listicle/comparison (not a product page)
122 - "HubSpot vs Salesforce" → head-to-head comparison (not a blog post)
123
124 - "what is a CRM" → educational/glossary (not a product page)
125 - "CRM pricing" → pricing table with real numbers (not a blog post about pricing)
126
127 1. **What information gain does this page offer over the current top 10?** If the answer is "nothing — we're just rewriting what's already there with AI," do not build the page. Specifically ask: do we have original data, unique analysis, proprietary metrics, real case studies, or first-hand experience that the current results lack?
128 2. **Does the user's site have topical authority in this area?** A brand-new domain publishing 200 glossary pages on a topic it has no track record in will be ignored. Topical authority is built through depth (many related, high-quality pages), external trust signals (backlinks from relevant sites), and user engagement over time.
129
130 3. **What is the commercial intent and conversion path?** Pages that attract traffic but have no clear next step for the user waste crawl budget and dilute site quality. Every pSEO page should have a purpose beyond "get impressions."
131
132 ### What the agent should do
133
134 - **Always ask the user** what unique data, experience, or perspective they can bring to the topic before generating content. If they say "just write something about X," push back and explain that pages without information gain will not rank.
135 - **Never assume scale equals strategy.** If the user asks for "500 SEO articles," the correct response is to first validate whether 500 pages is the right number, whether the topics have demand, and whether the user has enough unique data to make each page valuable.
136
137 - **Recommend starting small.** Build 10-20 high-quality pages first, validate they get indexed and earn impressions, then scale what works. This is the opposite of the "blast 500 pages and hope" approach.
138 - **Be explicit about what AI is doing vs. what requires human input.** AI writes the page. The human decides whether the page should exist.
139
140 ### The "SEO cosplay" trap
141
142 A page can have perfect H1 tags, clean H2/H3 hierarchy, optimized meta descriptions, proper schema markup, good keyword density, and fast load times — and still rank nowhere. These are table stakes, not differentiators. Google's ranking systems evaluate whether a page genuinely satisfies the user's query better than alternatives. Structure without substance is what practitioners call "SEO cosplay" — it looks like SEO but doesn't perform.
143
144 **The agent must not conflate technical SEO hygiene with content quality.** Both are necessary. Neither is sufficient alone.
145
146 ---
147
148 ## Information Gain — The Core Quality Signal
149
150 Google's systems increasingly evaluate "information gain" — whether a page adds something new to the corpus of existing results for a query. This is the single most important concept in modern pSEO.
151
152 ### What counts as information gain
153
154 | Source of gain | Example | Strength |
155
156 |---|---|---|
157
158 | **Original data / metrics** | Your own A/B test results, proprietary benchmarks, internal analytics | Strongest — impossible to replicate |
159
160 | **First-hand experience** | Actual product reviews you conducted, real case studies with named clients | Very strong — hard to fake |
161
162 | **Unique analysis** | Novel comparisons, calculated scores, derived insights from raw data | Strong — requires expertise |
163
164 | **Curated judgment** | Expert picks, opinionated rankings with reasoning, "here's what we'd actually use" | Moderate — requires credibility |
165
166 | **Structured aggregation** | Data from multiple sources combined into a single useful view (with attribution) | Moderate — useful but reproducible |
167
168 | **Rewritten common knowledge** | The same information available on 50 other sites, just rephrased | Zero — this is what gets penalized |
169
170 ### The information gain test
171
172 Before publishing any pSEO page set, run this test on 5 random pages:
173
174 1. Search Google for the target keyword
175 2. Read the top 3 results
176
177 3. Read your generated page
178 4. Ask: **"Does our page contain at least one piece of information, data point, or insight that none of the top 3 results have?"**
179
180 5. If no → do not publish. Improve the data source or reduce the page set to only pages where you have genuine information gain.
181
182 ### Scaled content abuse (Google's 2024+ spam policy)
183
184 Google now explicitly targets "scaled content abuse" — content produced at scale (whether by AI, humans, or automation) that exists primarily to manipulate rankings rather than help users. The pattern Google detects is:
185
186 - Many pages with similar structure
187 - Smooth, readable prose with no new information
188
189 - Comprehensive coverage that is really just reorganized common knowledge
190 - High page count with no corresponding trust signals or user engagement
191
192 - Each page is readable, but after reading it you've learned nothing new
193
194 **This pattern triggers penalties regardless of whether AI wrote the content.** A human content farm producing the same pattern gets the same treatment. The issue is not AI — it is the absence of value at scale.
195
196 ---
197
198 ## Proven Playbooks (Real Traffic Numbers)
199
200 | Playbook | URL pattern | Who does it | Scale |
201
202 |---|---|---|---|
203
204 | **Integrations** | `/apps/[A]/integrations/[B]` | **Zapier** — ~56k pages, 5.8M+ monthly organic visits, ranks for 1.3M keywords. Proprietary data (triggers/templates per app pair) no one else can replicate. | N² combinations |
205
206 | **Conversions** | `/currency-converter/[from]-to-[to]-rate` | **Wise** — 8.5M pages across locale subfolders, 60M+ monthly visits. Live exchange-rate data + fee calculators = unique value per page. | N² × locales |
207
208 | **Locations** | `/Restaurants-[city]`,`/[cuisine]-Restaurants-[city]`,`/Restaurants-[neighborhood]` | **Tripadvisor** — 700M+ pages, 226M+ monthly visits. UGC reviews keep pages fresh; layered matrix (city × cuisine × neighborhood). | city × category × modifier |
209
210 | **Data profiles** | `/[city-slug]` | **Nomad List** — cost-of-living, internet speed, safety scores per city. Pages are pure data tables — minimal prose, high value. | N entities |
211
212 | **Comparisons** | `/[A]-vs-[B]`,`/alternatives/[A]` | **G2, Capterra** — "vs" pages + "alternatives" pages, populated by user reviews. | N² / 2 |
213
214 | **Templates** | `/templates/[type]` | **Canva, Notion** — each template is a landing page. | N types |
215
216 | **Glossary** | `/learn/[term]` | **Ahrefs, HubSpot** — definition pages cluster topical authority. | N terms |
217
218 | **How-to guides** | `/guides/[task]-with-[tool]` | Documentation sites — step-by-step guides, HowTo schema | N tasks × M tools |
219
220 | **Personas** | `/[product]-for-[audience]` | "CRM for real estate agents" | N × M |
221
222 **The test:** If your data doesn't meaningfully change between page variations, don't build it. Zapier works because Slack+Asana genuinely differs from Slack+Trello. "Plumber in Austin" vs "Plumber in Dallas" with identical boilerplate = thin content penalty.
223
224 Layer playbooks for long-tail: Tripadvisor's "Best Italian Restaurants in Chinatown NYC" = curation × cuisine × neighborhood.
225
226 ### Why these playbooks work — and why copycats fail
227
228 Every successful pSEO example above shares one trait: **the data genuinely changes between pages.** Zapier's Slack+Asana page has different triggers, different actions, and different templates than Slack+Trello. Wise's USD-to-EUR page has a different exchange rate, different fee structure, and different historical chart than USD-to-GBP. Tripadvisor's pages have different restaurants, different reviews, and different ratings per city.
229
230 Copycats fail because they replicate the URL structure without replicating the information gain. Creating 500 "/service-in-city" pages where only the city name changes and the rest is identical prose is not programmatic SEO — it is spam at scale. Google's systems detect this pattern reliably: many pages, similar structure, no meaningful data variation.
231
232 **Before choosing a playbook, ask:** "Do I have genuinely different data for each page variation, or am I just swapping one variable into the same template?" If the latter, reduce the page count to only variations where you have real data, or choose a different playbook entirely.
233
234 ---
235
236 ## Implementation
237
238 ### Step 1: Keyword Pattern Research
239
240 - Identify the repeating structure and variables
241 - Count how many unique combinations exist
242
243 - Validate demand: aggregate search volume, distribution (head vs. long tail), trend direction
244
245 ### Step 2: Data Requirements
246
247 - What data populates each page?
248 - Is it first-party, scraped, licensed, or public?
249
250 - How is it updated and maintained?
251
252 ### Step 3: Template Design
253
254 #### Page structure
255
256 - H1 with target keyword
257 - Unique intro (not just variables swapped — conditional content based on data)
258
259 - Data-driven sections with original insights/analysis per page
260 - Related pages / internal links
261
262 - CTAs appropriate to intent
263
264 ##### Ensuring uniqueness — critical to avoid thin content penalties
265
266 - Conditional content blocks that vary based on data attributes
267 - Calculated or derived data (not just raw display)
268
269 - Editorial commentary unique to each entity
270 - User-generated content where possible
271
272 ### Step 4: Internal Linking Architecture
273
274 #### Hub and spoke model
275
276 - Hub: Main category page (e.g., "/integrations/")
277 - Spokes: Individual programmatic pages (e.g., "/integrations/slack-asana/")
278
279 - Cross-links between related spokes
280
281 Every page must be reachable from the main site. **Update the main app's footer and navigation to include links to SEO hub pages** — SEO pages that aren't linked from the main site are orphan pages that Google may never discover or trust.
282
283 Include XML sitemap and breadcrumbs with structured data.
284
285 ### Step 5: Indexation Strategy
286
287 - Prioritize high-volume patterns for initial crawling
288 - Noindex very thin variations rather than publishing them
289
290 - Manage crawl budget (separate sitemaps by page type)
291 - Monitor indexation rate in Search Console
292
293 ### Step 6: Build the Page Generator
294
295 #### Rendering strategy decision
296
297 | Page count | Data freshness | Strategy |
298
299 |---|---|---|
300
301 | <1,000 | Rarely changes | **SSG** — pre-render everything at build |
302
303 | 1,000-100,000 | Changes daily/weekly | **ISR** — pre-render popular subset, generate rest on-demand + cache |
304
305 | 100,000+ or live data | Real-time (prices, rates) | **ISR with short revalidate** or SSR |
306
307 SSG is fastest but build time scales linearly — 50k pages can mean 30+ min builds. ISR is the pSEO sweet spot: instant deploys, pages generate on first request then cache.
308
309 #### For SPAs: Express SSR pattern
310
311 When adding pSEO to an existing SPA, the simplest approach is adding Express routes that return complete HTML strings. Organize into three layers:
312
313 ##### 1. Shared HTML shell (create this first)
314
315 ```typescript
316
317 // server/ssrShared.ts — create before writing any individual page
318
319 export function ssrHtmlShell({ title, description, canonical, schemaJson, css, body, ...}): string {
320
321 return `<!DOCTYPE html>
322
323 <html lang="...">
324
325 <head>
326
327 <meta charset="UTF-8" />
328
329 <title>${title}</title>
330
331 <meta name="description" content="${description}" />
332
333 <link rel="canonical" href="${canonical}" />
334
335 <!-- OG tags, fonts, schema -->
336
337 <style>${sharedCss()}${css ?? ""}</style>
338
339 </head>
340
341 <body>
342
343 ${header()}
344
345 ${body}
346
347 ${footer()}
348
349 </body>
350
351 </html>`;
352
353 }
354
355 ```
356
357 Create this shared shell **before** writing any individual page. Every new SEO page reuses it. This ensures consistent branding (header, footer, fonts, base CSS) across all SSR pages without duplication.
358
359 ###### 2. Data + page generator modules (one per playbook)
360
361 ```typescript
362
363 // server/glossary.ts
364
365 export const TERMS: GlossaryTerm[] = [ /* structured data */ ];
366
367 export function getTermHtml(slug: string, logoBase64: string): string | null { ... }
368
369 export function getTermIndexHtml(logoBase64: string): string { ... }
370
371 ```
372
373 Keep data as typed arrays/objects in the same file as the generator. This makes content easy to update without touching routing logic.
374
375 ###### 3. Routes (thin, just wiring)
376
377 ```typescript
378
379 // in routes.ts
380
381 app.get("/glossary/:slug", (req, res) => {
382
383 const html = getTermHtml(req.params.slug, LOGO_BASE64);
384
385 if (!html) { res.status(404).end(); return; }
386
387 res.setHeader("Cache-Control", "public, max-age=86400");
388
389 res.setHeader("Content-Type", "text/html; charset=utf-8");
390
391 res.send(html);
392
393 });
394
395 ```
396
397 #### Cache headers per page type
398
399 Set cache headers based on data freshness — not one-size-fits-all:
400
401 | Page type | Recommended header | Reason |
402
403 |---|---|---|
404
405 | Static content (glossary, guides) | `public, max-age=86400` | Content rarely changes; CDN can serve |
406
407 | Live data (exchange rates, prices) | `no-cache`or`s-maxage=60` | Must be fresh; stale data damages credibility |
408
409 | Semi-dynamic (weekly updates) | `public, s-maxage=3600, stale-while-revalidate=86400` | Balance freshness vs. load |
410
411 #### For Next.js App Router
412
413 ```tsx
414
415 export async function generateStaticParams() {
416
417 const popular = await db.query('SELECT slug FROM entities ORDER BY search_volume DESC LIMIT 500');
418
419 return popular.map(e => ({ slug: e.slug }));
420
421 }
422
423 export const dynamicParams = true;
424
425 export const revalidate = 3600;
426
427 export async function generateMetadata({ params }) {
428
429 const { slug } = await params;
430
431 const entity = await getEntity(slug);
432
433 return {
434
435 title: `${entity.name} — ${entity.category} | Brand`,
436
437 description: entity.summary,
438
439 alternates: { canonical: `https://site.com/${entity.category}/${slug}` },
440
441 };
442
443 }
444
445 export default async function Page({ params }) {
446
447 const { slug } = await params;
448
449 const entity = await getEntity(slug);
450
451 if (!entity) notFound();
452
453 }
454
455 ```
456
457 **Critical ISR rules:** `generateStaticParams`is NOT re-run on revalidation. Must return an array (even`[]`) or the route becomes fully dynamic. Set`dynamicParams = false` only if you want 404s for anything not pre-generated.
458
459 #### Astro alternative
460
461 Better for content-heavy, less-interactive pages. Ships zero JS by default — better Core Web Vitals.
462
463 ```js
464
465 export async function getStaticPaths() {
466
467 const entities = await loadEntities();
468
469 return entities.map(e => ({ params: { slug: e.slug }, props: { entity: e } }));
470
471 }
472
473 ```
474
475 No native ISR; use on-demand rendering + `Cache-Control: s-maxage=3600, stale-while-revalidate`.
476
477 #### Sitemaps at scale
478
479 Google's limit is 50,000 URLs per sitemap file. Shard into `sitemap-1.xml`,`sitemap-2.xml`... referenced by a sitemap index. For ISR/SSR sites, generate sitemaps server-side from the DB, not at build time. **Warning:** Google will NOT index all pages immediately — indexation at scale takes weeks/months. Prioritize high-volume slugs in the first sitemap.
480
481 ---
482
483 ## SSR-Specific Implementation Notes
484
485 ### Asset handling in SSR
486
487 Frontend assets (images, fonts) processed by Vite/webpack/esbuild are **not** directly accessible to server-side routes. Common problems:
488
489 - Images imported with `import logo from "@assets/logo.png"` work in React but not in Express route handlers
490 - Files copied to `public/` by the build tool may not be served correctly in development
491
492 - Files with restricted permissions (`rw-------`) will throw EACCES errors when read from disk
493
494 #### Solutions by asset type
495
496 | Asset | Recommended approach |
497
498 |---|---|
499
500 | Logo / brand images | Base64-encode at server startup; embed inline in `<img src="data:...">` |
501
502 | Small icons | Inline SVG string in the HTML template |
503
504 | External fonts | Google Fonts CDN link in `<head>` |
505
506 | Background images | Use CSS gradients or CDN URLs |
507
508 ```typescript
509
510 // Base64 encode logo once at startup — safe for all SSR pages
511
512 import fs from "fs";
513
514 const logoBuffer = fs.readFileSync("./attached_assets/logo.png");
515
516 const LOGO_BASE64 = `data:image/png;base64,${logoBuffer.toString("base64")}`;
517
518 ```
519
520 ### Header/footer consistency
521
522 SSR pages and the React/Vue/SPA app will share the same domain. Users will navigate between them. **Brand consistency is critical** — a mismatch between the main app's header and the SSR page's header breaks trust.
523
524 Steps:
525
526 1. Study the main app's header/footer colors, logo treatment, link structure, and typography
527 2. Replicate them in the shared `ssrShared.ts` shell functions — not approximations, pixel-for-pixel matches
528
529 3. When the main app's footer is updated (new links, new columns), update the SSR footer function too
530 4. Conversely, when new SSR hub pages are added (e.g., `/glossary`,`/guides`), add them to the main app's footer navigation
531
532 ### Logo rendering in SSR
533
534 If the logo image has a non-standard aspect ratio or significant whitespace, a plain `height: 48px` will render it incorrectly. Inspect how the main React app clips and positions the logo, then replicate exactly:
535
536 ```html
537
538 <!-- If the main app uses overflow-hidden clipping to show only part of the image: -->
539
540 <div style="height:56px; width:160px; overflow:hidden; display:flex; align-items:center; justify-content:center;">
541
542 <img src="${LOGO_BASE64}" style="width:auto; height:250%; object-fit:contain; margin-top:-5%;" />
543
544 </div>
545
546 ```
547
548 Don't guess — read the main app's logo component before writing the SSR equivalent.
549
550 ### Breadcrumb placement and negative margins
551
552 A common hero design pattern uses negative `margin-top` on the content section to create a "card floating up into the hero" visual effect:
553
554 ```css
555
556 .hero { padding-bottom: 72px; }
557
558 .main { margin-top: -32px; } /* pulls first card up into hero */
559
560 ```
561
562 **This breaks breadcrumbs.** If a breadcrumb element sits between the hero and `.main`in the HTML, the negative margin on`.main` will pull the main content over the breadcrumb, hiding it.
563
564 **Rule: breadcrumbs must always be the first child inside `.main`**, never between`.main` and the preceding section.
565
566 ```html
567
568 <!-- WRONG: breadcrumb between hero and main -->
569
570 <div class="hero">...</div>
571
572 <nav class="breadcrumb">...</nav> <!-- will be covered by .main -->
573
574 <div class="main">...</div>
575
576 <!-- CORRECT: breadcrumb inside main -->
577
578 <div class="hero">...</div>
579
580 <div class="main">
581
582 <nav class="breadcrumb">...</nav> <!-- safely in the content flow -->
583
584 <div class="card">...</div>
585
586 </div>
587
588 ```
589
590 Additionally, if a page has a breadcrumb and the hero uses large `padding-bottom` (designed to pair with negative margin), that padding becomes wasted dark space. Override it on breadcrumb pages:
591
592 ```html
593
594 <div class="hero" style="padding-bottom: 32px;">
595
596 ```
597
598 ---
599
600 ## Schema Markup by Playbook
601
602 Match schema type to content type — don't use a generic WebPage schema when a more specific type exists:
603
604 | Playbook | Schema type | Key properties |
605
606 |---|---|---|
607
608 | Glossary / definitions | `DefinedTerm`+`DefinedTermSet`|`name`,`description`,`inDefinedTermSet` |
609
610 | How-to guides | `HowTo`|`step[]`with`HowToStep`,`tool`,`supply` |
611
612 | FAQ sections | `FAQPage`|`mainEntity[]`with`Question`+`Answer` |
613
614 | Comparisons | `ItemList`or`Review`|`itemListElement[]`,`ratingValue` |
615
616 | Data profiles | `Dataset`or`Place`|`name`,`description`,`spatialCoverage` |
617
618 | Recipes/templates | `CreativeWork`|`name`,`description`,`genre` |
619
620 Multiple schema types can appear on the same page (e.g., a HowTo guide can also have a FAQPage block at the bottom).
621
622 ---
623
624 ## Internationalization and Locale Handling
625
626 Only add multilingual pSEO when you have **genuinely translated data** — not machine-translated boilerplate. Machine-translating 10,000 thin pages at once is one of the fastest ways to trigger a spam manual action.
627
628 ### URL structure — subfolders win
629
630 | Approach | Example | SEO verdict |
631
632 |---|---|---|
633
634 | Subfolder | `yoursite.com/es/glosario/presupuesto` | **Preferred** — consolidates domain authority |
635
636 | ccTLD | `yoursite.es/glosario/presupuesto` | Strong signal but costly to maintain |
637
638 | Subdomain | `es.yoursite.com/...` | Treated as separate site; avoid for pSEO |
639
640 | Query param | `yoursite.com/glossary?lang=es` | Not crawlable reliably; never use |
641
642 ### hreflang implementation
643
644 Every page that has a locale variant must declare all variants (including `x-default`) in`<head>`:
645
646 ```html
647
648 <link rel="alternate" hreflang="en" href="https://site.com/glossary/budget" />
649
650 <link rel="alternate" hreflang="es" href="https://site.com/es/glosario/presupuesto" />
651
652 <link rel="alternate" hreflang="x-default" href="https://site.com/glossary/budget" />
653
654 ```
655
656 Rules:
657
658 - Every page in the set must list **all** pages in the set (bidirectional)
659 - Omitting a language from even one page breaks the cluster for Google
660
661 - `x-default` should point to the most general version (usually English or a language-selector page)
662 - Validate with Google Search Console → Enhancements → International targeting
663
664 ### Data structure for multilingual pages
665
666 Store translations as a map keyed by locale so the generator stays clean:
667
668 ```typescript
669
670 const TERM = {
671
672 slug: { en: "budget", es: "presupuesto" },
673
674 name: { en: "Budget", es: "Presupuesto" },
675
676 definition: { en: "...", es: "..." },
677
678 };
679
680 function getTermHtml(slug: string, locale: "en" | "es"): string { ... }
681
682 ```
683
684 ### Machine-translation risk
685
686 Machine-translated prose is detectable by Google and often results in near-duplicate content across locales. Only translate if:
687
688 - A human reviews/edits the output, OR
689 - The page is primarily structured data (tables, numbers, steps) with minimal prose
690
691 ---
692
693 ## Content Freshness Signals
694
695 Google rewards pages that are genuinely kept up to date. Stale pSEO pages — especially data-driven ones — gradually lose rankings as fresher competitors appear.
696
697 ### `dateModified` in schema
698
699 Every page with structured data should carry a timestamp:
700
701 ```json
702
703 {
704
705 "@type": "WebPage",
706
707 "datePublished": "2024-01-15",
708
709 "dateModified": "2025-03-01"
710
711 }
712
713 ```
714
715 **Only update `dateModified`when content actually changes.** Bumping it on a schedule without real changes is a deception signal that Google penalises. If you auto-regenerate pages, tie`dateModified`to the data source's`updatedAt`, not the deploy timestamp.
716
717 ### When and how to regenerate
718
719 | Data type | Trigger strategy |
720
721 |---|---|
722
723 | Exchange rates / prices | SSR with short cache (`s-maxage=60`); no static generation |
724
725 | Product/company data | Webhook from data source on change → re-render and invalidate CDN cache |
726
727 | Weekly-updated datasets | Nightly cron job → regenerate changed pages → update sitemap `<lastmod>` |
728
729 | Static reference content (glossary, guides) | Regenerate on code deploy only |
730
731 For Express SSR, the page is always fresh — no regeneration needed. The cost is latency; mitigate with a CDN in front.
732
733 For ISR (Next.js), trigger on-demand revalidation via the revalidation API when upstream data changes:
734
735 ```typescript
736
737 // When data changes, invalidate the specific path
738
739 await fetch(`/api/revalidate?secret=TOKEN&path=/glossary/${slug}`, { method: "POST" });
740
741 ```
742
743 ### Signals that indicate staleness to Google
744
745 - Dates in page content (e.g., "Updated January 2022") that are old
746 - Prices or rates that no longer match the live source
747
748 - Links to pages that now 404
749 - Schema `dateModified` older than 12 months for competitive queries
750
751 ---
752
753 ## Crawl Budget Management at Scale
754
755 Crawl budget is the number of pages Googlebot will crawl on your site per day. For sites with 10k+ URLs it becomes a real constraint — Googlebot will simply stop crawling before it reaches all your pages, and newly added pages may take weeks to be discovered.
756
757 ### What wastes crawl budget
758
759 - **Faceted navigation**: Filter/sort URLs like `?color=red&size=M` that produce near-duplicate pages
760 - **Pagination**: `/page/2`,`/page/3`... where the same products appear on multiple pages
761
762 - **Session IDs / tracking params**: `?sessionid=abc123` creating millions of unique URLs
763 - **Internal search results**: `/search?q=anything` — never crawlable, always thin
764
765 ### Solutions
766
767 **Block via `robots.txt`** (prevents crawling, doesn't affect indexation of already-indexed pages):
768
769 ```text
770
771 User-agent: Googlebot
772
773 Disallow: /search
774
775 Disallow: /*?sessionid=
776
777 ```
778
779 **`noindex` on parameterized URLs** (crawled but not indexed — use when the URL must exist for users):
780
781 ```html
782
783 <meta name="robots" content="noindex, follow" />
784
785 ```
786
787 **Canonical for near-duplicates** (one sorted/filtered variant is canonical; others point to it):
788
789 ```html
790
791 <!-- On /products?sort=price-asc -->
792
793 <link rel="canonical" href="https://site.com/products" />
794
795 ```
796
797 **Pagination**: Use `rel="next"`/`rel="prev"` or consolidate paginated content into one long page. Don't noindex paginated pages if they contain unique products not on page 1.
798
799 ### Diagnosing crawl budget problems
800
801 In Google Search Console → Settings → Crawl stats:
802
803 - **Crawled pages/day declining** → Googlebot is throttling due to slow responses or crawl errors
804 - **Response codes: high 404 rate** → fix broken links; they waste crawl budget
805
806 - **Crawled but not indexed** rising → thin content or crawl budget exhausted before quality assessment
807
808 For large sites, use separate sitemaps per page type (`sitemap-glossary.xml`,`sitemap-guides.xml`) and prioritize high-value pages in the first sitemap.
809
810 ---
811
812 ## AI-Generated Content Policy
813
814 ### What Google actually detects
815
816 Google does not need to determine whether content was written by AI or a human. It identifies a **pattern**: low-cost mass production where pages look different on the surface but are essentially repetitive underneath. The signals of this pattern include:
817
818 - Many pages with similar titles and angles
819 - Smooth, grammatically correct prose with no new information
820
821 - Comprehensive coverage that is really just reorganized common knowledge
822 - Each page is readable, but after reading it you've learned nothing new
823
824 - High page count with no corresponding user engagement or trust signals
825
826 **This pattern triggers penalties regardless of authorship.** A human content farm producing the same pattern gets the same treatment. The issue is never "AI wrote this" — it is "this adds nothing."
827
828 ### The correct framing
829
830 The question is not: "Is it safe to use AI for SEO content?"
831
832 The question is: **"Does this page contain enough information gain, search intent match, and trust signals that Google would rank it — regardless of how it was produced?"**
833
834 If yes, AI is a powerful accelerator. If no, AI just lets you produce worthless pages faster.
835
836 ### Where AI helps in pSEO
837
838 | Use case | Why it works |
839
840 |---|---|
841
842 | Summarising structured data into readable prose | Each page's data is unique → output is unique |
843
844 | Generating FAQ blocks from a data object | Templated but data-driven → not boilerplate |
845
846 | Translating + adapting content (with review) | Saves time; review catches errors |
847
848 | Writing meta descriptions from page data | Short, formula-driven → low risk |
849
850 | Structuring raw data into tables and comparisons | Saves formatting time; data does the heavy lifting |
851
852 | Drafting initial content for human review and enrichment | Faster starting point; human adds the information gain |
853
854 ### Where AI hurts in pSEO
855
856 | Anti-pattern | Risk |
857
858 |---|---|
859
860 | Generic "overview" paragraphs with no data | Identical across all pages → thin content |
861
862 | AI-expanded boilerplate ("X is a type of Y that...") | Detectable by pattern; no unique value |
863
864 | Mass-generating content without data variation | Amplifies the thin content problem at scale |
865
866 | Fabricating entity-specific facts | Hallucinations become trust/legal liabilities |
867
868 | Using AI to "rewrite the top 10 results" for a keyword | Produces content with zero information gain — just reshuffled common knowledge |
869
870 | Letting AI make strategic decisions (keyword selection, page type, whether to publish) | AI will confidently produce content for keywords where the user has no chance of ranking |
871
872 ### The role boundary
873
874 **AI is an execution tool, not a strategy tool for SEO.** AI should:
875
876 - ✅ Draft content from structured data the user provides
877 - ✅ Format, structure, and polish pages
878
879 - ✅ Generate meta descriptions, FAQ blocks, schema markup
880 - ✅ Help with technical implementation (SSR, sitemaps, caching)
881
882 AI should NOT:
883
884 - ❌ Decide which keywords to target
885 - ❌ Determine whether a page has enough information gain to publish
886
887 - ❌ Replace competitive analysis and intent matching
888 - ❌ Assess whether the user's site has enough authority to compete
889
890 - ❌ Generate "content" without underlying data that varies per page
891
892 When the user asks for "SEO content," the agent's first job is to ensure there is a real data source and strategic rationale — not to start generating pages.
893
894 ### Practical checklist before publishing AI-assisted content
895
896 - [ ] Does each page's AI output differ meaningfully from every other page? (run a diff on 5 random pages)
897 - [ ] Is the AI prose grounded in real data from the data source, or is it freestanding prose?
898
899 - [ ] Does each page contain at least one data point or insight not found in the current top 10 results?
900 - [ ] Could a human reviewer catch factual errors before publish? (build a review step for high-stakes verticals)
901
902 - [ ] Would a user who arrived from Google find this page more useful than the search results they came from?
903 - [ ] Has the page type been matched to the query intent (not just a blog post by default)?
904
905 If all answers are yes, the content is likely safe. If any is no, rethink the content strategy before scaling.
906
907 ---
908
909 ## Performance and Core Web Vitals for SSR Pages
910
911 pSEO pages are often deprioritized for performance work because they're "just content pages." But Core Web Vitals are a confirmed ranking factor, and a slow pSEO page competes against well-optimized competitors who target the same keyword.
912
913 ### The three metrics that matter
914
915 | Metric | Threshold | Common cause in pSEO |
916
917 |---|---|---|
918
919 | **LCP** (Largest Contentful Paint) | < 2.5s | Hero image without dimensions/preload; render-blocking CSS |
920
921 | **CLS** (Cumulative Layout Shift) | < 0.1 | Images without `width`/`height`; fonts causing FOUT |
922
923 | **INP** (Interaction to Next Paint) | < 200ms | Large JS bundles blocking the main thread |
924
925 ### For SSR Express pages specifically
926
927 #### Eliminate render-blocking resources
928
929 ```html
930
931 <!-- WRONG: blocks render until stylesheet loads -->
932
933 <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter" />
934
935 <!-- CORRECT: preconnect + async load -->
936
937 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
938
939 <link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Inter&display=swap" />
940
941 <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter&display=swap" media="print" onload="this.media='all'" />
942
943 ```
944
945 **Inline critical CSS** (the styles needed to render above-the-fold content):
946
947 - Inline it in `<style>`tags in`<head>` — eliminates one round trip
948 - Load remaining CSS asynchronously or omit it (pSEO pages rarely need large stylesheets)
949
950 **Images must have explicit dimensions** to prevent CLS:
951
952 ```html
953
954 <!-- WRONG: browser doesn't know height → layout shift on load -->
955
956 <img src="${hero}" alt="..." />
957
958 <!-- CORRECT -->
959
960 <img src="${hero}" width="1200" height="630" alt="..." loading="lazy" />
961
962 ```
963
964 **No JavaScript is ideal.** SSR content pages need zero client-side JS. If you must include JS (analytics, interactivity), load it `defer`or`async`.
965
966 ### Measuring pages not yet in Chrome UX Report
967
968 New pages have no real-user data (CrUX) for 28 days. Use:
969
970 - **Lighthouse** (Chrome DevTools or CLI): `lighthouse https://yoursite.com/glossary/term --only-categories=performance`
971 - **PageSpeed Insights API**: programmatic scoring for a batch of URLs
972
973 - **WebPageTest**: detailed waterfall view to identify bottlenecks
974
975 Run Lighthouse on a representative sample of pages (hub, high-volume spoke, low-volume spoke) — don't assume all pages perform equally.
976
977 ---
978
979 ## Monitoring and Iteration Loop
980
981 Shipping pSEO pages is not a one-time event. The standard pattern is: launch → wait 4-8 weeks → analyze → iterate. Most pSEO playbooks fail because teams skip the iteration step.
982
983 ### What to track in Search Console (per page type)
984
985 Open the **Performance** report, filter by URL prefix for each playbook (`/glossary/`,`/guides/`, etc.):
986
987 | Signal | Healthy | Problem indicator |
988
989 |---|---|---|
990
991 | **Impressions growing** | Steady week-over-week increase | Flat after 8 weeks → pages not indexed or no demand |
992
993 | **CTR** | >2% for informational, >5% for navigational | Low CTR → title/description not compelling |
994
995 | **Average position** | Trending toward <20 | Stuck at 30-50 → content quality or authority issue |
996
997 | **Coverage: Crawled, not indexed** | Decreasing | Increasing → thin content signal |
998
999 | **Coverage: Excluded (noindex)** | Matches intentional noindex count | Higher than expected → template accidentally sets noindex |
1000
1001 ### Decision framework per page set
1002
1003 ```text
1004
1005 8 weeks post-launch:
1006
1007 │
1008
1009 ├── Indexed AND getting impressions?
1010
1011 │ ├── YES, position <10 → monitor; optimize title/meta for CTR
1012
1013 │ ├── YES, position 11-30 → improve content depth, add internal links
1014
1015 │ └── YES, position >30 → thin content; merge with a stronger page or rewrite
1016
1017 │
1018
1019 ├── Not indexed (Crawled, not indexed)?
1020
1021 │ ├── Low page count (<100) → submit to Search Console for inspection
1022
1023 │ └── High page count (100+) → thin content; noindex worst pages, improve rest
1024
1025 │
1026
1027 └── Not even crawled?
1028
1029 ├── Check sitemap submission
1030
1031 ├── Check robots.txt for accidental blocks
1032
1033 └── Check crawl budget — too many thin pages may be crowding out good ones
1034
1035 ```
1036
1037 ### When to kill a playbook
1038
1039 Kill it (noindex + 301 redirect survivors to a hub) when after 6 months:
1040
1041 - Zero pages from the set are in the top 50 for any target keyword, AND
1042 - Impressions are flat or declining, AND
1043
1044 - The page type is consuming crawl budget (visible in crawl stats)
1045
1046 Keeping dead pages alive harms the rest of the site — Google distributes trust across the whole domain. A smaller set of high-quality pages consistently outperforms a large set of mediocre ones.
1047
1048 ### Iteration cadence
1049
1050 | Frequency | Action |
1051
1052 |---|---|
1053
1054 | Weekly | Check indexation coverage for errors |
1055
1056 | Monthly | Review impressions/CTR per playbook; update titles on underperformers |
1057
1058 | Quarterly | Full content audit — merge, improve, or kill underperforming page sets |
1059
1060 | On data change | Regenerate affected pages; update `dateModified` in schema |
1061
1062 ---
1063
1064 ## Quality Checks
1065
1066 ### Pre-Strategy (before writing any code)
1067
1068 - [ ] User has identified unique data source that varies meaningfully per page
1069 - [ ] Page type matches query intent (blog vs. comparison vs. tool vs. directory)
1070
1071 - [ ] Information gain validated: each page offers something the current top 10 results don't have
1072 - [ ] Competitive viability assessed: user's site has (or is building) topical authority in this area
1073
1074 - [ ] Starting with a small batch (10-20 pages) before scaling to hundreds
1075 - [ ] Human has made the strategic decisions; AI is handling execution only
1076
1077 ### Pre-Launch (technical and content)
1078
1079 - [ ] `curl` the URL and verify target keywords appear in raw HTML (not rendered by JS)
1080 - [ ] Each page provides unique value beyond variable substitution — if you removed the entity name, pages would NOT be indistinguishable
1081
1082 - [ ] Information gain test passed: 5 random pages each contain data/insights not in the current top 10 results
1083 - [ ] Answers search intent for the target keyword with the correct page type
1084
1085 - [ ] Unique titles and meta descriptions per page
1086 - [ ] Proper heading structure (one H1, logical H2/H3 hierarchy)
1087
1088 - [ ] Correct schema markup with no validation errors (test at schema.org/validator)
1089 - [ ] Page speed acceptable — LCP <2.5s, CLS <0.1, no render-blocking resources
1090
1091 - [ ] Images have explicit `width`and`height` attributes
1092 - [ ] Connected to site architecture (footer/nav links to hub pages; hub links to spokes)
1093
1094 - [ ] Breadcrumbs render correctly and are not obscured
1095 - [ ] Logo and branding match the main application exactly
1096
1097 - [ ] In XML sitemap and crawlable (no `noindex` accidentally set)
1098 - [ ] 404s return proper status codes (no pages for invalid slugs)
1099
1100 - [ ] Cache headers set appropriately per page type (live data: no-cache; static: max-age=86400)
1101 - [ ] Faceted/filtered URLs blocked via `robots.txt`or marked`noindex`
1102
1103 - [ ] Multilingual pages: hreflang tags present, bidirectional, with `x-default`
1104 - [ ] AI-assisted content: each page's output meaningfully differs from every other page
1105
1106 - [ ] `dateModified` in schema tied to actual data change, not deploy date
1107
1108 ### Post-Launch Monitoring
1109
1110 Track (per playbook URL prefix in Search Console): indexation rate, impressions, average position, CTR
1111
1112 8-week review: assess which page sets are indexed and gaining impressions, which are stuck, which are excluded. Apply the decision framework above.
1113
1114 Watch for: "Crawled, not indexed" count rising (thin content), crawl budget errors, manual actions, ranking drops after content updates.
1115
1116 ---
1117
1118 ## Common Mistakes
1119
1120 ### Strategic mistakes (most damaging)
1121
1122 - **Confusing scale with strategy**: Believing "more pages = more traffic" without validating that each page has information gain. This is the single most common pSEO failure mode in 2025+.
1123 - **SEO cosplay**: Pages with perfect technical structure (H1, H2, meta tags, schema) but zero information gain over existing results. Structure is table stakes, not a ranking advantage.
1124
1125 - **No information gain audit**: Publishing pages without checking whether they add anything the current top 10 results don't already have.
1126 - **Skipping intent matching**: Building blog posts when the query demands a comparison table, or building a product page when the query demands educational content. Wrong page type = won't rank regardless of quality.
1127
1128 - **Ignoring topical authority**: Publishing hundreds of pages in a domain where the site has no track record. Authority is earned through depth and trust signals, not page count.
1129 - **Letting AI make strategic decisions**: AI generates confidently even when the keyword is unwinnable, the page type is wrong, or the content has no unique value. Human judgment is required for: keyword selection, page type, competitive viability, and publish/no-publish decisions.
1130
1131 - **Treating AI output as finished content**: AI drafts need human enrichment — original data, case studies, expert judgment, first-hand experience. Raw AI output is a starting point, not a final product.
1132
1133 ### Content mistakes
1134
1135 - **Thin content**: Just swapping entity names in identical boilerplate (Google will deindex)
1136 - **AI prose with no data variation**: All pages share the same generic paragraphs — amplifies thin content at scale. If you removed the entity name, pages would be indistinguishable.
1137
1138 - **Rewriting the top 10 results**: Using AI to rephrase what already ranks is zero information gain. Google doesn't need another version of existing content.
1139 - **Keyword cannibalization**: Multiple pages targeting the same keyword
1140
1141 - **Over-generation**: Creating pages with no search demand
1142 - **Poor data quality**: Outdated or incorrect information erodes trust
1143
1144 ### Technical mistakes
1145
1146 - **SPA without SSR**: Adding pSEO pages to a React/Vue app without server-rendering — Google sees nothing
1147 - **Orphan pages**: New SEO pages not linked from anywhere in the main site's navigation or footer
1148
1149 - **Inconsistent branding**: SSR pages that look different from the main app break user trust
1150 - **Breadcrumb obscured by layout tricks**: Negative margin patterns hiding breadcrumb navigation
1151
1152 - **Wrong cache headers**: Live-data pages cached for 24h showing stale rates; static pages uncached causing unnecessary server load
1153 - **Asset permission errors**: Referencing frontend assets from SSR routes that can't read them
1154
1155 - **Machine-translating pSEO content at scale**: Produces near-duplicate content across locales; triggers spam signals
1156 - **Missing or asymmetric hreflang**: One page in the set not listing all variants breaks the entire locale cluster
1157
1158 - **Bumping `dateModified` without real content changes**: Deception signal; Google can detect and penalise it
1159 - **Faceted navigation URLs not blocked**: Filter/sort param combinations create thousands of near-duplicate crawlable URLs, wasting crawl budget
1160
1161 - **No crawl budget monitoring**: Teams discover Googlebot stopped crawling new pages only after rankings plateau
1162 - **Skipping Core Web Vitals on pSEO pages**: Slow LCP or high CLS on "just content" pages loses rankings to faster competitors
1163
1164 - **Images without dimensions**: Causes CLS on SSR pages (browser doesn't know reserved height until image loads)
1165
1166 ### Process mistakes
1167
1168 - **Blasting hundreds of pages at once**: Instead of starting with 10-20 pages, validating indexation and impressions, then scaling what works
1169 - **No iteration loop**: Shipping pSEO and never reviewing Search Console — dead pages accumulate and drag down domain trust
1170
1171 - **No freshness strategy for live-data pages**: Exchange rates or prices cached for 24h showing stale data destroys credibility
1172 - **Ignoring UX**: Pages that exist for Google but not for users — no conversion path, no value to visitors
1173
1174 ---
1175
1176 ## Output Format
1177
1178 Deliver strategy validation first, then implementation. **Do not skip straight to building pages.**
1179
1180 1. **Strategic validation** (before any code):
1181
1182 - What unique data does the user have that varies per page?
1183 - What page type matches the query intent?
1184
1185 - What information gain does each page offer over current top 10 results?
1186 - Does the user's site have topical authority in this area?
1187
1188 - Recommended initial batch size (start small, validate, then scale)
1189
1190 1. **Rendering audit**: Confirm whether the app is a SPA and what SSR approach to use
1191 2. **Strategy**: Opportunity analysis, chosen playbook(s), keyword patterns, data sources, page count estimate
1192
1193 3. **Shared infrastructure**: `ssrShared.ts` (or equivalent) HTML shell before any individual pages
1194 4. **Template**: URL structure, title/meta templates, content outline, schema markup
1195
1196 5. **Implementation**: Working server-rendered pages that return full HTML to `curl`
1197 6. **Integration**: Footer/nav updates in the main app linking to the new hub pages
1198
1199 7. **Validation plan**: How to verify pages are indexed, earning impressions, and when to scale or kill the campaign
1200
1201 ## Limitations
1202
1203 - Cannot access Search Console data to monitor indexation
1204 - Cannot check existing backlink profiles
1205
1206 - Data quality depends on the source — always validate before publishing
1207 - Cannot guarantee rankings — SEO involves many factors beyond on-page optimization