Skip to content
Searchpedia SEO field notes Callum Bennett Callum

Site ops

Faceted Navigation SEO

I now treat faceted navigation as a crawl budget problem first and a content problem second, because every filter combination that gets indexed is a page I have to defend.

Beginner3 min readUpdated 2026-07-27Notes by Callum Bennett

What I’d do first

  • Audit your faceted URL parameters and decide which ones produce unique, valuable pages worth indexing.
  • Use robots.txt to block all low-value facet combinations from being crawled.
  • Implement self-canonical tags on every facet page you do want indexed.
  • For AJAX filtering, use pushState to update the URL hash so states are shareable without creating crawlable duplicates.
  • Only include canonical facet pages in XML sitemaps, never parameter variations.

The path I'd take

I'd start by mapping every filter parameter my site uses. Colour, size, price range, brand — each one multiplies the URL space. For a clothing site with 10 colours, 5 sizes, and 5 price brackets, that is 250 combinations per category. Most of those will never earn a visit. I run a crawl on a representative category, look at the parameter URLs that are indexable, and tally how many have organic traffic in Search Console. Usually fewer than 5% of the combos drive any visits. Those are the ones I keep.

Next I set my crawl control strategy. I use [robots.txt](/robots-txt/) to block all parameter URLs that I do not want indexed. Google's guidance says you can block faceted URLs with robots.txt when they do not need to be indexed. I create a rule that disallows any URL with a query string except for a whitelist of parameters I have identified as valuable. For example, I allow only ?category= and ?colour= because those produce useful landing pages. Everything else gets blocked.

Then I add self-canonical tags on every page I want indexed — even the category page itself. For the valuable facet combinations, I make sure they have a unique title and meta description, and enough descriptive text to justify indexation. For the rest, I set <meta name="robots" content="noindex, follow"> so crawlers can still discover products through those links but won't waste index space. This is a layered approach that combines [canonical tags](/canonical-tag/) and selective noindex.

Finally, I check the URL structure. I prefer clean parameter names like ?colour=red over ?c=1 because readability helps both users and crawlers. For [technical SEO](/technical-seo/) audits, I also verify that the site handles empty facet states correctly. If the site uses AJAX filtering, I configure pushState to update the URL hash only — Googlebot does not crawl hash fragments, so that avoids duplicate URLs while preserving user shareability.

This approach reduced indexed pages by roughly 80% on an e-commerce site I worked on, and crawl budget shifted to product pages that actually convert. It is a deliberate trade-off: fewer indexed facet pages, but each one has a fighting chance to rank.

Watch-outs

The biggest mistake I see is mixing signals. Do not add a canonical tag pointing to a different URL and a noindex on the same page — they conflict and Google might ignore both. Pick one: either self-canonical if the page is good, or noindex if it is thin.

Do not include parameter URLs in XML sitemaps. Only the canonical version of a facet page should appear there. Including every colour and size variation pollutes the sitemap and tells Google those pages are more important than they are. Sitemaps are for your best content, and parameter variations almost never qualify.

Another watch-out: empty results. If a facet combination yields no products, return a 404 status code. Do not redirect to a generic not-found page, and do not serve a templated "no results" page that gets indexed. I saw a site that had thousands of indexable empty pages because they returned 200 with a message. Each one wasted crawl budget and looked thin to Google. This is also a [duplicate content](/duplicate-content/) risk if the message is the same across many URLs.

Also watch out for canonical tags pointing to the parent category page when the facet page has different content. If you canonical a ?colour=red page to the root category, Google may not index the red-specific content, so you lose any keyword potential for "red [product]". Instead, use self-canonical only when you want that page indexed.

Finally, monitor crawl stats in Search Console. After implementing controls, the number of pages crawled per day should drop, and the ratio of crawled to indexed pages should improve. If you see no change, your robots.txt rule might be too permissive. For sites with [pagination](/pagination/) alongside facets, the interaction between pagination parameters and filter parameters can create even more URL chaos — those need separate handling.

What I got wrong

I used to think more pages meant more traffic. I let every facet combination be indexable — colour, size, brand, every possible filter. On a site with 200 categories, each with 6 parameters, that generated about 1.2 million URLs. I thought I was creating a long tail of landing pages. Instead, I watched crawl budget plummet: Google crawled my product pages less because it was busy crawling ?colour=blue&size=m which never got a single click. The site had been live for two years, and those pages had not earned a single organic visit.

I also made the mistake of using rel="canonical" on all facet pages back to the parent category. That stopped the duplicate content issue, but it also meant Google never discovered the unique content I had written for some of those facet pages. I lost rankings for terms like "blue running shoes" because the canonical URL was just /running-shoes. I now understand that canonical tags are not a silver bullet — they tell Google which URL to show, not which content to index.

Now I audit facet pages monthly. I use Search Console to find parameter URLs that have impressions but no clicks — those are opportunities to either optimise and keep, or noindex and save budget. I also check for any facet page that has more than a few hundred visits; I add unique content and a self-canonical to make it a proper landing page. I have even found a few facet combinations that, once optimised, outranked the parent category for specific queries.

One thing I still struggle with: dynamic parameters from JavaScript. Many e-commerce frameworks append parameters via JS without updating the URL properly. That creates a gap where Google sees the same URL for different filter states, but the content changes. I have not found a perfect solution yet, but using URL fragments and pushState helps. It is a trade-off between user experience and crawlability, and I have not settled on a single rule yet.

Next step

Quick answers

Should I block all faceted URLs with robots.txt?

No, only the ones that do not add search value. Blocking everything prevents crawlers from finding products behind filters. Use robots.txt to block low-value combos, but allow those you want indexed. Google's advice is layered: combine robots.txt, noindex, and canonicals.

How do I decide which facet pages to index?

Look at Search Console data for parameter URLs that have impressions but no clicks — those are candidates to noindex. Index only the combos that attract search demand or have unique content. I typically keep fewer than 5% of all possible facet combinations.

Can I use rel='canonical' to solve duplicate content from facets?

Yes, but only if you do not want the facet page to appear in search. Pointing the canonical to the parent category tells Google that the facet page is a duplicate. If the facet page has unique value, use a self-canonical and ensure it is noindexed only if thin.

What about faceted navigation on AJAX sites?

Use pushState to update the URL hash when a filter is applied. Googlebot ignores hash fragments, so you avoid creating new URLs for every state. If you must use query parameters via AJAX, ensure they are either blocked or properly canonicalised.

Sources

Primary documentation is linked directly. Anything commercial is marked nofollow.

Notes from Callum Bennett.