What Shopify does, and what is left to you
Shopify handles more of technical SEO than most hosted platforms, and the first useful thing an audit can do is stop you rebuilding what already works.
| Shopify handles | You handle |
|---|---|
| Sitemap generation, kept current in real time | Checking it lists what you expect |
robots.txt, including crawl-trap rules |
Deciding whether to override it at all |
| Canonical tags, including parameter stripping | Confirming the theme actually emits canonical_url
|
| HTTPS, HTTP/2, global CDN | Image sizes and third-party scripts |
| Duplicate collection-product URLs | Thin pages the theme creates |
| SEO title and description fields | Actually filling them in, uniquely |
Every claim in this article about how Shopify behaves was measured by requesting the URL on a live storefront and reading the response, not taken from documentation. Some of it will change; the method for re-checking it will not.
One product, five URLs, one canonical
The most common Shopify SEO worry is duplicate product URLs. Measured on a live store, it is mostly a solved problem.
Two things follow. First, do not write theme code to "fix" collection-scoped product URLs — you would be duplicating platform behaviour and creating a second thing to maintain. Second, link internally to the clean /products/handle form anyway. A canonical resolves the duplicate for search engines; an internal link that points at a redirect still wastes a hop for everyone.
The one thing you do control is whether your theme emits a canonical at all. A custom layout that forgets {{ canonical_url }} throws away everything above. Fetch a page and check.
Titles and descriptions: the metafields that matter
Shopify stores the SEO title and description as metafields on the resource — global.title_tag and global.description_tag — for products, collections, pages, blogs and articles alike. That matters for two reasons.
First, they can be set programmatically, so keeping metadata unique across a growing catalogue is a script rather than an afternoon in the admin:
For every article in the guides blog, set:
global.title_tag from the manifest's seoTitle
global.description_tag from the manifest's seoDescription
Then fetch each published URL and confirm the rendered title and meta
description match the manifest. Report any that do not.
The second half is the part that matters. Setting a metafield and assuming the theme reads it is a check that cannot fail. Fetch the page.
Second, a theme has to actually use them. Shopify's page_title and page_description resolve to these metafields when set and fall back to the resource title and body when not — so a theme that hard-codes something else silently ignores everything you entered in the admin.
Two Shopify-specific gotchas worth knowing before you write the script: the Admin GraphQL blog.articles connection has no query argument, so finding an article by handle means listing and filtering locally; and ShopPolicyInput keys on type, not id, which is not what the surrounding API conventions lead you to expect.
The homepage title trap
This one is worth its own section because it is invisible and it hits the most valuable page on the store.
Shopify's homepage title comes from Online Store → Preferences, not from the theme and not from any product. When that field is empty, page_title falls back to the store's raw .myshopify.com domain. The homepage then ships with a title like kjhvcj-yi.myshopify.com, which looks completely normal in a browser tab and is a disaster in a search result.
There is no Admin API mutation for that preference field, so a script cannot set it and a script cannot check it in the admin. The workaround used on this site is to have the layout supply the homepage title from a theme setting instead:
{% if template.name == 'index' %}
{% assign seo_title = settings.home_seo_title | default: shop.name %}
{% else %}
... normal page_title handling ...
{% endif %}
Either way, the check is the same and takes five seconds: fetch the homepage and read the <title> out of the response.
Thin pages Shopify creates for you
A Shopify store generates indexable URLs nobody deliberately created. Two categories are worth auditing on every store.
Blog tag archives. Every tag on every article produces /blogs/<blog>/tagged/<tag>. These self-canonicalise, are indexable, and in most themes inherit the blog's own title and meta description — so a store with eight tags ships eight pages carrying the blog index's exact title tag. They also add no text of their own: each one lists a subset of articles the index already lists in full. On this site the fix was a distinct title and description for anyone who lands on one, plus noindex, follow so they stop competing with the index while still passing link equity to the articles.
Default collections. Shopify creates a frontpage collection, and it goes into the generated sitemap whether or not your theme uses it. On this store it was rendering through the purchased theme rather than the custom layout, with an h1 of "Home page" and no title tag at all — a page in a different design, in the sitemap, that nothing on the site linked to. The fix was a collection template on the site's own layout so it is at least in-brand, and noindex because a one-product store's collection listing duplicates the homepage.
Neither of these is exotic. Both were found by crawling the site and sorting the results by title.
Structured data on a Shopify theme
Most purchased themes ship Product markup. Before adding your own, check what is already in the rendered HTML, or you will end up with two Product entities on one page disagreeing about price.
What a clean product page needs: one Product with name, description, brand, url, image, and one Offer carrying price, priceCurrency, availability and url. Build the price from the variant rather than a hard-coded string, so it cannot drift from what checkout charges.
Three rules that matter more than completeness:
- No review, rating or aggregate markup without real reviews. Review apps sometimes inject it on stores with zero reviews. Check the rendered output rather than trusting the app.
- Do not add a return policy you do not have. If refunds are handled case by case, none of schema.org's return categories describes that, and inventing a window to satisfy a validator puts your markup in conflict with your published policy. Omit the property and write down why.
- A warning is not an error. Rich Results Test flags recommended-but-absent properties. Filling one in with a plausible guess is worse than leaving it out.
Breadcrumb markup should mirror the breadcrumb the visitor can see. If the theme shows Home / Guides / Article, the markup says the same three, generated from the same condition so they cannot drift.
The sitemap: validate, never replace
/sitemap.xml on Shopify is an index that points at per-resource sitemaps for products, pages, collections and blogs, and it is regenerated as the catalogue changes. It cannot be edited by hand, and you should not want to: a hand-maintained sitemap goes stale the first time somebody adds a page.
What to do instead is validate it:
Fetch /sitemap.xml, follow every child sitemap, and for each URL:
- fetch it and record the status code
- record the canonical, and whether it points at the URL itself
- record any robots meta
Report: any non-200, any URL whose canonical points elsewhere, any
noindexed URL, and any page in the store that is missing entirely.
Do not modify the sitemap.
The two findings that matter are a URL in the sitemap that should not be indexable, and a page that should be indexable and is missing. Both are real. Neither is fixed by editing XML.
robots.txt, and when to override it
Shopify's generated robots.txt is better than most hand-written ones. It already disallows the cart, checkout, account and order paths, sort and filter parameter combinations, preview parameters, and internal endpoints, and it declares the sitemap.
Shopify does allow an override through robots.txt.liquid. Almost nobody needs it, and the downside is asymmetric: a mistake here can remove your store from search results, and it will not be obvious. Use it only for a rule you can state in one sentence with a reason, and check the live file afterwards.
Images
Shopify's CDN handles format negotiation and resizing, so the work is on your side of the boundary.
- Request the size you render, through
image_url: width: N, and provide asrcsetrather than one large file scaled down in the browser. - Set
widthandheighton every image. Layout shift is the most common Core Web Vitals failure on a storefront. - The product's main image and an article's hero are the LCP element. Load them eagerly. Everything below the fold gets
loading="lazy". - Descriptive filenames and real alt text. Alt describes the image; it is not a place to repeat the product name with keywords attached.
-
image_urlreturns a protocol-relative URL. Structured data and Open Graph need an absolute one, so prependhttps:— nothttp:.
Product copy that is worth ranking
The technical layer above is table stakes. The product page still has to answer the question somebody typed.
An assistant is genuinely useful here and genuinely dangerous. Useful: restructuring a description so the specific answer comes first, finding the questions the copy never answers, catching claims the rest of the site contradicts. Dangerous: it will happily invent a specification, a dimension, a compatibility claim, or a customer count, because product copy in its training data is full of them.
The rule that works: the assistant may rewrite what is there, and may tell you what is missing, but may not supply a fact. Anything it flags as missing, you answer.
For a digital product specifically, the details that convert are also the details that rank: what is in the file, how many of them, the format, how delivery works, what the licence permits, and what happens if it does not work. That last one is a link to the refund policy, and it should be a real one.
Search Console, and Merchant Center for a digital product
Verify the property for the exact production hostname — the custom domain, not the .myshopify.com one — and submit /sitemap.xml. Then watch coverage rather than resubmitting: "Discovered — currently not indexed" is a quality signal, not a technical fault.
Merchant Center deserves a straight answer, because a lot of advice assumes every store is eligible. Google's unsupported Shopping content policy lists, verbatim, "eBooks and digital books (not including audiobooks)" with the examples "PDFs, EPUB books, MOBI", and separately lists services as "labour, time, effort, expertise or actions which don't result in ownership of a tangible product".
A downloadable document product sits close enough to both of those that eligibility is genuinely uncertain. The correct action is to read the current policy against your specific product and decide, not to set up a feed and hope. If the answer is no, or unclear, do not add merchant markup or a product feed anyway — misrepresentation is the single most common cause of Merchant Center suspension, and there is no SEO benefit to markup for a programme you are not in.
None of this affects organic search. A digital product ranks in web search on the same terms as anything else.
Validating after every theme push
Shopify theme changes go live the moment they are pushed, and a broken Liquid tag does not produce a 500 — it renders the error as text on the page. So the check after every push is not "did it deploy" but "what does the page say now":
- Fetch the affected URLs and search the response for
Liquid error - Confirm the title, description and canonical are what you expect
- Parse every JSON-LD block
- Re-run the internal-link crawl
Write the search for Liquid error carefully. A naive pattern will match an article that mentions the phrase in prose — that happened here, and reported a perfectly healthy page as broken. Match Shopify's actual format, and control-test it against both a page with a real error and a page that merely talks about them.
Where to go next
For building the store in the first place, how to build a Shopify store with Claude Code covers the theme architecture, credentials, digital delivery and going live. For the platform-independent SEO work, the SEO workflow is the pillar this sits under, and running a technical SEO audit covers proving your checks can fail. For auditing the whole storefront rather than its SEO, see the website audit guide. The platform rules worth writing down are in the Shopify example in the CLAUDE.md guide. The free SEO checklist is the short form of all of it.
Sources and further reading
- Shopify: theme architecture — layouts, templates, sections and section groups
- Shopify: canonical_url
- Shopify: robots.txt.liquid and hiding pages
- Google: unsupported Shopping content — the eligibility question for digital products
- Google: product structured data