eg

shopify seo audit.

run 50+ checks against a shopify store, including the ones only shopify stores fail

paste a store url. we read what googlebot reads, including the mistakes only shopify stores make, and hand back a ranked list of what to fix first.

  • 98 checks
  • 8 categories
  • results in seconds
  • no sign up

we fetch the store from our server and read exactly what a crawler reads on its first pass. nothing is stored against you, and there is no sign up.

> every check this runs

98 checks, grouped the way the report groups them. every check is weighted 1 to 5, and the 4s and 5s are marked below because they move the score most. 15 of them read shopify specific markup, so they only run when the store is shopify.

  • technical 16
  • platform 15
  • meta and head 15
  • content 11
  • structured data 12
  • ecommerce 13
  • speed signals 10
  • mobile 6

  • https5weight 5 of 5

    Reads the scheme of the URL finally served, after any redirect, and reports whether it is https.

  • meta robots indexing5weight 5 of 5

    Reads the meta robots and googlebot tags for noindex and nofollow directives.

  • robots.txt site block5weight 5 of 5

    Reads robots.txt for a Disallow rule that covers the whole site for every crawler.

  • x-robots-tag header5weight 5 of 5

    Reads the X-Robots-Tag response header, where indexing directives can sit without appearing in the HTML.

  • http to https redirect4weight 4 of 5

    Requests the http entry point and reports whether it redirects to https.

  • robots.txt present4weight 4 of 5

    Requests /robots.txt and reports the status code it returned.

  • xml sitemap present4weight 4 of 5

    Looks for an XML sitemap, both at the usual paths and on the Sitemap line in robots.txt.

  • one hostname4weight 4 of 5

    Requests the apex and www hostnames to see whether both answer with 200 instead of one redirecting to the other.

  • 404 handling3weight 3 of 5

    Requests a path that does not exist and reads the status code returned, rather than the page shown.

  • noindex urls in sitemap3weight 3 of 5

    Records which sitemaps answered, and states that their listed URLs are not fetched, so none of them are tested for noindex.

  • redirect chain3weight 3 of 5

    Counts the hops between the URL entered and the URL finally served.

  • robots.txt sitemap line3weight 3 of 5

    Looks for a Sitemap line in robots.txt and reads the URL it points at.

  • sitemap contents3weight 3 of 5

    Reads the retrieved sitemap to see whether it parses, whether it is an index or a urlset, and how many loc entries it holds.

  • html compression2weight 2 of 5

    Reads the Content-Encoding header on the HTML response to see which compression was applied.

  • hreflang set2weight 2 of 5

    Reads the hreflang annotations on the page and looks for a self referencing entry and an x-default.

  • html cache-control1weight 1 of 5

    Reads the Cache-Control header returned with the HTML document.

  • myshopify.com in canonicals5weight 5 of 5

    Reads the canonical tags on the sampled pages for a myshopify.com URL in place of the store's own domain.

  • storefront password page5weight 5 of 5

    Looks for the markers of the Shopify password page in the home page URL, status, redirect hops, markup and form actions.

  • third party script hosts4weight 4 of 5

    Counts the external script hosts on the sampled pages that are neither the store domain nor a Shopify CDN.

  • collection scoped product urls4weight 4 of 5

    Counts internal product links written in the /collections/x/products/y form against the plain /products/y form.

  • liquid errors in markup4weight 4 of 5

    Searches the rendered markup for Liquid error strings left behind by a broken theme edit.

  • shopify detected3weight 3 of 5

    Reports the signals that identify the store as Shopify, and the confidence behind them.

  • sitemap index structure3weight 3 of 5

    Reads /sitemap.xml to see whether it is a sitemap index, and which of the products, collections, pages and blogs child sitemaps it names.

  • variant query parameters3weight 3 of 5

    Counts links carrying a variant query parameter, and reads whether the sampled product canonical keeps that parameter.

  • shopify cdn images2weight 2 of 5

    Counts the sampled images served from a Shopify CDN host, and how many of those carry an explicit width parameter.

  • /collections/all2weight 2 of 5

    Counts links to /collections/all and reads whether robots.txt disallows that path.

  • default shopify paths2weight 2 of 5

    Counts links to the default Shopify paths /policies/, /pages/contact, /challenge and /apps/, and reads the robots.txt rule covering each one.

  • markets hreflang2weight 2 of 5

    Reads the hreflang entries across the sampled pages, which is how Shopify Markets annotates regional storefronts.

  • robots.txt override2weight 2 of 5

    Compares the Disallow lines served in robots.txt against the set Shopify generates by default, so edits to robots.txt.liquid are visible.

  • title store name suffix2weight 2 of 5

    Compares the sampled page titles for a shared trailing store name, and measures how many characters it takes.

  • shopify theme1weight 1 of 5

    Reads the Shopify.theme object in the markup for the theme name, id and schema name.

  • title tag present5weight 5 of 5

    Reports whether each sampled page carries a title tag with text in it.

  • canonical tag present4weight 4 of 5

    Reports whether the page declares a rel=canonical link in the head.

  • canonical points to this page4weight 4 of 5

    Compares the canonical URL against the page's own URL, with tracking parameters stripped from both.

  • meta description present4weight 4 of 5

    Reports whether the page carries a meta description with text in it.

  • title length4weight 4 of 5

    Counts the characters in the title tag and flags the point where Google truncates it.

  • html lang attribute3weight 3 of 5

    Reads the lang attribute on the html element and the language code it declares.

  • meta description length3weight 3 of 5

    Counts the characters in the meta description against the 70 to 160 character band.

  • og:image3weight 3 of 5

    Reports whether an og:image tag is present, which is the image a shared link carries.

  • charset declared2weight 2 of 5

    Reads the character encoding declared in the head.

  • meta description repeats the title2weight 2 of 5

    Compares the meta description against the title tag to see how much of it is repeated.

  • og:description2weight 2 of 5

    Reports whether an og:description tag is present, which is the summary a shared link carries.

  • og:title2weight 2 of 5

    Reports whether an og:title tag is present, which is the headline a shared link carries.

  • title keyword position2weight 2 of 5

    Detects the brand name from og:site_name, the title or the hostname, then reads whether the title opens with it.

  • favicon link1weight 1 of 5

    Looks for a link element with an icon rel in the head.

  • twitter:card1weight 1 of 5

    Reads the twitter:card tag and the card type it names.

  • h1 present5weight 5 of 5

    Reports whether each sampled page carries an h1 with text in it.

  • exactly one h14weight 4 of 5

    Counts the h1 elements in the document.

  • image alt coverage4weight 4 of 5

    Counts the img elements carrying an alt attribute against those with none.

  • word count4weight 4 of 5

    Counts the words in the visible text, with script, style and markup stripped out.

  • duplicate titles across sampled pages3weight 3 of 5

    Groups the titles of the sampled pages to see whether any two of them are the same.

  • internal link count3weight 3 of 5

    Counts the internal links in the server HTML of the home page.

  • h1 length2weight 2 of 5

    Counts the characters in the h1.

  • h1 and title overlap2weight 2 of 5

    Measures how many words the h1 and the title tag share.

  • heading order2weight 2 of 5

    Walks the heading levels in document order and reports where a level is skipped.

  • outbound nofollow ratio1weight 1 of 5

    Counts the external links on the home page and the share of them carrying rel=nofollow.

  • text to html ratio1weight 1 of 5

    Compares the bytes of visible text against the bytes of HTML delivered.

  • product schema5weight 5 of 5

    Looks for a Product node in the JSON-LD on the sampled product page.

  • product schema fields5weight 5 of 5

    Reads the Product node for name, image, description, brand, sku and gtin or mpn, plus the price, currency and availability inside offers.

  • json-ld parses4weight 4 of 5

    Parses every JSON-LD block on the sampled pages and counts the ones that fail.

  • structured data present4weight 4 of 5

    Counts the JSON-LD blocks and microdata itemtype values on the home page, and lists the types declared.

  • breadcrumb schema3weight 3 of 5

    Looks for a BreadcrumbList node in the JSON-LD on the sampled pages.

  • faq schema visible3weight 3 of 5

    Compares every Question name in FAQPage markup against the visible text of the page it is marked up on.

  • organization schema3weight 3 of 5

    Looks for an Organization, LocalBusiness, OnlineStore or Store node and reads its name and url.

  • offer shape3weight 3 of 5

    Reads each offers node for an Offer or AggregateOffer type and for an availability value written as a schema.org enum.

  • aggregate rating2weight 2 of 5

    Reads an AggregateRating node already on the page for a numeric rating value and a review or rating count, and reports nothing further when there is no such node.

  • duplicate schema types2weight 2 of 5

    Counts how many times each top-level schema type is declared on a page, so a type emitted twice is visible.

  • website schema2weight 2 of 5

    Looks for a WebSite node in the home page JSON-LD.

  • microdata itemtypes1weight 1 of 5

    Counts the microdata itemtype attributes across the sampled pages and lists the types they name.

  • price in server html4weight 4 of 5

    Looks for a currency-formatted price in the HTML your server returns, rather than one painted later by JavaScript.

  • add to cart in server html3weight 3 of 5

    Looks for an add to cart form or control in the server HTML of the sampled product page.

  • faceted navigation3weight 3 of 5

    Counts the filter and sort parameters linked from the sampled collection page, and reads what robots.txt and that page's own canonical say about them.

  • search results disallowed3weight 3 of 5

    Reads robots.txt for a Disallow rule covering an internal search path or query parameter.

  • product page sampled3weight 3 of 5

    Reports whether a product page was reached and fetched, since every product scoped check runs against it.

  • availability signal2weight 2 of 5

    Looks for stock wording such as in stock or sold out in the visible text of the sampled product page.

  • visible breadcrumb2weight 2 of 5

    Looks for a breadcrumb trail rendered in the markup, separately from any BreadcrumbList schema.

  • cart and checkout disallowed2weight 2 of 5

    Reads robots.txt for Disallow rules covering the cart and checkout paths.

  • collection page sampled2weight 2 of 5

    Reports whether a collection or category page was reached and fetched, since every collection scoped check runs against it.

  • pagination signals2weight 2 of 5

    Counts the links on the sampled collection page that carry a page parameter or a /page/ segment.

  • products linked on collection2weight 2 of 5

    Counts the distinct product URLs linked from the sampled collection page.

  • image host1weight 1 of 5

    Ranks the hosts serving images across the sampled pages.

  • open graph product tags1weight 1 of 5

    Reads og:type and the product:price:amount and product:price:currency tags on the sampled product page.

  • render blocking scripts4weight 4 of 5

    Counts the scripts in the head that carry neither async nor defer.

  • html weight3weight 3 of 5

    Measures the bytes of the HTML document itself, before any image, script or stylesheet loads.

  • image dimensions3weight 3 of 5

    Counts the img elements that declare width and height against those that leave the size to CSS.

  • render blocking css3weight 3 of 5

    Counts the stylesheets loaded in the head without a media condition.

  • third party scripts3weight 3 of 5

    Counts the distinct external hosts serving scripts to the page.

  • html response time3weight 3 of 5

    Records the wall-clock time this audit waited for the HTML response, from one request, from one location.

  • image count2weight 2 of 5

    Counts the img elements in the document.

  • image lazy loading2weight 2 of 5

    Counts the img elements carrying loading=lazy.

  • inline css weight1weight 1 of 5

    Measures the total bytes of the inline style blocks in the document.

  • preconnect hints1weight 1 of 5

    Counts the preconnect and dns-prefetch link hints declared in the head.

  • viewport meta tag5weight 5 of 5

    Reports whether the page declares a viewport meta tag.

  • viewport width4weight 4 of 5

    Reads the viewport tag for width=device-width against a fixed pixel width.

  • pinch zoom allowed3weight 3 of 5

    Reads the viewport tag for user-scalable=no or a maximum-scale value that blocks pinch zoom.

  • fixed width markup2weight 2 of 5

    Counts the elements declaring a fixed width above 480px in a width attribute or an inline style.

  • responsive images2weight 2 of 5

    Counts the img elements that offer a srcset alongside the src.

  • tap targets and font size1weight 1 of 5

    Notes that tap target size and font legibility need a rendered mobile viewport, which this HTML-only audit does not produce.

