Skip to content

SEOPart of: Canonical tags

Faceted navigation and parameter URLs: keeping filters out of the index

Filters, sort options and tracking tags turn one category into thousands of URLs. Learn which parameter URLs to index, canonicalise, noindex or block, how to handle utm and click ids, and how to see what Googlebot crawls.

getReport teamUpdated 27 Sept 202613 min read

A category page with filters for colour, size, price and a sort order is one page to a visitor and thousands of URLs to a crawler: every combination of every value is a new address, and every campaign link adds a ?utm_source= variant on top. Left alone, Googlebot spends its visits on ?colour=red&size=42&sort=price while new products wait, and the index fills with near-duplicates that compete with the category itself. This guide is part of canonical tags explained: it gives a decision per parameter type, the three controls and what each does and does not do, how to keep tracking parameters harmless without breaking analytics, and how to see what Googlebot really crawls. Plan half a day for the decisions, and a deploy for the fixes.

Quick answer

Parameter typeExampleWhat to do
Sort, view, items per page?sort=price, ?view=grid, ?per_page=48Canonical to the base URL; optionally block in robots.txt
Pagination?page=3Keep crawlable and self-canonical; do not canonical to page 1
Single filter with search demand/shoes/red/ or ?colour=redMake it a real landing page: unique title, H1, text, self-canonical, in the sitemap
Multi-select combinations?colour=red&size=42&brand=xnoindex, or canonical to the single-facet page; block crawling once the good pages are indexed
Tracking parameters?utm_source=, ?gclid=, ?fbclid=Canonical to the clean URL; keep crawlable; ignore in the page cache; never redirect them away
Session ids?sessionid=, ?PHPSESSID=Move sessions to a cookie

Rules that hold everywhere: one parameter order, one value spelling, rel="canonical" on every variant, and never both noindex and a robots.txt block on the same URL, because a blocked page cannot be read and so cannot be de-indexed.

Why parameter URLs matter

Crawl waste. Google crawls a site at a rate based on how fast it answers and how useful its pages have been. A shop with 200 categories, five facets of ten values each and four sort orders has, in theory, tens of millions of URLs. Googlebot does not crawl them all, but every fetch spent on a sort variant is one not spent on a new product or an updated price.

Duplicate content. /shoes/?sort=price shows the same products as /shoes/, in a different order. Google usually picks one, but the pick is not always yours, and each duplicate splits the signals that should go to the category. On a large site the "Duplicate without user-selected canonical" row in Search Console is where that shows.

Index bloat. Thousands of thin filter pages in the index produce results nobody wants: a visitor searching for red shoes lands on ?colour=red&size=36&sort=newest with two products. Google's documentation on managing crawling of faceted navigation URLs is direct: if you do not need filtered URLs in search, keep them from being crawled with robots.txt or by using URL fragments.

The opposite mistake exists too. Some facets are queries people type: "red running shoes", "size 46 boots". A single-facet page with real content is a landing page for a query the category alone cannot answer. Blocking every filter throws those away.

How getReport checks it

Run the audit on three URLs: the plain category, one single-facet URL you want indexed, and one multi-facet URL you do not. The three SEO panels side by side tell you whether the controls do what you decided:

The SEO audit panel for a filtered category URL: the canonical, noindex, robots.txt and URL length findings with their status
The findings for a filtered category URL: canonical, noindex, robots.txt and URL length in one panel.

The first reads the <link rel="canonical"> on the final URL; the second tells you where it points and whether that target answers 200. A canonical on ?sort=price should point at the category, and a canonical on /shoes/red/ at itself.

On the multi-facet URL you decided to keep out, this finding should fail: that is the confirmation that the noindex is in place. On the single-facet landing page it must pass. It reads both the meta tag and the X-Robots-Tag header.

Reports the robots.txt line that blocks the URL. Run it on the multi-facet URL after adding a Disallow rule to confirm the pattern matches, and on the category and the single-facet page to confirm it does not.

Warns above 100 characters. Multi-facet URLs get there fast, which is a hint that the URL should not be indexable, or that the single-facet form should be a short path.

