The client conversation that produced this guide started, as they usually do, with a list of “critical errors” from an audit tool. Half were Shopify defaults the tool does not understand, a quarter were real and fixable, and the rest were platform properties no amount of budget changes. Sorting a Shopify audit into those three boxes is most of its value.
Step 1. Check the duplicate product URLs and their canonicals
Shopify serves every product at /products/handle and again at /collections/collection-handle/products/handle for each collection that contains it. The duplication is real; the damage mostly is not, because the collection-path version carries a canonical tag pointing at /products/handle.
curl -s https://store.example/collections/caps/products/blue-cap | grep -o '<link rel="canonical"[^>]*>'
What remains worth fixing is internal linking. Older themes link products through the collection path, spreading internal links across URLs that all defer to another URL. Check the theme’s product card snippet for within: in the URL construction, and prefer linking straight to /products/handle [VERIFY: whether your current theme still does this; recent standard themes have moved to the direct URL].
Step 2. Decide what tag pages are allowed to do
Every collection filters by tag at /collections/handle/tagname, and each tag combination is a crawlable page with thin, near-duplicate content. A store with 40 collections and 30 tags mints over a thousand of these without anyone deciding to.
Check the scale of the problem in Search Console with a site: style filter on /collections/ pages indexed, and in the crawl stats. The control point since 2021 is robots.txt.liquid, which you create under Online Store, Edit code, Templates. A common pattern blocks tag-filtered paths while leaving the collections themselves open. Blocking via robots keeps crawl budget but leaves already-indexed pages in the index; for those, the theme can emit noindex on tag-filtered views by testing current_tags in Liquid. Choose one target state, then apply both mechanisms toward it rather than one of them halfway.
Step 3. Know that the catalogue is public JSON
Append .json to a products URL, or fetch /products.json, and the store returns its catalogue as structured data, with titles, prices, inventory hints and publication dates.
curl -s "https://store.example/products.json?limit=5" | head -c 400
Competitors and scrapers use it, price-monitoring services are built on it, and it cannot be switched off from the admin [VERIFY: current status; Shopify has adjusted endpoint behaviour over the years]. Treat it as a fact of the platform. The practical consequences are two. Nothing you consider commercially sensitive belongs in product fields, and if you see your prices mirrored elsewhere within hours of a change, this is how.
Step 4. Check pagination behaviour
Collections paginate with ?page=N. The pages self-canonicalise, which is correct, and the thing to verify is that page 2 and beyond are reachable and that the theme has not added a noindex or pointed every page’s canonical at page 1, which orphans every product that lives past the first 24. One curl per doubt:
curl -s "https://store.example/collections/caps?page=2" | grep -oE '<link rel="canonical"[^>]*>|noindex'
Step 5. Audit the script pile
Every installed app injects script, and most stores accumulate apps the way drawers accumulate cables. The result lands on Core Web Vitals, and removal is the only optimisation that works; apps “disabled” in the theme often keep their script tags. List what loads, match each script to an app, and uninstall what nobody defends. Check also for leftover ScriptTag entries from uninstalled apps under the theme’s rendered source, since uninstalling does not always clean up [VERIFY: current behaviour; Shopify has been deprecating ScriptTag in favour of app embeds, which clean up better].
Step 6. Accept the fixed properties, in writing
Three things do not change on any plan, and the audit should say so instead of pricing work against them. URL prefixes are locked, so products live under /products/, collections under /collections/, pages under /pages/, articles under /blogs/handle/; no flat URLs, no category paths in product URLs. The sitemap at /sitemap.xml is generated and not editable; it is also usually correct, so this costs little. And server-level anything, log files, redirects beyond the URL redirect tool, header manipulation past what the platform exposes, is not available, because there is no server to reach.
Markets and locale subfolders (/en-in/ style) follow the platform’s own hreflang and canonical wiring when configured through Markets; configure through the product rather than hand-editing theme hreflang, which conflicts with what the platform emits.
Check it worked
Five checks after the changes. The collection-path product URL still canonicalises to /products/. A tag-filtered URL returns the blocking or noindex behaviour you chose. ?page=2 is indexable with a self-canonical. The rendered page loads only scripts you can name an app for. And Search Console’s page indexing report, four to six weeks later, shows the tag-page count falling rather than rising, which is the lagging proof.
Where I could be wrong
Shopify moves. The robots.txt.liquid capability arrived in 2021, endpoint behaviour has been adjusted since, and theme defaults improve, so any line above marked [VERIFY] should be checked against a current store rather than trusted from the date on this guide.
The tag-page advice also assumes tags are navigation, not landing pages. A store that has deliberately built tag pages into content, with copy and curation, should index the good ones, and the blanket block would throw them away.
Sources
- Shopify Help Centre, editing robots.txt.liquid
- Shopify Help Centre, canonical URLs and duplicate content
- Shopify.dev, Liquid current_tags reference
- Shopify Help Centre, sitemaps
- Shopify Help Centre, Markets and international domains
- Google Search Central, ecommerce guidance