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.
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.xmlfrom 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-LD —
Serviceschema withareaServed, notLocalBusiness(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.