SEOAstroCloudflareprogrammatic SEO

How we shipped 161 programmatic SEO pages on Astro + Cloudflare in one afternoon

The exact stack, data model, and template pattern behind LogicsLevel's own local pages — you can copy it for any service business.

SEO analytics dashboard

This site — the one you’re reading — has 161 programmatic local pages, one per US state, US metro, Canadian province/city, Australian state/city, and NZ region/city we serve. Each one has genuinely different copy: named local business anchors, districts, buyer verticals, search phrases, and FAQs.

The whole system was built in one afternoon. Here’s the exact recipe.

The stack

  • Astro 5 — static-first, zero JS by default. Content Collections + getStaticPaths() render one HTML file per location entry.
  • Cloudflare Pages — hosts every file at edge, free tier, unlimited bandwidth.
  • TypeScript data modules — one file per country (src/data/local-content-us-metros.ts, etc.) mapping each slug to a research object.
  • @astrojs/sitemap — auto-generates sitemap-index.xml from the file tree.

Total infra cost at expected volume: $0/month.

The data model

Every location entry has the same shape:

export interface LocationContent {
  audience: { name: string; reason: string; tools: string[] }[]; // 3-4 verticals
  districts: string[];             // 2-3 named business clusters
  searchPhrases: string[];         // 4-6 real search variants
  opportunity: string;             // 1-sentence competitor-gap
  hook: string;                    // hero eyebrow line
  faqs: { q: string; a: string }[]; // 2-3 truly local Q&A
  imageUrl: string;
  imageAlt: string;
}

The reason field is the difference between a doorway page and a real programmatic page. Every vertical cites a real local anchor: “Wichita ‘Air Capital’ → Textron / Spirit AeroSystems / Bombardier Learjet Tier-1/2 supplier ecosystem”. Not “healthcare and manufacturing” for every state.

The template

One .astro file (src/components/LocalPage.astro) renders every location. Four [slug].astro files (one per country) call getStaticPaths() from the data module. That’s it.

The template cycles the H1 pattern by slug hash so pages differ structurally — the URL determines which of four heading variants gets used, deterministically, so search engines see stable titles.

The uniqueness discipline

Google’s helpful-content passes penalise thin programmatic pages that look identical. Rules we followed:

  • ≥ 600 words genuinely different copy per page — not template with {city} swapped.
  • One truly local element per page — a named district, an industry cluster with real companies, a regulatory quirk.
  • Per-page JSON-LDService schema with areaServed, not LocalBusiness (which implies a physical address).
  • Sibling + parent internal links — every page links to 3–4 neighbouring cities and its region hub.

The research

Deep-researching 161 locations manually would take weeks. We used Claude in a parallel-fork pattern: four AI research agents, one per country, each writing a TypeScript module with per-location content. Total tokens spent: ~2M. Total wall-clock: 40 minutes.

We audited every entry after generation — no invented statistics, no false company associations. Every reference is grounded in a real business district or industry cluster.

The result

Every page has genuinely distinct buyer-intent copy. Local search phrases that a real prospect would type. FAQs that answer questions specific to that geography. The template makes structural variation automatic; the data module carries the substantive variation.

Want the same on your site? Our SEO & Programmatic Indexing service ships this exact pattern — from $299/month. Or if you want us to build the whole marketing site on this stack, that’s /services/website-build from $897.

Ready to move?

Book a 20-minute discovery call. Bring your current CRM. Leave with a fixed price and a delivery date.