February 14, 2026

Turning WooCommerce Filters Into Landing Pages Google Actually Indexes

Faceted navigation is an SEO minefield. Here's how I turned a store's product filters into rankable landing pages — dynamic titles from the URL, and strict index control.

  • WooCommerce
  • Technical SEO
  • PHP
Illustration of filter result pages, some indexed with a check, some blocked

Faceted navigation is a trap and an opportunity

Every WooCommerce store with attribute filters — size, color, brand, season — is sitting on the same double-edged sword. Those filters generate URLs, and URLs are pages, and pages can rank. A tire store’s “winter / 205 / 55 / R16” filter combo is exactly what a buyer types into Google. That’s the opportunity.

The trap: filters combine. Four attributes with ten options each is ten thousand URLs, most of them near-duplicates of each other, all competing for the same rankings and burning your crawl budget. Left unmanaged, faceted navigation doesn’t help your SEO — it dilutes it into oblivion.

The whole game is: make the valuable combinations into real landing pages, and stop the rest from existing in the index. Here’s how I did it on a real store.

1. Build the title and meta from the filter state

A filtered URL should never show the generic shop <title>. Read the active filters and construct a snippet that matches the search intent:

function custom_title( $title ) {
    $uri = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
    if ( '/shyny/' !== $uri ) return $title;

    $parts = [];
    if ( ! empty($_GET['filter_vyrobnyk']) ) {
        $parts[] = sanitize_text_field($_GET['filter_vyrobnyk']);       // brand
    }
    if ( ! empty($_GET['filter_sezon']) ) {
        $map = ['zymovi' => 'зимові', 'litni' => 'літні', 'vsesezonka' => 'всесезонні'];
        $parts[] = $map[ $_GET['filter_sezon'] ] ?? '';                 // translate the slug
    }
    // ...width, profile, diameter...

    $title['title'] = $parts
        ? 'Шини ' . implode(' ', $parts) . ' — купити в Україні'
        : 'Шини — купити в Україні';
    return $title;
}
add_filter( 'document_title_parts', 'custom_title' );

Two details matter more than they look. Translate the slugszymovi is a URL token, «зимові» is what a human searches. And keep a sensible fallback for the bare page, so an un-filtered view still has a good title.

2. Give the good combinations pretty URLs

Query strings work but read as junk. A few rewrite_rules_array rules turn them into clean, hierarchical paths that users and crawlers trust:

add_filter( 'rewrite_rules_array', function( $rules ) {
    return [
        'shyny/([^/]*?)/page/([0-9]+)/?$' => 'index.php?product_cat=$matches[1]&paged=$matches[2]',
        'shyny/([^/]*?)/?$'               => 'index.php?product_cat=$matches[1]',
    ] + $rules;
});

3. Control the index — the part everyone skips

This is where most faceted-SEO attempts fall apart. My rule was blunt and effective: too many filters at once = noindex.

function add_noindex_meta_tag() {
    if ( preg_match_all('/filter_/', $_SERVER['REQUEST_URI']) >= 4 ) {
        echo '<meta name="robots" content="noindex, nofollow" />' . "\n";
    }
}
add_action('wp_head', 'add_noindex_meta_tag');

Three-or-fewer-filter combinations are the sweet spot — specific enough to match a real search, broad enough to have inventory and not be a duplicate of ten neighbors. Beyond that, they get blocked.

The inverse also matters: for the specific landing pages I did want ranking, I stripped any stray noindex an interacting plugin had added, at the output-buffer level, so nothing accidentally deindexed a page I’d chosen to promote:

add_action('template_redirect', function () {
    if ( strpos($_SERVER['REQUEST_URI'], '/ru/shynu/') !== false ) {
        ob_start(function ($html) {
            return preg_replace(
                '/<meta[^>]+name=["\']robots["\'][^>]*content=["\'][^"\']*noindex[^"\']*["\'][^>]*>/i',
                '', $html
            );
        });
    }
});

Output buffering to rewrite the final HTML is a heavy hammer — but when three plugins (WooCommerce, the filter plugin, the SEO plugin) each think they own the <head>, it’s sometimes the only place you can get the last word.

4. Don’t forget pagination and canonicals

Paginated category pages are duplicate-title machines. Append the page number to the title and meta, emit rel="prev"/rel="next", and canonicalize the plain shop page to itself so filtered variants don’t split its signals. Unglamorous, necessary.

The principle

Faceted-navigation SEO isn’t one feature — it’s a policy: which of the pages my site can generate are allowed to exist in Google’s index, and what does each one say about itself? WooCommerce and its plugins generate the pages; they don’t answer that question. The answer is custom code sitting in the seam between them — a few hundred lines that decide, per URL, “index this and call it winter tires 205/55 R16” or “block this, it’s the ten-thousandth near-duplicate.” Get that policy right and your filters stop being a liability and start being your entire long-tail strategy.