> why run this at all

shopify's defaults are sound, its duplicates are not

A product on Shopify is reachable at /products/handle, at /collections/anything/products/handle for every collection it belongs to, and at either of those with a ?variant= parameter appended. A theme that sets canonicals correctly collapses all of it back to one URL. A theme that has been edited, and most have been, sometimes does not. This audit reports which URL shape the collection page actually links to, and what the product page's canonical says about it.

robots.txt is a liquid template, and templates get edited

Shopify ships a default block list covering /cart, /checkout, /orders, /account and the sort and filter parameters on collections. Since robots.txt.liquid became editable, those lines can be removed, and there is nothing in the admin that flags it. The audit lists which of the default rules are still present and which non-default rules have been added.

apps add scripts, uninstalling them does not always remove them

Every app that touches the storefront adds a script tag or an app block. Removing the app from the admin does not always remove the theme code it injected. The audit counts the external script hosts that are neither your domain nor a Shopify CDN, and names them, so the leftovers are visible.

a password-protected store returns a real page

A store still behind the storefront password serves the challenge page with a 200. An audit that does not check for it will happily report on the password page: one h1, no product schema, thin content. This one detects the challenge and says the store was not reachable, rather than scoring the wrong document.

> worked example

A store returns 51 checks: 38 passed, 9 warnings, 4 failures. The failures are a missing meta description on the sampled product page, an hreflang entry pointing at the .myshopify.com host, 17 third-party script hosts, and a soft 404 (a nonsense path returns HTTP 200 with the home page). The platform section adds that the collection page links to products in the /collections/x/products/y form while the product page canonical uses /products/y, which is the shape Shopify intends.

