Fixing Your Product Variant URL Strategy
Most products come in more than one size, colour or material, and every shop has to decide how those variants appear as URLs.
Some keep one page and switch options on it; some give every colour its own page; many end up with a muddle of both, with variant URLs competing, canonicals pointing the wrong way and swatches generating addresses crawlers chase. This guide helps you choose the right model for your catalogue, make each variant reachable at a stable URL, set canonicals that match the model, and describe the variants in ProductGroup markup.
The two models, and the muddle in between
There are two coherent ways to handle variants. Both are acceptable to search engines; problems start when a shop mixes them without meaning to.
| Single page | Separate pages | |
|---|---|---|
| What it looks like | /products/field-jacket, with ?colour=olive&size=m
preselecting options | /products/field-jacket-olive and /products/field-jacket-navy,
each a full page |
| Canonical | Every variant URL points to the base product URL | Each page points to itself |
| What ranks | One page for the product as a whole | Each variant can rank for its own searches |
| Content effort | One description, shared reviews | Distinct images and at least some distinct text per page |
| Best for | Sizes, pack quantities, minor options | Colours or materials people search for by name, where images and stock differ |
The muddle looks like this: a single product page whose colour swatches link to URLs that each canonicalise to themselves, but with identical titles and descriptions; or separate colour pages that all canonicalise to the first colour, so only one can ever be shown. The Ecommerce Crawl surfaces these as duplicate product URLs or as clusters of near-identical pages.
Variants are not the same issue as one product reachable through several paths, such as through different collections. That is covered in Fixing duplicate product URLs.
How to choose, product type by product type
You do not have to use one model across the whole shop. The right choice depends on how people search and how different the variants really are. Ask four questions for each product type:
- Do people search for the variant by name? "Navy linen shirt" is a real search; "linen shirt size 42" rarely is. Search Console queries and keyword tools show which attributes appear in searches.
- Does the variant look different? Different colours need different photos. Different sizes usually look identical.
- Can you write something distinct for it? If the only difference on a separate page would be one word in the title, the pages will be treated as duplicates.
- Does the variant have its own stock, price or identifiers? Separate GTINs and prices are normal for both models, but strongly different prices and availability make separate pages easier to understand.
A common and sensible result: colours on separate pages for clothing and furniture, where colour drives searches and photos; sizes, capacities and pack quantities as options on the same page. Accessories such as phone cases may justify separate pages by device model, because people search by model.
What makes a good variant URL
Whatever the model, each variant should be reachable at a URL that loads with that variant already selected. Google's product variant documentation asks for exactly this: a distinct URL per variant, for example using query parameters, that preselects the variant. Shopping ads and free listings also link to variant-level URLs, so a shopper who clicked "olive, medium" lands on olive, medium rather than the default.
- Stable: the same variant always has the same URL. Avoid IDs that change when a product is re-imported.
- Readable where possible:
?colour=olive&size=mis easier to maintain than an internal ID, although platform IDs such as Shopify's?variant=work. - Server-honoured: the page should show the right image, price and stock for the variant in the initial HTML or very early in rendering, not only after a shopper clicks.
- One order: if two options are in the URL, always output them in the same order.
Describing variants with ProductGroup
Google supports ProductGroup markup for variants. The group carries the shared details
and lists each variant as a Product through hasVariant. variesBy
names the attributes that differ, using schema.org properties such as https://schema.org/color
and https://schema.org/size, and productGroupID ties the variants together.
On a single-page setup the group's url is the base product URL. Full detail on offers, shipping
and returns is in the Schema Builder guide;
this is the variant-specific shape.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "ProductGroup",
"name": "Waxed field jacket",
"description": "Waxed cotton field jacket with a corduroy collar and four front pockets.",
"url": "https://www.example.co.uk/products/field-jacket",
"brand": { "@type": "Brand", "name": "Hollowmere" },
"productGroupID": "FJ100",
"variesBy": ["https://schema.org/color", "https://schema.org/size"],
"hasVariant": [
{
"@type": "Product",
"sku": "FJ100-OLV-M",
"gtin13": "5098765432109",
"name": "Waxed field jacket, olive, medium",
"image": "https://www.example.co.uk/images/fj100-olive.jpg",
"color": "Olive",
"size": "M",
"offers": {
"@type": "Offer",
"url": "https://www.example.co.uk/products/field-jacket?colour=olive&size=m",
"price": "149.00",
"priceCurrency": "GBP",
"availability": "https://schema.org/InStock"
}
},
{
"@type": "Product",
"sku": "FJ100-NVY-M",
"gtin13": "5098765432116",
"name": "Waxed field jacket, navy, medium",
"image": "https://www.example.co.uk/images/fj100-navy.jpg",
"color": "Navy",
"size": "M",
"offers": {
"@type": "Offer",
"url": "https://www.example.co.uk/products/field-jacket?colour=navy&size=m",
"price": "149.00",
"priceCurrency": "GBP",
"availability": "https://schema.org/OutOfStock"
}
}
]
}
</script>
On a separate-pages setup, each page describes its own variant as a Product and uses
inProductGroupWithID to point at the shared group ID, with the group details repeated on
each page or placed on one page and referenced. Only mark up variants that are actually offered on the
page.
Stopping variants from multiplying the crawl
Variants can create a small explosion of their own. A jacket in six colours and seven sizes has 42 combinations, and if every swatch and size button is an ordinary link with its own parameters, crawlers will request all of them, plus every ordering of the parameters if the shop accepts both.
- On the single-page model, the variant URLs should exist and work, but they do not all need crawlable links. Colour swatches can be links if colour matters; size buttons can be form controls that update the page.
- Make sure every variant URL carries the canonical to the base product, so any that are crawled consolidate.
- Do not block variant parameters in robots.txt if you rely on canonicals for them, and do not block them if shopping ads land on them, because Google needs to fetch those pages to check price and availability.
- On the separate-pages model, keep size selection inside each colour page, rather than creating colour-and-size pages.
The fix, step by step
Step 1
List how variants currently appear as URLs
Pick five representative products of different types and click through every option on each, noting the URL, the canonical tag in the page source and the title each variant shows. Then run the Ecommerce Crawl and filter for product URLs with parameters or colour words in the path. You are looking for whether variants have their own URLs, whether they canonicalise consistently, and whether swatches are generating crawlable links to every combination.
Step 2
Choose one page or separate pages for each product type
For each product type, check whether people search for the variant by name, whether it looks different, whether you can write distinct content for it, and whether stock and price differ. Use Search Console queries to see which attributes appear in real searches. Write the decision down as a short rule, such as colours on separate pages and sizes as options, so product managers apply it the same way to every new listing.
Step 3
Give every variant a stable, preselecting URL
Make sure each variant loads at its own URL with the right options already selected, showing the correct image, price and stock in the page as delivered. Use readable parameters or the platform variant ID, output options in one fixed order, and make sure the URL does not change when products are re-imported. Test by pasting a variant URL into a private browser window and checking that the page opens on that exact variant.
Step 4
Set canonicals to match the model
On the single-page model, every variant URL should carry a canonical tag pointing to the base product URL without parameters. On the separate-pages model, each variant page should carry a self-referencing canonical and never point to a sibling colour. Check a sample in the page source and in the URL Inspection tool in Search Console, which shows the canonical Google selected, and correct any template that outputs a different canonical from the one you intended.
Step 5
Control how swatches and option links are crawled
Decide which options need crawlable links. On a single page, sizes and minor
options can be buttons or form controls that update the page without an <a href>.
On separate pages, colour swatches should link to the sibling colour pages using their clean URLs, while
size choices stay inside each page. Remove any template code that adds variant parameters to links in
category listings unless you deliberately want that variant shown first.
Step 6
Mark up the product group and its variants
Add ProductGroup markup with name, productGroupID,
variesBy and a hasVariant list, giving each variant its own sku,
identifiers, image, attributes and an offer whose URL is the preselecting variant URL. On separate pages,
use inProductGroupWithID on each variant. Remove older markup that describes only the default
variant, and test the page in the Rich Results Test to confirm the variants are detected.
Step 7
Align sitemaps and shopping feeds
Your
XML sitemap should list only canonical URLs: the base product on the single-page model, each variant
page on the separate-pages model. Your shopping feed, by contrast, usually lists one row per purchasable
variant with its preselecting URL, sharing an item group ID that matches your productGroupID.
Check that the price and availability in the feed, the markup and the visible page agree for a sample
of variants.
Step 8
Test variant URLs and recrawl
Run the Ecommerce Crawl again and check that variant URLs are no longer reported as competing duplicates, and that the number of crawlable variant combinations has dropped where you removed links. Inspect a few variant URLs in Search Console to confirm the Google-selected canonical matches yours. Keep an eye on the Page indexing report over the following weeks for variant URLs moving into the alternate page with proper canonical tag status.
Platform notes
Shopify
Shopify uses the single-page model by default. Each variant is reachable at /products/handle?variant=
followed by a numeric ID, and themes output a canonical to the plain product URL. Shopify allows a limited
number of options and variants per product, so large ranges are sometimes split into separate products
by colour, which moves you into the separate-pages model; if you do that, link sibling colours to each
other and give each its own photos and text.
WooCommerce
Variable products keep variants on one page, with attributes selectable by dropdown. Out of the box,
variations do not get their own preselecting URLs unless attributes are passed as ?attribute_pa_colour=
parameters, which WooCommerce can read. Check that your theme keeps the canonical on the parent product
when those parameters are present.
Custom builds
Design the variant URL scheme deliberately before launch: readable parameters for the single-page model, or a clean path segment per variant for separate pages. Render the selected variant's details on the server so crawlers see the right image and price.
1. Should each colour of a product have its own page?
Only when colour affects how people search and what they see. If shoppers search for navy linen shirt or oak dining table, and each colour has its own photos, separate pages can rank for those searches. If the colours differ only by one word and share the same photos and text, separate pages will look like duplicates, and one page with selectable options is the better choice. You can use different models for different product types in the same shop.
2. Should variant URLs have a canonical pointing to the main product?
On the single-page model, yes: every variant URL, such as one with a variant or colour parameter, should point its canonical to the base product URL, so signals consolidate on one page. On the separate-pages model, no: each variant page should point to itself, because the pages are meant to rank separately. The error to avoid is pointing all separate colour pages to one of them, which stops the others from ever appearing in results.
3. Do product variants cause duplicate content penalties?
There is no penalty for having variants. The practical risk is that Google sees several near-identical pages, picks one as canonical itself, and ignores the rest, which may not be the one you would have chosen. It also spends crawl on pages that add nothing new. Clear canonicals, a deliberate choice between the two models, and distinct content on any separate variant pages keep Google choosing the pages you intended.
4. Is ProductGroup markup required for variants?
No, it is optional. Product markup on its own still works, but it only describes one variant at a time, and search engines have to work out the relationship between variants themselves. ProductGroup with hasVariant, variesBy and productGroupID states the relationship explicitly, and Google documents it for exactly this purpose. It is most useful for shops with many variants per product, and for anyone also running shopping feeds, where the group ID can match the feed's item group.
5. Should I block variant parameters in robots.txt?
Usually not. Google needs to fetch variant URLs to read their canonical tags, and free listings and shopping ads often send shoppers to variant URLs, which Google checks for price and availability. Blocking them can cause feed and landing page problems. A better way to reduce variant crawling is to stop generating crawlable links to every combination, for example by using buttons for sizes, while leaving the URLs themselves working and canonicalised.
6. What happens to rankings if I switch variant models?
Expect some movement while Google processes the change. Moving from separate pages to one page means redirecting the retired variant URLs to the main product with the right option preselected, so any links and history carry across. Moving the other way means creating new pages that have no history yet. In both cases, update internal links, sitemaps and feeds in the same release, and avoid making the change just before your busiest season.
The Ecommerce Crawl maps the URLs on your store, finds faceted navigation explosion and duplicate product URLs, flags out-of-stock products, surfaces orphans and measures how efficiently your crawl is spent.
Open Ecommerce Crawl Fixes โ