Skip to content

Programmatic SEO — Multi-Location Sites

For contractor and home-services clients operating across multiple cities, use Astro’s static route generation to build a dedicated page per city × service combination.

src/
├── data/
│ ├── cities.json # [{slug, name, region}, ...]
│ └── services.json # [{slug, name, description}, ...]
└── pages/
└── [city]/
└── [service].astro # getStaticPaths() = cartesian product
/harare/plumbing/
/harare/roofing/
/bulawayo/plumbing/
/bulawayo/roofing/

Static URLs only — dynamic URLs crawl worse and rank lower.

---
import { getCollection } from 'astro:content';
import cities from '../../data/cities.json';
import services from '../../data/services.json';
export async function getStaticPaths() {
return cities.flatMap(city =>
services.map(service => ({
params: { city: city.slug, service: service.slug },
props: { city, service },
}))
);
}
const { city, service } = Astro.props;
---

Astro v5 generates 10,000 static pages in under 60 seconds. Zero performance penalty at scale.

Each location page must include:

  • Local landmarks and neighborhoods
  • Local team member names (if applicable)
  • Community involvement (local sponsorships, events)
  • Region-specific service details (local regulations, climate considerations)
  • Unique testimonials from local customers

Documented impact: 107% ranking lift with hyperlocal vs generic content (AgencyAnalytics, 2025). Real case: contractor expanded to 10+ locations → 500% organic visibility increase, 400%+ phone calls YoY.

  • Hybrid model: centralized brand content + location-specific blog posts
  • Generic blog republished across all locations = weak Google signal
  • Use mos-seo-content parameterized with city/service slots to generate per-location content
  • Add LocalBusiness schema with areaServed on each [city]/[service] page
  • Separate Google Business Profile per location (not one shared profile)
{
"@type": "LocalBusiness",
"name": "Acme Plumbing Harare",
"areaServed": {
"@type": "City",
"name": "Harare"
},
"address": {
"@type": "PostalAddress",
"addressLocality": "Harare",
"addressCountry": "ZW"
}
}

Package as a distinct offering — separate from the standard site build:

TierScopePrice range
StarterUp to 3 cities × 5 services = 15 pagesAdd $500–$800 to base build
GrowthUp to 10 cities × 10 services = 100 pagesAdd $1,500–$2,500
Scale50+ cities, AI-generated per-location contentCustom, $5,000+