takeaway, The score is a summary of the checks below it. The four failures are the report, the number is the index.

> when operators reach for this

  • Merchants checking a new theme before it goes live, particularly one bought from a marketplace rather than built by the store's own developer.
  • Developers confirming a robots.txt.liquid edit did what was intended, and did not remove a default rule alongside it.
  • Agencies opening an account they have just inherited and needing a written starting point covering crawl, index, markup and platform issues in one pass.
  • Anyone auditing after an app clear-out, to see which script hosts are still loading on the storefront.
  • Store owners who want to know whether their product pages render a price in the HTML or only in JavaScript, which decides whether a crawler sees it at all.

> the calculation

  • scoresum(weight × status points) ÷ sum(weight) × 100
  • status pointspass 1 · warning 0.5 · fail 0Info rows and skipped checks are never scored. A check we could not evaluate does not count against you and does not flatter you either.
  • weights1 cosmetic → 5 blocks indexingA noindex tag and a missing favicon are not the same problem, so they do not move the score by the same amount.
  • gradeA ≥ 90 · B ≥ 80 · C ≥ 70 · D ≥ 60 · F below 60
  • what is fetchedhome page + robots.txt + sitemap + up to four sampled pagesAt most 10 requests per audit, each identifying itself in the user agent. Nothing is crawled beyond that.