One thing to know about tracking parameters: before any report runs, getReport removes utm_*, the ad click ids (gclid, gclsrc, dclid, fbclid, msclkid, ttclid, twclid, igshid, yclid, wbraid, gbraid), email tags such as mc_cid, mc_eid, _hsenc and _hsmi, and ref, source, _ga and _gl from the address you paste, so it tests the page, not the tag. To see how your site treats a parameter, add one it does not strip, such as ?campaign-test=1, and read the canonical row in the canonical checker.

Step by step

1. List the parameters

Click every filter, sort and view control on a category and write down each parameter name that appears in the address bar. Then load a day of access logs into the log file analyser: the parameter-URL share of Googlebot's hits and the most-fetched URLs show which parameters the crawler actually spends time on, often one you forgot (an old ?replytocom=, an ?add-to-cart=).

2. Classify each one

Use the table in the quick answer. The only judgement call is which single facets have search demand. Look at the queries the category already ranks for in Search Console; if "red shoes" and "shoes size 42" appear, those facets deserve pages. Brand and material usually qualify; sort order, price ranges and page size never do.

3. Fix the parameters themselves

Before adding any control, make the URLs consistent, or the controls multiply:

  • One order. ?colour=red&size=42 and ?size=42&colour=red are two URLs. Generate them in a fixed order.
  • One spelling. red, Red and RED are three URLs. Lower-case values.
  • No empty parameters and no defaults. ?colour=&size=42 and ?sort=relevance are extra URLs for pages that already exist.
  • No empty results. Google's guidance is to return a 404 for a filter combination with no products rather than a 200 page saying "no results".

