# Service-area pages in WordPress without thin, duplicated content

A plumber in Dallas who also serves Plano and Irving. An accountant in Leeds with clients in Bradford and York. A cleaning company covering Toronto, Mississauga and Oshawa. They all want a page for each area, and most WordPress sites handle it badly: one template, a city name swapped in, and twenty near-identical pages that Google ignores.

Here is how we build service-area pages in WordPress so each one earns its place.

## 1. Decide which areas deserve a page

A page per suburb isn't a strategy. Start with areas where the business genuinely does work and can say something specific:

- jobs completed there, with photos
- staff or vans based nearby
- local rules that change the work (permits, building codes, council requirements)
- reviews from customers in that area

If you can't write three specific facts about an area, leave it on a single "Areas we serve" page instead.

## 2. Model areas as a custom post type

Areas are content, not settings, so give them their own post type. This keeps URLs clean (`/areas/plano/`) and lets editors manage them without touching the theme.

```php
add_action( 'init', function () {
    register_post_type( 'service_area', [
        'label'        => 'Service areas',
        'public'       => true,
        'has_archive'  => 'areas',
        'rewrite'      => [ 'slug' => 'areas', 'with_front' => false ],
        'show_in_rest' => true, // needed for the block editor
        'supports'     => [ 'title', 'editor', 'excerpt', 'thumbnail', 'custom-fields' ],
        'menu_icon'    => 'dashicons-location',
    ] );
} );
```

Flush permalinks once after adding it (Settings, Permalinks, Save).

## 3. Store the facts as fields, not prose

Put the area-specific data in fields so the template can display it consistently and you can see at a glance which pages are thin:

```php
add_action( 'init', function () {
    foreach ( [ 'area_region', 'area_jobs_count', 'area_local_note' ] as $key ) {
        register_post_meta( 'service_area', $key, [
            'type'          => 'string',
            'single'        => true,
            'show_in_rest'  => true,
            'auth_callback' => fn() => current_user_can( 'edit_posts' ),
        ] );
    }
} );
```

`area_local_note` is the important one: one or two sentences only a local would know, such as "Most homes in older parts of Irving still have galvanised pipes, so we check pressure before any fixture swap."

## 4. Build the template with a block pattern

In a block theme, create `templates/single-service_area.html` and lay it out with blocks. Use a pattern for the repeatable parts (services list, call-to-action) and leave the main content to the editor. The page should open with a short, specific answer: what you do in this area and how fast you get there.

Avoid auto-generating the body text. Templates should handle layout. People should write the words.

## 5. Add LocalBusiness schema once, and areaServed per page

Your business has one address. Don't invent a new one per city. Output `LocalBusiness` with the real address site-wide, and on each area page add `areaServed`:

```php
add_action( 'wp_head', function () {
    if ( ! is_singular( 'service_area' ) ) return;
    $data = [
        '@context'   => 'https://schema.org',
        '@type'      => 'Service',
        'name'       => get_the_title() . ' services',
        'areaServed' => [ '@type' => 'City', 'name' => get_the_title() ],
        'provider'   => [ '@type' => 'LocalBusiness', 'name' => get_bloginfo( 'name' ), 'url' => home_url( '/' ) ],
    ];
    echo '<script type="application/ld+json">' . wp_json_encode( $data ) . '</script>';
} );
```

## 6. Link areas from real places

Link each area page from the "Areas we serve" archive and from relevant case studies or blog posts. A page reached only from a footer list of twenty cities looks like a doorway page.

## 7. Check for thin pages every quarter

A simple WP-CLI check flags pages with too little unique content:

```bash
wp post list --post_type=service_area --fields=ID,post_title --format=csv \
| tail -n +2 | while IFS=, read -r id title; do
  words=$(wp post get "$id" --field=post_content | wp eval 'echo str_word_count( wp_strip_all_tags( file_get_contents("php://stdin") ) );')
  echo "$title: $words words"
done
```

Anything under about 250 words with no local note should be improved or merged.

## FAQ

**Is a plugin better than a custom post type?**
Plugins that generate hundreds of location pages from one template produce exactly the thin content Google filters out. A custom post type with hand-written content is safer.

**Should area pages target "near me" searches?**
No. "Near me" results depend on the searcher's location and your Google Business Profile, not on the words "near me" in your page.

**How many area pages should a small business have?**
Usually three to ten: the places where you have real jobs, reviews and something specific to say.

---

We design and build WordPress sites for service businesses in the USA, UK, Canada and Australia, including service-area structures like this. See how we approach [WordPress website design and development at Dzimark](https://dzimark.com/services/wordpress-development/).