Frequently asked questions

What does the Shopify SEO audit actually check?
Around 50 checks across eight groups: technical (https, redirects, robots.txt rules, sitemap, indexability, 404 handling), platform (the Shopify-specific ones), meta and head tags, content and headings, structured data, ecommerce signals such as whether the price is server-rendered, speed signals from the response we received, and mobile markup. Every check reports what was observed on your store, with the fix stated only where something is wrong.
Which checks are Shopify-specific?
Collection-scoped product URLs and what the product canonical does with them, ?variant= parameters, whether robots.txt still carries Shopify's default block list or has been overridden through robots.txt.liquid, the structure of the generated sitemap and which child sitemaps exist, the count of app and pixel script hosts, Liquid rendering errors left in the HTML, the storefront password page, the theme's default title suffix and how many characters it consumes, and any canonical or hreflang still pointing at the .myshopify.com domain instead of your own.
Does it crawl my whole store?
No. It fetches the home page, robots.txt, up to two sitemaps, and up to four sampled pages that it picks from your home page links or your sitemap, up to two products and up to two collections. It also sends one request to a path that does not exist, to see whether your store returns a real 404 or a 200. That is at most ten requests, which is less traffic than one visitor browsing a couple of pages.
Does it run JavaScript?
No. It reads the HTML your server returns, which is what a crawler reads on its first pass. That is deliberate: if your price, product schema or main copy only appears after JavaScript runs, this audit will report it as absent, and that is useful information rather than a flaw in the tool. It does mean speed figures here are one server response from one location, not Core Web Vitals, and the report says so rather than implying otherwise.
Will it work on a store that is still password-protected?
No, and it will tell you. A locked store serves Shopify's challenge page with a normal 200 response, so an audit that did not check would score the password page instead of your store. This one detects it and stops, rather than handing you a report about a page you did not ask about.
How is this different from the ecommerce SEO audit?
The check set. The ecommerce audit is platform-agnostic and runs the technical, content, structured data and ecommerce groups against any store on any platform. This one runs all of those and adds the Shopify group on top: Liquid errors, robots.txt.liquid overrides, collection-scoped URLs, app scripts, the myshopify domain, Markets hreflang. If your store is on Shopify, run this one. If it is not, or you are auditing several stores on different platforms, run the other.
Is there a signup, an email gate or a limit?
None of the three. Enter a URL, get the report, run it as many times as you want. Your URL is not stored against an account, because there are no accounts here.
Does a score of 100 mean the store will rank?
No. The score summarises the checks in this report, and this report covers what can be read from the HTML of a handful of pages. It says nothing about your links, your prices, your competitors, your product range, or the demand for what you sell. Treat it as a list of things that are demonstrably wrong on the pages we fetched, not as a prediction.

> related calculators, seo & metadata