Filters that use URL fragments (/shoes/#colour=red) do not create new URLs for the crawler, which is one of the approaches Google recommends when you do not want facets crawled at all.

4. Canonical: for sorts, views and tracking tags

Every variant that shows the same set of products as another URL gets a canonical pointing at that URL:

HTML
<!-- on /shoes/?sort=price, /shoes/?view=list and /shoes/?utm_source=newsletter -->
<link rel="canonical" href="https://example.com/shoes/">

The canonical is a strong hint, not a command, but for variants whose content matches, Google follows it reliably. Google's guidance adds that a canonical reduces crawling of the variants only slowly, over time. Pagination is the exception: ?page=2 shows different products, so it is self-canonical. Pagination after rel=prev/next covers the details.

5. noindex: for combinations that must stay crawlable for now

Multi-select combinations show a unique product set, so a canonical to the category is a hint Google may ignore. noindex is the honest control: the page is crawled, its product links are followed, and it stays out of the index.

HTML
<!-- on any URL with two or more facet parameters -->
<meta name="robots" content="noindex, follow">

noindex does not save crawl budget, because Google has to fetch the page to read it. It is the right tool while single-facet pages are still being discovered, and the wrong tool as the only control on a very large shop.

6. robots.txt: for what should not be crawled at all

Once the pages you want indexed are linked from the category and listed in the sitemap, block the rest. Google's robots.txt parser supports * and $; the patterns must allow the single-facet pages and block the combinations:

Text
# /robots.txt
User-agent: *
# sort, view and page-size variants
Disallow: /*?*sort=
Disallow: /*?*view=
Disallow: /*?*per_page=
# two or more facets in one URL
Disallow: /*?*colour=*&size=
Disallow: /*?*size=*&colour=
Sitemap: https://example.com/sitemap.xml

Do not add tracking parameters here: a blocked ?utm_source= URL is never fetched, so Google never sees its canonical, and the URL can still be indexed from links as a bare address. For facets the order is: noindex first, wait until the combinations have dropped out (Search Console's Pages report shows it), then block. The robots.txt patterns guide has the matching rules.

7. Build the single-facet pages properly

A filter page that should rank needs what any landing page needs: a short path (/shoes/red/ rather than ?colour=red), a unique title and H1 ("Red shoes", not "Shoes"), a paragraph a visitor would read, a self-canonical, internal links from the category and a sitemap entry. Without those it is a duplicate with a nicer URL.

Tracking parameters: utm, gclid and fbclid

Campaign tags are a different problem from filters: the page is identical, and the parameter exists only for analytics. You add utm_source, utm_medium and utm_campaign to links in emails and ads; ad platforms append gclid (Google Ads), fbclid (Facebook and Instagram), msclkid (Microsoft Ads) or ttclid (TikTok) on their own. Search Console's URL Parameters tool, which once let you declare such parameters, was retired in 2022; canonicals and clean linking are the whole toolkit now.

  • Canonical from the path, never from the request. Yoast, Rank Math, Shopify, Squarespace and Webflow build the canonical from the permalink, so parameters never appear in it. A hand-built template needs the query string removed:
PHP
<?php
// Canonical from the path only, never from the query string
$path = strtok($_SERVER['REQUEST_URI'], '?');
?>
<link rel="canonical" href="<?= htmlspecialchars('https://example.com' . $path, ENT_QUOTES) ?>" />
  • The page cache should ignore them. A cache keyed by the full URL treats every campaign click as a new, cold page, which is exactly the hour your server should be fastest. WP Rocket ignores common tracking parameters by default; in LiteSpeed Cache, list them under Cache → Advanced → "Drop Query String" (utm*, fbclid, gclid, msclkid); on nginx, build the cache key from a map that empties tracking-only query strings.
  • Never redirect them away. A server redirect from the tagged URL to the clean one strips the tags before GA4 or the Meta pixel can read them, and the campaign disappears from your reports. If you want a tidy address bar, remove the tags with history.replaceState after the load event, once the analytics scripts have run.
  • Never tag internal links. utm_campaign=homepage-banner on a link between your own pages overwrites the visit's real source. Track internal promotions with click events instead.

Platform notes

WordPress and WooCommerce

WooCommerce produces ?orderby=price, ?min_price=10&max_price=50 and, with the layered navigation widget, ?filter_colour=red&query_type_colour=or. Yoast SEO and Rank Math canonicalise sorted and filtered URLs to the base category; check one in the report to confirm your theme prints it. For noindex on combinations:

PHP
// Add noindex on product category URLs that carry two or more filter parameters.
add_action('wp_head', function () {
    if (!is_product_category()) return;
    $facets = array_filter(array_keys($_GET), fn ($k) => str_starts_with($k, 'filter_'));
    if (count($facets) >= 2) {
        echo '<meta name="robots" content="noindex, follow">' . "\n";
    }
}, 1);

?add-to-cart=123 links on category pages perform an action; block them in robots.txt (Disallow: /*?*add-to-cart=) and never cache them.

Shopify

Collection filters use ?filter.v.option.color=Red, ?filter.v.price.gte=10 and ?filter.p.vendor=Acme; sorting uses ?sort_by=price-ascending. Check what the theme's canonical prints on a filtered URL with the canonical checker. Shopify's robots.txt is editable through the robots.txt.liquid template. Single-facet landing pages are best built as separate collections (/collections/red-shoes) with their own description, since filter URLs cannot carry unique text. Canonicals ignore tracking parameters, and Shopify's CDN handles caching.

Magento

Layered navigation produces ?color=52&price=10-20. Under Stores → Configuration → Catalog → Catalog → Search Engine Optimization, "Use Canonical Link Meta Tag For Categories" makes every filtered URL canonical to the category. Combine it with robots.txt rules for attribute parameters you never want crawled.

How to check it worked

  • A sort, view or ?campaign-test=1 URL shows a canonical pointing at the clean category, and indexable-status on the category passes.
  • A multi-facet URL fails robots-meta-noindex (intended) or, after the block, robots-txt-blocks-page with the line you wrote; never both at once.
  • A single-facet landing page passes canonical, noindex and robots.txt, and is in the sitemap.
  • Two weeks later, the log analyser shows Googlebot's parameter-URL share falling. A sample of filter URLs in the bulk URL checker shows noindex on combinations and the category as canonical for sorts.
  • A test campaign link still shows up under its campaign in analytics.

Common mistakes

  • Blocking everything with Disallow: /*?. It also blocks pagination, tracking variants and the single-facet pages you wanted. Write patterns per parameter.
  • noindex and a robots.txt block on the same URLs. The block prevents the tag from being read. noindex first, block later.
  • Canonical from a combination page to the category. The product sets differ, so Google may ignore it. Use noindex for combinations.
  • Relying on nofollow on filter links. Google's faceted navigation guidance calls it less effective in the long run than robots.txt or fragments; crawlers still find the URLs.
  • A canonical that echoes the tracking parameter, from a template built on the request URL. Build it from the path.
  • Canonicalising page 2 to page 1. Products on later pages lose their only crawl path.
  • Fixing the controls but not the URL generation. Two parameter orders and mixed case double every rule. For the URL side, see URL length and structure.
Check your site before and after Check