<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Max</title>
    <description>The latest articles on DEV Community by Max (@max_buildogs).</description>
    <link>https://dev.to/max_buildogs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4040371%2F3325063e-c539-470e-b9e4-104fb0b25dcb.png</url>
      <title>DEV Community: Max</title>
      <link>https://dev.to/max_buildogs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/max_buildogs"/>
    <language>en</language>
    <item>
      <title>Discovered, not indexed: the Shopify crawl bug clients hit</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Wed, 02 Sep 2026 11:56:15 +0000</pubDate>
      <link>https://dev.to/max_buildogs/discovered-not-indexed-the-shopify-crawl-bug-clients-hit-66j</link>
      <guid>https://dev.to/max_buildogs/discovered-not-indexed-the-shopify-crawl-bug-clients-hit-66j</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Discovered - currently not indexed" means Google found the URL but hasn't fetched it yet; it says nothing about content quality (that's the neighboring "Crawled - currently not indexed" status).&lt;/li&gt;
&lt;li&gt;On Shopify the usual cause is URL inflation: recommendation tracking parameters, collection-scoped product URLs, and app-added query strings that the default robots.txt doesn't block.&lt;/li&gt;
&lt;li&gt;The fix lives in robots.txt.liquid and internal link markup, not in rewriting product copy or spamming "Request indexing."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A client's Shopify store shows a growing pile of URLs stuck under "Discovered - currently not indexed" in Search Console, and nothing in the site throws an error. No noindex tag, no 404, no robots block anyone remembers writing. This status shows up on stores you build and maintain, not just ones you audit after the fact, because it's produced by defaults nobody chose: Shopify's Product Recommendations API appends tracking parameters to product links, collection pages create a second URL for every product they list, and apps bolt on filters and widgets with their own query strings. None of it is flagged as an SEO risk anywhere in the app install flow. This article walks through what the status actually means, why Shopify stores generate far more crawlable URLs than products, and the exact checks (view-source, robots.txt, sitemap counts) you can run against a client's store in about five minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does "Discovered - currently not indexed" actually mean?
&lt;/h2&gt;

&lt;p&gt;Google's &lt;a href="https://support.google.com/webmasters/answer/7440203" rel="noopener noreferrer"&gt;Page indexing report documentation&lt;/a&gt; defines it precisely: "The page was found by Google, but not crawled yet. Typically, Google wanted to crawl the URL but this was expected to overload the site; therefore Google rescheduled the crawl."&lt;/p&gt;

&lt;p&gt;Compare that with the status directly beneath it in the same report: "The page was crawled by Google but not indexed. It may or may not be indexed in the future; no need to resubmit this URL for crawling."&lt;/p&gt;

&lt;p&gt;One word apart, and the fixes are opposite. A &lt;strong&gt;crawled&lt;/strong&gt; page has been fetched and read, so its title, markup and content have actually been judged. A &lt;strong&gt;discovered&lt;/strong&gt; page has been seen by nothing at Google. Editing product copy on a page in that state changes nothing, because the change isn't visible to the entity that put it in the queue. The first question to ask on a client's store isn't "what's wrong with these pages," it's "what is the crawler doing instead."&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does Google flag this as a crawl-budget signal?
&lt;/h2&gt;

&lt;p&gt;Google opens its &lt;a href="https://developers.google.com/search/docs/crawling-indexing/large-site-managing-crawl-budget" rel="noopener noreferrer"&gt;large-site crawl budget guide&lt;/a&gt; by narrowing who needs it: "If your site doesn't have a large number of pages that change rapidly, or if your pages seem to be crawled the same day that they are published, you don't need to read this guide." Two of the three qualifying conditions it lists are about raw size (a million pages, or ten thousand changing daily). The third isn't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Sites with a large portion of their total URLs classified by Search Console as Discovered - currently not indexed"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Google is naming the status itself as a qualifying condition, independent of catalog size. A four-hundred-product store isn't a large site by any normal reading, but if its Page indexing report is dominated by this status, it's in scope. The real question isn't how many products the client sells, it's how many addresses the store hands out for them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does a Shopify store have more URLs than products?
&lt;/h2&gt;

&lt;p&gt;A Shopify store doesn't present one URL per product. It presents the canonical product URL plus every alternative address the platform, the theme, and installed apps are willing to link. Three mechanisms account for most of it, and none is visible in the admin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where do the recommendation tracking parameters come from?
&lt;/h3&gt;

&lt;p&gt;Shopify's &lt;a href="https://shopify.dev/docs/api/ajax/reference/product-recommendations" rel="noopener noreferrer"&gt;Product Recommendations API reference&lt;/a&gt; documents this directly: "the &lt;code&gt;url&lt;/code&gt; property for each &lt;code&gt;product&lt;/code&gt; in the products response contains URL parameters that lets you build a conversion funnel." The example URL in Shopify's own docs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/products/gorgeous-wooden-computer?pr_choice=default&amp;amp;pr_prod_strat=description&amp;amp;pr_rec_pid=13&amp;amp;pr_ref_pid=17&amp;amp;pr_seq=alternating
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those parameters exist so Shopify's reports can attribute a sale to a recommendation strategy. The side effect: the same product page gets a distinct address for every combination of referring product and strategy. A &lt;a href="https://community.shopify.com/c/technical-q-a/how-can-i-stop-unwanted-urls-from-being-indexed-in-google/td-p/2489745" rel="noopener noreferrer"&gt;Shopify Community thread from March 2024&lt;/a&gt; describes roughly 80,000 parameter URLs discovered on one store. A &lt;a href="https://ed.codes/blog/weird-shopify-seo-issue-millions-of-indexed-pages" rel="noopener noreferrer"&gt;case study by developer Eduard Fastovski&lt;/a&gt; describes a 200-product store that accumulated over a million indexed URLs (plus several million more not indexed) from &lt;code&gt;?pr_prod&lt;/code&gt; and &lt;code&gt;?filter&lt;/code&gt; parameters; after blocking both patterns: "It worked! Down to just 1k pages now, from 5 million!"&lt;/p&gt;

&lt;p&gt;What lets this persist: &lt;strong&gt;Shopify's default robots.txt blocks the recommendations &lt;em&gt;endpoint&lt;/em&gt;, not the URLs it hands out.&lt;/strong&gt; Checking the live robots.txt of two unrelated Shopify stores shows the standard &lt;code&gt;Disallow: /recommendations/products&lt;/code&gt; rule, and no rule matching &lt;code&gt;pr_prod_strat&lt;/code&gt;, &lt;code&gt;pr_rec_id&lt;/code&gt;, &lt;code&gt;pr_rec_pid&lt;/code&gt;, &lt;code&gt;pr_ref_pid&lt;/code&gt;, or &lt;code&gt;pr_seq&lt;/code&gt;. The Ajax endpoint is closed; every product URL carrying those params, wherever it's linked from (a theme section, an app, a third-party sitemap), stays fully crawlable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do collection-scoped product URLs duplicate every product?
&lt;/h3&gt;

&lt;p&gt;Shopify creates a second address whenever a product card links through its collection: &lt;code&gt;/collections/summer-sale/products/linen-shirt&lt;/code&gt; alongside &lt;code&gt;/products/linen-shirt&lt;/code&gt;. Shopify's own &lt;a href="https://shopify.dev/docs/api/liquid/filters/within" rel="noopener noreferrer"&gt;Liquid reference for the &lt;code&gt;within&lt;/code&gt; filter&lt;/a&gt; warns: "Because a standard product page and a product page in the context of a collection have the same content on separate URLs, you should consider the SEO implications of using the within filter."&lt;/p&gt;

&lt;p&gt;A product sitting in six collections can be reached at seven addresses. The default robots.txt blocks sorted and filtered collection URLs (&lt;code&gt;Disallow: /collections/*sort_by*&lt;/code&gt;, &lt;code&gt;Disallow: /collections/*+*&lt;/code&gt;), but nothing blocks &lt;code&gt;/collections/*/products/*&lt;/code&gt;. Those URLs carry a canonical tag pointing back at the clean product URL, which is correct, but a canonical is only readable after a fetch. Consolidating them still costs a crawl each.&lt;/p&gt;

&lt;h3&gt;
  
  
  What URLs are your client's apps adding without telling anyone?
&lt;/h3&gt;

&lt;p&gt;The default file blocks named patterns, not parameters in general: &lt;code&gt;oseid&lt;/code&gt;, &lt;code&gt;preview_theme_id&lt;/code&gt;, &lt;code&gt;preview_script_id&lt;/code&gt;, a specific duplicated &lt;code&gt;ls=&lt;/code&gt; shape, sorted/filtered collections, and &lt;code&gt;/search&lt;/code&gt;. Any parameter an app or theme introduces outside those patterns is crawlable by default: a filter app with its own query format, a "recently viewed" widget, a wishlist link, a campaign parameter that leaks into an internal link instead of staying outbound-only. No app declares its URL footprint at install time, so this is invisible until it shows up in Search Console weeks later.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you check a client's store in five minutes?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read the shapes, not the count.&lt;/strong&gt; Open Page indexing, select &lt;em&gt;Discovered - currently not indexed&lt;/em&gt;, and bucket the sample URLs: clean &lt;code&gt;/products/…&lt;/code&gt;, collection-scoped &lt;code&gt;/collections/…/products/…&lt;/code&gt;, and anything with a &lt;code&gt;?&lt;/code&gt;. The proportions tell you which mechanism above is dominant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare what Google knows against what the sitemap declares.&lt;/strong&gt; Add the indexed and not-indexed totals, then open &lt;code&gt;yourstore.com/sitemap.xml&lt;/code&gt;. Shopify auto-generates it with &lt;a href="https://help.shopify.com/en/manual/promoting-marketing/seo/find-site-map" rel="noopener noreferrer"&gt;separate child sitemaps&lt;/a&gt; for products, collections, blogs, and pages. If Google knows an order of magnitude more URLs than the sitemap declares, the surplus arrived through internal links.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;View source on a product page and search for &lt;code&gt;pr_prod_strat&lt;/code&gt;.&lt;/strong&gt; If the theme renders a recommendations block into the page, the parameters sit in the &lt;code&gt;href&lt;/code&gt; attributes. Search for &lt;code&gt;pr_rec_pid&lt;/code&gt; too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read &lt;code&gt;yourstore.com/robots.txt&lt;/code&gt; properly.&lt;/strong&gt; Check whether anything blocks the parameter shapes from step 3, and whether a past edit went further than intended. Shopify's &lt;a href="https://help.shopify.com/en/manual/promoting-marketing/seo/editing-robots-txt" rel="noopener noreferrer"&gt;own help page&lt;/a&gt; warns that "Incorrect use of the feature can result in loss of all traffic."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspect one clean product URL, not fifty.&lt;/strong&gt; Run URL Inspection and note the last crawl date. Google's &lt;a href="https://support.google.com/webmasters/answer/9012289" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; states this "is not a live test" and that there's "a daily limit of inspection requests for each property." One URL is a diagnosis; a full catalog isn't auditable this way.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How do you fix each cause?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Parameter URLs.&lt;/strong&gt; Two real options, with a trade-off. Strip the parameters at the theme level (loses the conversion-funnel attribution those params exist for), or block the shapes in &lt;code&gt;robots.txt.liquid&lt;/code&gt; (keeps tracking working for real visitors, stops Google from fetching them). Google's guidance is explicit about method: "Block crawling of URLs using robots.txt," and separately, "Don't use noindex, as Google will still request, but then drop the page when it sees a noindex meta tag or header in the HTTP response, wasting crawling time." A noindex on a parameter URL is the intuitive fix and it makes the crawl problem worse. Edit &lt;code&gt;robots.txt.liquid&lt;/code&gt; carefully and re-check it after theme updates, since it's theme code and a theme change can silently overwrite your edit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collection-scoped duplicates.&lt;/strong&gt; Where a product card doesn't need collection context, link to the plain product URL instead of the collection-scoped one. Google's crawl guidance: "Eliminate duplicate content to focus crawling on unique content rather than unique URLs."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Products that are genuinely gone.&lt;/strong&gt; Return a 404 or 410, per Google's guidance, rather than redirecting a deleted product's URL to something convenient, which just keeps a competing URL alive in the queue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server response.&lt;/strong&gt; Google's own definition of the status mentions overloading the site as a possible cause. Efficient page loads, HTTP caching, and 304 responses all matter here; heavy third-party scripts and app-injected assets are usually the biggest levers you actually control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What not to do:&lt;/strong&gt; don't work down the list requesting indexing on every URL. Google states "Submitting a request does not guarantee that the page will appear in the Google Index," and the daily limits make a catalog-sized pass impossible anyway. It's a diagnostic tool, not a remedy, and it leaves the underlying crawl surface untouched.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does this bug keep coming back after you fix it?
&lt;/h2&gt;

&lt;p&gt;Every mechanism above comes from ordinary store maintenance, not a one-time mistake. A merchandiser adds three collections and every product in them gains three addresses. A theme update ships a new recommendations section. An app adds a filter with a query format nobody reviewed. A seasonal range gets deleted and redirected instead of allowed to 404. None of it announces itself, and the crawl surface only grows unless someone deliberately shrinks it. Worth building a recurring check into your maintenance routine (robots.txt diff after theme updates, periodic sitemap-vs-indexed count) rather than treating this as a one-time cleanup.&lt;/p&gt;

&lt;p&gt;Has anyone found other URL shapes leaking through a Shopify store's default robots.txt, beyond the recommendation params and collection-scoped duplicates covered here?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The full version of this article — with screenshots and ongoing updates — lives on &lt;a href="https://storecanary.io/blog/discovered-currently-not-indexed-shopify" rel="noopener noreferrer"&gt;the StoreCanary blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>webdev</category>
      <category>seo</category>
      <category>googlesearch</category>
    </item>
    <item>
      <title>The Shopify structured data bug that gets clients suspended</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Mon, 31 Aug 2026 11:56:45 +0000</pubDate>
      <link>https://dev.to/max_buildogs/the-shopify-structured-data-bug-that-gets-clients-suspended-4l62</link>
      <guid>https://dev.to/max_buildogs/the-shopify-structured-data-bug-that-gets-clients-suspended-4l62</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Merchant Center suspends an account for "Misrepresentation" without naming a product and without a warning period; Google's own policy states accounts are "suspended upon detection and without prior warning."&lt;/li&gt;
&lt;li&gt;The policy has a clause unrelated to business identity: it forbids offering products you can't deliver and points directly at the availability and price attributes in Google's product data specification.&lt;/li&gt;
&lt;li&gt;On a Shopify build, those two attributes come from four separate mechanisms (theme, the &lt;code&gt;structured_data&lt;/code&gt; Liquid filter, any app injecting its own markup, and the feed), and nothing forces them to agree.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build or maintain Shopify stores for clients, this is the bug report you never get. Merchant Center suspends an account for "Misrepresentation," with no product name, no line item, no warning period, just Shopping ads and free listings dropping to zero. Most of what ranks for this error points at business identity: refund policy, contact page, real business name at checkout. Worth checking, genuinely required, and not the whole story.&lt;/p&gt;

&lt;p&gt;The same policy also forbids offering products a shopper can't actually buy, and it refers directly to two feed attributes: availability and price. On a Shopify build those values come from four different places, and nothing reconciles them automatically. This piece covers where that agreement breaks in a typical Shopify build, how to audit a client's store for it in a few minutes, and what to fix before requesting Google's re-review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why doesn't Merchant Center name the product that broke the rule?
&lt;/h2&gt;

&lt;p&gt;Most Merchant Center issues arrive with a warning period and specific examples. &lt;a href="https://support.google.com/merchants/answer/2948694" rel="noopener noreferrer"&gt;Google's documentation on warnings and suspensions&lt;/a&gt; describes products continuing to appear "however their performance may be limited," with emails pointing at what to fix. Misrepresentation doesn't work that way. Its &lt;a href="https://support.google.com/merchants/answer/6150127" rel="noopener noreferrer"&gt;policy page&lt;/a&gt; classifies these violations as egregious and states that accounts "will be suspended upon detection and without prior warning." Shopify's own &lt;a href="https://help.shopify.com/en/manual/online-sales-channels/google/getting-setup/product-disapprovals-warnings" rel="noopener noreferrer"&gt;page on Google disapprovals and warnings&lt;/a&gt; lists this under "Unacceptable business practices" and notes that "Deadline extensions aren't granted for any violation."&lt;/p&gt;

&lt;p&gt;So there's no missing notification setting to find. The enforcement acts first and explains later, which leaves you reading the policy text itself instead of an email with an example. A &lt;a href="https://community.shopify.com/t/a-shopify-store-owner-is-struggling-with-a-persistent-google-merchant-center-misrepresentation-suspension/590225" rel="noopener noreferrer"&gt;Shopify Community thread opened in February 2026&lt;/a&gt; and still active months later captures it directly: Google "continues to flag my account for 'Misrepresentation' without providing specific details on what's wrong or how to fix it."&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the misrepresentation policy actually require?
&lt;/h2&gt;

&lt;p&gt;Most guides for this suspension point at business identity. Shopify's &lt;a href="https://help.shopify.com/en/manual/online-sales-channels/marketplaces/google/requirements" rel="noopener noreferrer"&gt;requirements page for the Google &amp;amp; YouTube channel&lt;/a&gt; spells it out: refund policy and terms of service "available in your footer menu," plus contact information "visible to customers" via "at least one contact method such as email address, phone number, mailing address, or a contact form." Check that first, it's cheap and well documented.&lt;/p&gt;

&lt;p&gt;But the policy has a second clause with nothing to do with identity: "Offer products that you don't have or can't deliver" is listed as an unacceptable business practice, and the unavailable-offers section forbids "Promising products or promotional offers that aren't available for users" (examples: promoting unstocked products, expired deals, or a call-to-action that "isn't easily achievable from the landing page"). The policy then refers you to the product data specification's availability and price attributes for "specific guidelines to comply with this policy."&lt;/p&gt;

&lt;p&gt;That &lt;a href="https://support.google.com/merchants/answer/7052112" rel="noopener noreferrer"&gt;specification&lt;/a&gt; asks you to "accurately submit the product's availability and match the availability from your landing page, checkout pages, and structured data," and the same for price. Four surfaces (feed, landing page, structured data, checkout) have to agree with each other and with what you can actually ship. On a Shopify store those four are produced by four different mechanisms, and nothing reconciles them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does a Shopify build break that requirement?
&lt;/h2&gt;

&lt;p&gt;These are mechanisms that can make the four surfaces disagree, not proof of what triggered any specific suspension. Google doesn't publish which signal caused a given account action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Continue selling when out of stock
&lt;/h3&gt;

&lt;p&gt;Shopify's &lt;a href="https://help.shopify.com/en/manual/products/inventory/setup/selling-when-out-of-stock" rel="noopener noreferrer"&gt;docs on selling out-of-stock products&lt;/a&gt; describe this setting plainly: enable it in the Inventory section of a product or variant, and customers can buy at zero inventory or below. It's a legitimate setting for stock arriving soon or inventory tracked elsewhere, but with it on, every surface agrees on the wrong answer. The page offers the item, add-to-cart works, the structured data reports it available, the feed says in stock. Nothing is internally inconsistent, the store is just promising goods it doesn't hold, and the spec asks for availability that's accurate, not merely consistent.&lt;/p&gt;

&lt;p&gt;The setting is per-variant, too. Toggle it on during a stockout, sell through, restock, and it stays on for that one variant with nobody noticing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Duplicate or stale structured data
&lt;/h3&gt;

&lt;p&gt;Shopify generates Product markup through the Liquid &lt;a href="https://shopify.dev/docs/api/liquid/filters/structured_data" rel="noopener noreferrer"&gt;&lt;code&gt;structured_data&lt;/code&gt; filter&lt;/a&gt;, which emits an offer object like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"offers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/products/crocodile-tears?variant=39888242344001#offer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Offer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"availability"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://schema.org/OutOfStock"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"56.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"priceCurrency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CAD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://.../products/crocodile-tears?variant=39888242344001"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a stock theme that availability value tracks the variant correctly. It breaks the moment a second &lt;code&gt;Product&lt;/code&gt; block enters the page, from a review app, a rich-snippet app, or a theme section with a hardcoded &lt;code&gt;InStock&lt;/code&gt; value, and Google reads a claim the store never intended to make. This is the account-level version of a mismatch covered in more detail at the item level, in &lt;a href="https://storecanary.io/blog/shopify-merchant-center-availability-mismatch" rel="noopener noreferrer"&gt;the availability mismatch between Shopify and Google Shopping&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  One offer, many prices
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;structured_data&lt;/code&gt; filter emits a single offer, for a single variant, with a single price. A product whose variants span a wide range declares only one of those prices in its markup. A shopper landing on a different variant sees a different number than the one Google received, and the spec requires price to match the landing page, structured data, and checkout. Related failure modes: &lt;a href="https://storecanary.io/blog/merchant-center-price-mismatch-shopify" rel="noopener noreferrer"&gt;the on-page price mismatch trap&lt;/a&gt; and &lt;a href="https://storecanary.io/blog/invalid-value-category-price-shopify" rel="noopener noreferrer"&gt;a price of zero reaching Google&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Redirects on discontinued product URLs
&lt;/h3&gt;

&lt;p&gt;The unavailable-offers clause also covers the landing page itself: a call-to-action that "isn't easily achievable from the landing page." On Shopify this usually means an out-of-stock or discontinued product URL redirecting to a collection or the homepage instead of returning the product page, so a shopper who clicks through from a listing can't reach the item at all. More detail on that failure mode: &lt;a href="https://storecanary.io/blog/shopify-product-page-redirect-merchant-center" rel="noopener noreferrer"&gt;when a product page redirects to your homepage&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you audit a client's store in five minutes?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Copy the exact issue name from the policy area of Merchant Center. An account-level misrepresentation issue reads differently from a product-level disapproval, and the fix differs too.&lt;/li&gt;
&lt;li&gt;Pick five out-of-stock products and compare four surfaces for each: what the feed reports, what the page shows, the &lt;code&gt;availability&lt;/code&gt; value in the page's JSON-LD, and what happens on add-to-cart.&lt;/li&gt;
&lt;li&gt;Check the Continue selling setting on the Inventory section of each product or variant.&lt;/li&gt;
&lt;li&gt;View source on a product with a wide variant range and read the price inside the &lt;code&gt;offers&lt;/code&gt; object; compare it to the cheapest and most expensive variant.&lt;/li&gt;
&lt;li&gt;Open a discontinued product's URL directly and check whether it redirects instead of returning the product page.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How do you fix each issue?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Overselling.&lt;/strong&gt; Decide deliberately which products may oversell, and turn the setting off everywhere else. For products that genuinely ship later, use the spec's &lt;code&gt;preorder&lt;/code&gt; or &lt;code&gt;backorder&lt;/code&gt; availability values instead of claiming stock that isn't there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Duplicate markup.&lt;/strong&gt; Find the second &lt;code&gt;Product&lt;/code&gt; block before editing the first. It's usually coming from an app, and removing the app's block is easier than trying to make the theme's output match it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Price coverage.&lt;/strong&gt; If variants span a wide range, the real fix is markup that declares the range instead of a single figure. Until that's built, know which products are affected before touching the theme.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Redirects.&lt;/strong&gt; Let a product that no longer sells keep returning its own page (out of stock, priced) instead of redirecting away. The spec notes that even when a product is out of stock, "the price must still be clearly visible on the landing page," which only works if the page still exists.&lt;/p&gt;

&lt;p&gt;Then request the review, not before. Google's docs and Shopify's both give the same figure: reviews "can take up to 7 days to complete." A re-review on a store that hasn't actually changed spends a week to land back where it started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does this bug keep coming back after launch?
&lt;/h2&gt;

&lt;p&gt;Every mechanism above comes from ordinary work, not a code review failure. A stockout on a Friday turns on Continue selling, and the restock doesn't turn it back off. A new reviews app adds a second markup block. A supplier drops a line and someone redirects the URL to keep things tidy. A variant gets added well above the price sitting in the markup. None of it throws an error in Shopify admin, and none of it throws one in Merchant Center either, because for this policy Google doesn't warn, it suspends.&lt;/p&gt;

&lt;p&gt;It's worth being precise about what an automated scan can actually tell you here. The misrepresentation policy covers business identity, intent, and whether a merchant can really ship what's listed, none of which is readable from public pages. What is readable is the product-data half the policy points at: a page whose markup claims &lt;code&gt;InStock&lt;/code&gt; while every variant shows sold out, or a page whose declared price contradicts what the page itself renders. That's one input into an account-level judgment, not the judgment itself. Treat anything sold to you as a "misrepresentation checker" with the same skepticism.&lt;/p&gt;

&lt;p&gt;Have you shipped a Shopify feature that quietly broke a client's availability or price data without throwing an error? What caught it: an audit, a client complaint, or a Merchant Center suspension?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The full version of this article — with screenshots and ongoing updates — lives on &lt;a href="https://storecanary.io/blog/merchant-center-misrepresentation-shopify" rel="noopener noreferrer"&gt;the StoreCanary blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>webdev</category>
      <category>seo</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>The Shopify structured data bug your JSON-LD checks miss</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Thu, 27 Aug 2026 11:56:07 +0000</pubDate>
      <link>https://dev.to/max_buildogs/the-shopify-structured-data-bug-your-json-ld-checks-miss-4e9j</link>
      <guid>https://dev.to/max_buildogs/the-shopify-structured-data-bug-your-json-ld-checks-miss-4e9j</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search Console's "unparsable structured data" means a syntax error made Google discard the &lt;em&gt;entire&lt;/em&gt; markup block, not just one bad field, and Google is explicit that every item in that report is a critical error, never a warning.&lt;/li&gt;
&lt;li&gt;On Shopify, the usual causes are three specific mistakes: a trailing comma, unescaped characters (quotes, line breaks) inside a product title or description, or two JSON-LD objects concatenated in the same &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag without an array wrapper.&lt;/li&gt;
&lt;li&gt;You can tell whether the theme or an app produced the broken block by copying a text fragment from it and searching for that fragment in &lt;strong&gt;Online Store → Themes → Edit code&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build or maintain Shopify storefronts for clients, you've probably seen this line in Search Console and had no clean way to trace it: "unparsable structured data." It's one of the few reports where Google states severity plainly, every item listed is a critical failure, not a warning. The frustrating part is that nothing on the rendered page looks broken. No console error, no visual regression, no failed build. A single stray character in a Liquid snippet, or a JSON-LD block an app injects at runtime, quietly invalidates the whole document, and the client's product schema (price, availability, rating) disappears from how Google reads the page, with no trace in a normal QA pass.&lt;/p&gt;

&lt;p&gt;This post covers the three syntax errors that actually cause this on Shopify, how to figure out whether it's your theme code or a third-party app, and how to fix and revalidate it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does "unparsable structured data" actually mean?
&lt;/h2&gt;

&lt;p&gt;Google's own definition is unusually direct. The report &lt;a href="https://support.google.com/webmasters/answer/9166415" rel="noopener noreferrer"&gt;lists structured data that "could not be parsed because of a serious syntax error"&lt;/a&gt;, and "the intended type of structured data (Job, Event, and so on) could not be determined because of the parsing error." Google isn't flagging a schema mismatch here. It saw a markup block, failed to parse it as JSON, and couldn't even tell what it was supposed to describe.&lt;/p&gt;

&lt;p&gt;The same page adds a detail worth building process around: "All items in this report are critical structured data errors; there are no warnings or valid items." There's no severity triage to do. Anything that shows up here is broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the client actually lose when a block fails to parse?
&lt;/h2&gt;

&lt;p&gt;The page stays indexed and keeps ranking on its text content. What disappears is everything that specific block declared. If the failing block was the &lt;code&gt;Product&lt;/code&gt; markup, the price, availability, and rating it carried are simply absent as far as Google is concerned, and the page loses eligibility for the rich result it used to qualify for. Where a Merchant Center feed depends on on-page data, the product can drop out of that feed too.&lt;/p&gt;

&lt;p&gt;This is why the bug usually surfaces sideways: someone notices review stars vanished from a listing, or a client asks why a competitor's price shows up in search but theirs doesn't, and the root cause traces back to a schema block that stopped parsing weeks earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which syntax errors cause this on Shopify themes?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Two JSON documents concatenated.&lt;/strong&gt; The theme writes one &lt;code&gt;Product&lt;/code&gt; object into a &lt;code&gt;&amp;lt;script type="application/ld+json"&amp;gt;&lt;/code&gt; tag, and an app appends a second object into the same tag with nothing separating them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/ld+json"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Wool Beanie&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AggregateRating&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ratingValue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;4.5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two valid objects placed back to back are not valid JSON. Both get discarded. The fix is either separate &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags or wrapping both in a &lt;code&gt;@graph&lt;/code&gt; array.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A trailing comma.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Offer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"24.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"priceCurrency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That trailing comma after &lt;code&gt;"USD"&lt;/code&gt; invalidates the entire document, not just that line. It's the single most common hand-edit mistake in a Liquid snippet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unescaped characters inside a value.&lt;/strong&gt; A product title containing a straight double quote or a line break, interpolated into JSON without escaping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight liquid"&gt;&lt;code&gt;"name": "&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;product.title&lt;/code&gt; is &lt;code&gt;Men's "Classic" Wool Beanie&lt;/code&gt;, that renders broken JSON. This one is the sneakiest because it only breaks on the products whose text happens to contain the offending character, so it looks intermittent until you spot the pattern. The reliable fix is Shopify's &lt;code&gt;json&lt;/code&gt; filter instead of raw interpolation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight liquid"&gt;&lt;code&gt;"name": &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;| json&lt;/code&gt; handles escaping for you and also gets you correct quoting (no manual &lt;code&gt;".."&lt;/code&gt; wrapper needed).&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you find whether the theme or an app injected the broken block?
&lt;/h2&gt;

&lt;p&gt;Once a validator has shown you the failing block, copy a distinctive text fragment from it and search for that fragment in &lt;strong&gt;Online Store → Themes → Edit code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If the fragment is in the theme files, the theme is generating the block and you can fix it directly. If it appears on the rendered page but nowhere in the theme source, an app is injecting it at runtime, editing theme code won't touch it. The fix in that case is in the app's own settings (most reviews, SEO, and social apps have a toggle for structured data output) or a support ticket with the app vendor.&lt;/p&gt;

&lt;p&gt;This distinction matters for maintenance, not just for the immediate fix. A theme fix stays fixed until someone edits the theme again. An app fix can be silently undone by the app's next update, so it's worth re-checking after any app update rather than treating it as a one-time fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you check and fix it end to end?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Paste the affected product URL into &lt;a href="https://validator.schema.org" rel="noopener noreferrer"&gt;validator.schema.org&lt;/a&gt; and read the exact parsing error, plus which block it belongs to.&lt;/li&gt;
&lt;li&gt;Copy a unique fragment from that block and search for it in &lt;strong&gt;Online Store → Themes → Edit code&lt;/strong&gt; to determine theme vs. app.&lt;/li&gt;
&lt;li&gt;If it's the theme: remove the trailing comma, switch raw interpolation to &lt;code&gt;| json&lt;/code&gt;, and wrap multiple objects in an array instead of concatenating them.&lt;/li&gt;
&lt;li&gt;If it's an app: disable its structured data injection in its settings, or send its support team the validator output.&lt;/li&gt;
&lt;li&gt;Revalidate on validator.schema.org, cross-check with the &lt;a href="https://search.google.com/test/rich-results" rel="noopener noreferrer"&gt;Rich Results Test&lt;/a&gt;, then open the error in Search Console and click &lt;strong&gt;Validate Fix&lt;/strong&gt;. Google notes validation "typically takes up to about two weeks, but in some cases can take much longer," the fix itself is live immediately, only the report is slow to catch up.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because the symptom is invisible on the page, the useful habit isn't checking when something looks wrong, it's checking after any change that touches the theme or the app list. A theme update, a new reviews app, a bulk product import: any of those can introduce a stray character into a block that had parsed cleanly for a year.&lt;/p&gt;

&lt;p&gt;Have you traced one of these back to a specific app before? Curious which ones are the repeat offenders in your stack.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The full version of this article, with screenshots and ongoing updates, lives on &lt;a href="https://storecanary.io/blog/unparsable-structured-data-shopify" rel="noopener noreferrer"&gt;the StoreCanary blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>webdev</category>
      <category>seo</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Shopify Product schema gap that breaks shippingDetails</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Wed, 26 Aug 2026 11:56:54 +0000</pubDate>
      <link>https://dev.to/max_buildogs/the-shopify-product-schema-gap-that-breaks-shippingdetails-2gi3</link>
      <guid>https://dev.to/max_buildogs/the-shopify-product-schema-gap-that-breaks-shippingdetails-2gi3</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shopify's built-in Liquid &lt;code&gt;structured_data&lt;/code&gt; filter never emits &lt;code&gt;shippingDetails&lt;/code&gt; or &lt;code&gt;hasMerchantReturnPolicy&lt;/code&gt; inside the &lt;code&gt;Offer&lt;/code&gt; object, on any theme, out of the box. It's a filter limitation, not a misconfiguration on a specific client site.&lt;/li&gt;
&lt;li&gt;Both fields are "recommended," not "required." Search Console still lists the product as valid; the only thing missing is the shipping/returns annotation on the listing itself.&lt;/li&gt;
&lt;li&gt;Google reads shipping/returns data from four sources in strict precedence (Content API for Shopping, then Merchant Center/Search Console settings, then product-level markup, then Organization markup). Pasting a snippet into a client's theme often changes nothing if a higher-priority source is already set, and only a Shopify store's "General" shipping profile syncs correctly to Merchant Center.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build or maintain Shopify stores for clients, you've probably run into this Search Console warning: &lt;code&gt;shippingDetails&lt;/code&gt; and &lt;code&gt;hasMerchantReturnPolicy&lt;/code&gt; missing from Merchant listings, across an entire catalog, on a store you just launched. It's not a content problem on the client's end. Shopify's built-in Liquid &lt;code&gt;structured_data&lt;/code&gt; filter never emits these fields in the &lt;code&gt;Offer&lt;/code&gt; object, on any theme, by default. The usual fix, pasting a shipping/returns block into the product template, is a real fix for a real symptom, but it often targets a layer Google isn't even reading. This piece covers why the fields are absent, why they don't actually invalidate a product, the four-source precedence order Google uses for shipping and returns data, a Shopify-specific shipping profile trap that silently desyncs rates from Merchant Center, and a five-minute audit you can run on any client account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why doesn't Shopify's offer markup include shipping and returns fields?
&lt;/h2&gt;

&lt;p&gt;Shopify generates product JSON-LD through the Liquid &lt;a href="https://shopify.dev/docs/api/liquid/filters/structured_data" rel="noopener noreferrer"&gt;&lt;code&gt;structured_data&lt;/code&gt; filter&lt;/a&gt;, and its documented output is short. Inside &lt;code&gt;offers&lt;/code&gt; it emits exactly six properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"offers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/products/crocodile-tears?variant=39888242344001#offer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Offer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"availability"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://schema.org/OutOfStock"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"56.00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"priceCurrency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CAD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://…/products/crocodile-tears?variant=39888242344001"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No &lt;code&gt;shippingDetails&lt;/code&gt;, no &lt;code&gt;hasMerchantReturnPolicy&lt;/code&gt;, no &lt;code&gt;priceValidUntil&lt;/code&gt;. This isn't something you find and undo in the theme code: it's absent by construction on any theme using the built-in filter, which offers no option to add it. That's why the warning covers a client's whole catalog on day one, on a store nobody has customized yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does this actually invalidate the product for search?
&lt;/h2&gt;

&lt;p&gt;No. Google's &lt;a href="https://developers.google.com/search/docs/appearance/structured-data/merchant-listing" rel="noopener noreferrer"&gt;merchant listing structured data docs&lt;/a&gt; list both fields as recommended properties of &lt;code&gt;Offer&lt;/code&gt;, not required ones. The required set is small: &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;image&lt;/code&gt;, &lt;code&gt;offers&lt;/code&gt; on the product, plus a price and currency inside the offer.&lt;/p&gt;

&lt;p&gt;Search Console's &lt;a href="https://support.google.com/webmasters/answer/7552505" rel="noopener noreferrer"&gt;rich result reports&lt;/a&gt; reflect that split directly: critical issues sit in a table called "Why items are invalid," non-critical ones in "Improve item appearance." Google's own definition: "A valid item is an item that doesn't have any critical issues and can appear on Google as a rich result." A product flagged only for these two fields stays valid and eligible.&lt;/p&gt;

&lt;p&gt;So the real cost is narrower than the warning implies: the client loses the delivery/returns annotation Google can attach to the listing, the kind sitting under a competitor's product. That's a different failure class from a block Google can't parse at all, which gets discarded outright along with everything inside it.&lt;/p&gt;

&lt;h2&gt;
  
  
  If I paste the markup into the theme, why does nothing change on the listing?
&lt;/h2&gt;

&lt;p&gt;Because Google reads shipping and returns data from four sources, in a fixed order of precedence, per its docs for &lt;a href="https://developers.google.com/search/docs/appearance/structured-data/return-policy" rel="noopener noreferrer"&gt;return policy&lt;/a&gt; and &lt;a href="https://developers.google.com/search/docs/appearance/structured-data/shipping-policy" rel="noopener noreferrer"&gt;shipping policy&lt;/a&gt; structured data:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Content API for Shopping account-level settings&lt;/li&gt;
&lt;li&gt;Settings in Merchant Center or Search Console&lt;/li&gt;
&lt;li&gt;Product-level merchant listing markup (the snippet you'd paste into the theme)&lt;/li&gt;
&lt;li&gt;Organization-level markup&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Google states the consequence directly: "if you provide both return policy markup on your site and return policy settings in Search Console, Google will only use the information provided in Search Console." The shipping docs say the same about shipping.&lt;/p&gt;

&lt;p&gt;If the store is connected to Merchant Center, which the Google &amp;amp; YouTube sales channel requires, level 2 is already occupied, and the theme edit only reaches level 3. It's not wasted work: it clears the Search Console warning, it helps any engine reading the page directly, and it's the only option for a store with neither Merchant Center nor Search Console settings configured. But it explains a pattern that looks like a bug and isn't one: the snippet goes in, the warning clears, and the actual listing doesn't move.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does Google actually want this markup, if you implement it at all?
&lt;/h2&gt;

&lt;p&gt;Not where most implementations put it. Google's own recommendation, repeated for both fields on the merchant listing page: "We recommend you provide a global shipping policy for your business under &lt;code&gt;Organization&lt;/code&gt; markup instead." A standard, business-wide policy belongs in &lt;code&gt;Organization&lt;/code&gt; markup (&lt;code&gt;ShippingService&lt;/code&gt; under &lt;code&gt;hasShippingService&lt;/code&gt;, &lt;code&gt;MerchantReturnPolicy&lt;/code&gt; for returns); per-product &lt;code&gt;Offer&lt;/code&gt; markup exists to override that default for a product that genuinely differs, and it supports a smaller set of properties than the Organization-level version.&lt;/p&gt;

&lt;p&gt;The common Shopify pattern, a shipping block duplicated into every product page by a Liquid loop, is the exception case implemented as the default, at the lowest-priority level.&lt;/p&gt;

&lt;p&gt;There's also a newer path, from Google's November 2025 post &lt;a href="https://developers.google.com/search/blog/2025/11/more-ways-to-share-shipping" rel="noopener noreferrer"&gt;"More ways to share your shipping and returns policies with Google"&lt;/a&gt;: set the policies directly in Search Console under &lt;strong&gt;Settings &amp;gt; Shopping &amp;gt; Shipping and returns&lt;/strong&gt;. Its &lt;a href="https://support.google.com/webmasters/answer/14907594" rel="noopener noreferrer"&gt;help page&lt;/a&gt; states "You don't need to create a Merchant Center account to configure shipping and returns," but also that policies "created or edited in Merchant Center won't be available in Search Console, and can only be managed in Merchant Center." Once a client connects the Google &amp;amp; YouTube channel, that door closes behind them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does the shipping rate Google shows differ from what the client charges?
&lt;/h2&gt;

&lt;p&gt;This is the Shopify-specific trap underneath the fix, documented by Shopify rather than Google. From the &lt;a href="https://help.shopify.com/en/manual/online-sales-channels/google/requirements" rel="noopener noreferrer"&gt;Google &amp;amp; YouTube channel requirements&lt;/a&gt;: "Only shipping rates in your General shipping profile can sync to Google Merchant Center. If you use custom shipping profiles, then rates will sync incorrectly with Google Merchant Center and cause errors."&lt;/p&gt;

&lt;p&gt;Custom shipping profiles are ordinary store maintenance: one for oversized items, one for a dropshipped line, one for a supplier with different delivery times. The moment a product moves into one, the rate Google receives stops matching checkout. Nothing is technically wrong in the store; the rates just aren't the ones being synced.&lt;/p&gt;

&lt;p&gt;Worth checking before touching anything else: Merchant Center can infer a return policy without an explicit upload, shown as a &lt;a href="https://support.google.com/merchants/answer/14011730" rel="noopener noreferrer"&gt;"For most items"&lt;/a&gt; annotation. A client's listings may already display returns info while Search Console keeps flagging the field missing from theme markup, this whole article's argument compressed into one symptom.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you audit this on a client's store in five minutes?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read the actual markup.&lt;/strong&gt; View source on a product page, find the JSON-LD block, look inside &lt;code&gt;offers&lt;/code&gt;. Six properties and nothing about shipping or returns means the store is on the stock filter output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check which table the warning sits in.&lt;/strong&gt; Merchant listings report in Search Console: "Improve item appearance" means non-critical; "Why items are invalid" means something else is broken.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find the authoritative source.&lt;/strong&gt; Is a Merchant Center account connected via the Google &amp;amp; YouTube channel? Is there already a policy under Settings &amp;gt; Shopping &amp;gt; Shipping and returns in Search Console? Whichever exists outranks theme markup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare the synced rate against the real one.&lt;/strong&gt; Check Merchant Center's shipping settings against what a shopper is actually charged at checkout for the same product and destination.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the Shopify shipping profiles.&lt;/strong&gt; Settings &amp;gt; Shipping and delivery. Any product sitting in a custom profile instead of General is a rate Google isn't receiving correctly.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What's the actual fix, and does it depend on the client's setup?
&lt;/h2&gt;

&lt;p&gt;If they use the Google &amp;amp; YouTube channel: set shipping and returns in Merchant Center and treat it as the source of truth. Keep the rates Google needs in the General shipping profile, or enter them manually in Merchant Center instead of importing (safer once custom profiles exist). Theme markup on top of this is cosmetic, not the fix.&lt;/p&gt;

&lt;p&gt;If there's no Merchant Center account: use Settings &amp;gt; Shopping &amp;gt; Shipping and returns in Search Console. It outranks markup and is far less fragile than theme code.&lt;/p&gt;

&lt;p&gt;If you're implementing the markup anyway (for engines reading the page directly, or because neither of the above exists): declare the standard policy once, sitewide, in &lt;code&gt;Organization&lt;/code&gt; markup, and reserve per-product &lt;code&gt;Offer&lt;/code&gt; markup for genuine deviations. Whatever gets declared has to match what the client actually charges. A hardcoded "free shipping" block on a store that charges for shipping is worse than the original warning: it's a promise to the shopper that checkout then breaks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does this keep coming back after you close the ticket?
&lt;/h2&gt;

&lt;p&gt;Four sources, a documented precedence order between them, and no single screen that tells you which one Google used today. A theme update replaces the file holding the markup. A new supplier shows up and someone creates a custom shipping profile. Someone edits rates in Merchant Center and the store's live rates drift from them. Each change is routine, done correctly, in isolation, and none of it throws an error in Shopify admin.&lt;/p&gt;

&lt;p&gt;Worth flagging to clients as ongoing monitoring rather than a one-time fix: a product missing &lt;code&gt;shippingDetails&lt;/code&gt; and &lt;code&gt;hasMerchantReturnPolicy&lt;/code&gt; is informational and still sellable, but worth a periodic recheck instead of a single pull request.&lt;/p&gt;

&lt;p&gt;Have you run into this on a client's store, and did fixing Merchant Center settings actually change what shoppers saw, or just clear the warning? What caught it for you, a client complaint or a routine audit?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The full version of this article — with screenshots and ongoing updates — lives on &lt;a href="https://storecanary.io/blog/shopify-shippingdetails-hasmerchantreturnpolicy" rel="noopener noreferrer"&gt;the StoreCanary blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>webdev</category>
      <category>seo</category>
      <category>googlesearch</category>
    </item>
    <item>
      <title>The Shopify Search Console statuses that mean you shipped a bug</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Tue, 25 Aug 2026 11:57:04 +0000</pubDate>
      <link>https://dev.to/max_buildogs/the-shopify-search-console-statuses-that-mean-you-shipped-a-bug-9d4</link>
      <guid>https://dev.to/max_buildogs/the-shopify-search-console-statuses-that-mean-you-shipped-a-bug-9d4</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search Console's Page indexing report has 18 documented statuses: 15 reasons a page wasn't indexed, 2 warnings on pages indexed anyway, and 1 clean pass.&lt;/li&gt;
&lt;li&gt;On a Shopify build, most of that list (redirects, alternate canonicals, robots.txt blocks on /cart or /checkout) is normal duplicate-URL handling, not a bug.&lt;/li&gt;
&lt;li&gt;A handful of statuses (&lt;code&gt;noindex&lt;/code&gt;, wrong robots.txt rule, wrong canonical, soft 404) mean your theme, app, or migration broke indexing on a live product page, and nothing in Shopify admin will tell you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build or maintain Shopify stores for clients, you've probably had this conversation: the client says "we're not showing up on Google anymore," you open Search Console, and the Page indexing report shows hundreds of "Not indexed" URLs. Before you panic and start debugging, you need to know that most of that number is Shopify behaving exactly as designed: variant URLs, collection-scoped duplicate product URLs, tracking parameters. The report treats all 18 statuses with the same visual weight, so a completely benign one sits right above the one that means you or an app you installed accidentally deindexed a product the client is actively selling.&lt;/p&gt;

&lt;p&gt;This is a reference for telling those apart during a build, a migration, or a routine client audit: what each status means per Google's own documentation, what produces it specifically on Shopify, and which ones are worth an incident, not just a shrug.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Page indexing report actually counting?
&lt;/h2&gt;

&lt;p&gt;Every status below is quoted from &lt;a href="https://support.google.com/webmasters/answer/7440203" rel="noopener noreferrer"&gt;Google's documentation for the Page indexing report&lt;/a&gt;, read on August 15, 2026. Google updates this list over time, so treat these as current as of that date, not permanent. Three things about the report trip up devs who are new to it.&lt;/p&gt;

&lt;p&gt;It's URL-level, not product-level. One product can appear under several reasons at once: as &lt;code&gt;/products/handle&lt;/code&gt;, as &lt;code&gt;/collections/summer/products/handle&lt;/code&gt;, as &lt;code&gt;/products/handle?variant=123&lt;/code&gt;. Counting rows and comparing that number to the product count in the catalog tells you nothing.&lt;/p&gt;

&lt;p&gt;It's an aggregate that lags. The report groups URLs by the state Google last recorded, which can be stale. A page can be fixed, recrawled, and reindexed while the report still shows the old grouping. A merchant on the Shopify Community ran into exactly this: they inspected a URL listed under "Crawled - currently not indexed" and the inspection said the page was indexed, prompting a &lt;a href="https://community.shopify.com/t/google-search-console-crawled-not-currently-indexed-but-page-is/346736" rel="noopener noreferrer"&gt;confused thread in August 2024&lt;/a&gt;. Both tools were right about different points in time.&lt;/p&gt;

&lt;p&gt;URL Inspection isn't live either, unless you ask it to be. Google states plainly that an inspection result is "from most recently indexed version of a page, not the live version on the web," and that pressing &lt;strong&gt;Test live URL&lt;/strong&gt; is what makes it "a live test: the tool fetches and examines the URL in real time" (&lt;a href="https://support.google.com/webmasters/answer/9012289" rel="noopener noreferrer"&gt;URL Inspection tool docs&lt;/a&gt;). When the report, the inspection, and the live test disagree, treat that as three timestamps, not three opinions, and trust the live test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which statuses are normal on a Shopify build (and when they're not)?
&lt;/h2&gt;

&lt;p&gt;These five account for most of a typical store's "Not indexed" count. Each still has an exception worth checking during QA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alternate page with proper canonical tag.&lt;/strong&gt; Google: "This page is marked as an alternate of another page… This page correctly points to the canonical page, which is indexed." On Shopify, this is mostly &lt;code&gt;?variant=&lt;/code&gt; URLs and collection-scoped product URLs pointing back at the clean &lt;code&gt;/products/handle&lt;/code&gt;. The duplicate-URL system working as intended. Flag it if the canonical target listed is a &lt;em&gt;different&lt;/em&gt; product, or the store's &lt;code&gt;.myshopify.com&lt;/code&gt; domain instead of the storefront domain, since that means the canonical tag itself is wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Page with redirect.&lt;/strong&gt; Google: "This is a non-canonical URL that redirects to another page." Expected after handle renames, seasonal URL cleanups, and merges. Worth inspecting if a product URL redirects to the homepage or a collection instead of a replacement product; that pattern also silently drops the &lt;a href="https://storecanary.io/blog/shopify-product-page-redirect-merchant-center" rel="noopener noreferrer"&gt;linked Merchant Center listing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Duplicate without user-selected canonical.&lt;/strong&gt; Google: "This page is a duplicate of another page, although it doesn't indicate a preferred canonical page. Google has chosen the other page as the canonical for this page, and so will not serve this page in Search." Usually parameter URLs from apps and recommendation widgets. Ilana Davis's writeup on &lt;a href="https://www.ilanadavis.com/blogs/articles/when-to-ignore-search-console-indexing-issues-for-shopify-stores" rel="noopener noreferrer"&gt;when to ignore Search Console indexing issues on Shopify&lt;/a&gt; calls out URLs containing &lt;code&gt;wpm&lt;/code&gt; or &lt;code&gt;web-pixel-shopify-custom-pixel&lt;/code&gt;, plus &lt;code&gt;?pr_prod_strat=&lt;/code&gt; parameters, as safe to ignore. Filter those out before you look at anything else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;URL blocked by robots.txt.&lt;/strong&gt; Google: "This page was blocked by your site's robots.txt file." Shopify's default &lt;code&gt;robots.txt.liquid&lt;/code&gt; already blocks &lt;code&gt;/admin&lt;/code&gt;, &lt;code&gt;/cart&lt;/code&gt;, &lt;code&gt;/checkout&lt;/code&gt;, &lt;code&gt;/search&lt;/code&gt;, &lt;code&gt;/policies/&lt;/code&gt;, and filtered collection URLs matching &lt;code&gt;/collections/*+*&lt;/code&gt;, per &lt;a href="https://help.shopify.com/en/manual/promoting-marketing/seo/editing-robots-txt" rel="noopener noreferrer"&gt;Shopify's robots.txt documentation&lt;/a&gt;. Those entries are correct. The exception that matters most: a &lt;code&gt;/products/&lt;/code&gt; URL showing up under this status means someone added a rule that shouldn't be there. Shopify itself warns that "incorrect use of the feature can result in loss of all traffic," and since the file lives in the theme, a theme update or a careless edit can reintroduce a bad &lt;code&gt;Disallow&lt;/code&gt; line without anyone noticing. &lt;a href="https://storecanary.io/blog/shopify-products-blocked-by-robots-txt" rel="noopener noreferrer"&gt;Full mechanism here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discovered - currently not indexed.&lt;/strong&gt; Google: "The page was found by Google, but not crawled yet. Typically, Google wanted to crawl the URL but this was expected to overload the site; therefore Google rescheduled the crawl." Common on a new store or right after a bulk catalog import; it's a queue, not a verdict. Worth a second look if product URLs sit here for weeks while the rest of the catalog crawls normally, since that usually means Googlebot's crawl budget is being burned on parameter URLs instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which statuses mean you shipped a bug that's hiding a live product?
&lt;/h2&gt;

&lt;p&gt;Anything below, on a URL for a product the client is actively selling, deserves a same-day fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;URL marked 'noindex'.&lt;/strong&gt; Google: "When Google tried to index the page it encountered a 'noindex' directive and therefore did not index it." This is the most consequential status for a Shopify build, because nothing in Shopify admin surfaces it: the product stays active, priced, and purchasable while being invisible to Google. Two known ways it gets applied: a theme conditional wrapping&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"robots"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"noindex"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and a &lt;code&gt;seo.hidden&lt;/code&gt; custom metafield, which &lt;a href="https://help.shopify.com/en/manual/promoting-marketing/seo/hide-a-page-from-search-engines" rel="noopener noreferrer"&gt;Shopify documents&lt;/a&gt; as applying to "products, pages, or blog posts." Both survive a CSV import, and both can be set by an app without a visible admin toggle. Across full-catalog scans of more than 90,000 Shopify product pages, a stray &lt;code&gt;noindex&lt;/code&gt; on live products is one of the most common critical findings. &lt;a href="https://storecanary.io/blog/shopify-phantom-noindex-google" rel="noopener noreferrer"&gt;Full breakdown here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not found (404).&lt;/strong&gt; Google: "This page returned a 404 error when requested." Correct and expected for a genuinely discontinued product. Worth checking in two cases: a handle that was renamed rather than retired (Shopify doesn't auto-redirect on a handle change), and a product deleted while it still had rankings and inbound links, since Shopify creates no redirect on deletion. &lt;a href="https://storecanary.io/blog/shopify-deleted-product-still-on-google" rel="noopener noreferrer"&gt;More on that here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Soft 404.&lt;/strong&gt; Google: "The page request returns what we think is a soft 404 response. This means that it returns a user-friendly 'not found' message but not a 404 HTTP response code." Usually a theme or app rendering an "unavailable" or empty state on a URL that still returns HTTP 200. Worth grepping your theme's product template for conditionals that swap in an empty state without changing the status code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server error (5xx).&lt;/strong&gt; Google: "Your server returned a 500-level error when the page was requested." Rare on Shopify's own infrastructure; at volume, this usually traces to an app proxy or a third-party endpoint the theme calls during server-side rendering. Check whether the errors cluster around an app's install date.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Redirect error.&lt;/strong&gt; Covers "a redirect chain that was too long, a redirect loop, a redirect URL that eventually exceeded the max URL length, a bad or empty URL in the redirect chain." On a storefront, loops are usually two systems each trying to own the same redirect: a URL redirect rule set in Shopify admin pointing one way, and an app or locale rule pointing back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blocked due to unauthorized request (401).&lt;/strong&gt; Google: "The page was blocked to Googlebot by a request for authorization." Usually points at a staging or preview environment, or an app proxy path sitting behind auth, rather than the live storefront. Note that a password-protected store is a separate mechanism: while the password page is active, Shopify states "search engines find and display only the password page," and other pages "are hidden and aren't displayed in search results" (&lt;a href="https://help.shopify.com/en/manual/online-store/themes/password-page" rel="noopener noreferrer"&gt;Shopify docs&lt;/a&gt;). Worth double-checking a client didn't leave store password protection on after launch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blocked due to access forbidden (403).&lt;/strong&gt; Google: "HTTP 403 means that the user agent provided credentials, but was not granted access." On a storefront, this usually means a bot-management or firewall layer applied at the domain level is filtering Googlebot's requests, not a Shopify setting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;URL blocked due to other 4xx issue.&lt;/strong&gt; Google's catch-all for "a 4xx error not covered by any other issue type." Rare enough that it's worth inspecting the specific URL individually rather than pattern-matching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crawled - currently not indexed.&lt;/strong&gt; Google's full description: "The page was crawled by Google but not indexed." Nothing blocked it; Googlebot read the page and declined to index it. This one alarms clients more than any other status, and it's mostly noise on Shopify since the URLs listed are usually duplicate surfaces. The work is in filtering: see the &lt;a href="https://storecanary.io/blog/crawled-currently-not-indexed-shopify" rel="noopener noreferrer"&gt;full breakdown of this status on Shopify products&lt;/a&gt; for how to tell a duplicate from a real failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Duplicate, Google chose different canonical than user.&lt;/strong&gt; Google: "This page is marked as canonical for a set of pages, but Google thinks another URL makes a better canonical." On Shopify this is structural: Shopify's own Liquid docs warn that "a standard product page and a product page in the context of a collection have the same content on separate URLs," and tell theme developers to consider the SEO implications of the &lt;a href="https://shopify.dev/docs/api/liquid/filters/within" rel="noopener noreferrer"&gt;&lt;code&gt;within&lt;/code&gt; filter&lt;/a&gt;. If most of the theme's internal links point at the collection-scoped URL, Google follows that internal linking pattern over your canonical tag. &lt;a href="https://storecanary.io/blog/shopify-google-chose-different-canonical" rel="noopener noreferrer"&gt;More detail here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do the two "indexed anyway" warnings mean?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Indexed, though blocked by robots.txt.&lt;/strong&gt; Google: "The page was indexed despite being blocked by your website's robots.txt file. Google always respects robots.txt, but this doesn't necessarily prevent indexing if someone else links to your page." The practical result is a search result Google can't describe, since it never read the page. If the blocked URL is a product, unblock it; don't add a &lt;code&gt;noindex&lt;/code&gt; tag, since Googlebot is never allowed to fetch the page to see it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Page indexed without content.&lt;/strong&gt; Google: "This page appears in the Google index, but for some reason Google could not read the content. Possible reasons are that the page might be cloaked to Google or the page might be in a format that Google can't index." Rare, and serious on a product page: the URL sits in the index carrying nothing to rank with. Run a live test to see what Googlebot actually receives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Page is indexed.&lt;/strong&gt; The clean pass, and worth saying plainly: it says nothing about ranking, price display, review stars, or whether the matching Merchant Center listing is approved. Those are decided elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you audit a client's Page indexing report step by step?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read the reason, not the total.&lt;/strong&gt; Open Indexing → Pages and ignore the headline count. The reasons listed underneath are where noise and real failures diverge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter each reason down to product URLs.&lt;/strong&gt; Look only for URLs containing &lt;code&gt;/products/&lt;/code&gt;. Discard anything carrying &lt;code&gt;wpm&lt;/code&gt;, &lt;code&gt;web-pixel-shopify-custom-pixel&lt;/code&gt;, &lt;code&gt;?variant=&lt;/code&gt;, or &lt;code&gt;?pr_prod_strat=&lt;/code&gt;, and discard &lt;code&gt;/cart&lt;/code&gt;, &lt;code&gt;/checkout&lt;/code&gt;, &lt;code&gt;/search&lt;/code&gt;, and &lt;code&gt;/policies/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the survivors against the live catalog.&lt;/strong&gt; For every product URL still standing, confirm in Shopify admin whether it's active and for sale. A discontinued product under 404 is fine. A product the client sold this morning is not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspect one affected URL, then test it live.&lt;/strong&gt; Run URL Inspection for the indexed state and Google-selected canonical, then press Test live URL. If they disagree, your fix already landed and the report just hasn't caught up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Match the status to its cause, fix the mechanism, then validate.&lt;/strong&gt; Use the entries above to trace the label back to what produced it (a &lt;code&gt;noindex&lt;/code&gt; directive, a &lt;code&gt;Disallow&lt;/code&gt; rule, a dead handle, a collection-scoped duplicate), fix that, then use Validate fix so Google rechecks the group instead of waiting on a natural recrawl.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why does this need to be a recurring check, not a one-time audit?
&lt;/h2&gt;

&lt;p&gt;Every status above describes a state Google recorded in the past, on URLs it happened to look at. Nothing in this report fires on the event that caused the change. A theme update that reintroduces a &lt;code&gt;Disallow&lt;/code&gt; line, an app that writes a &lt;code&gt;seo.hidden&lt;/code&gt; metafield across a collection during a bulk edit, a handle rename during a catalog migration: each of these is routine work, invisible in Shopify admin, and shows up here days or weeks later as one row among thousands.&lt;/p&gt;

&lt;p&gt;Running through this checklist once after launch is worth doing. But the report looks different next month for reasons nobody logged in a changelog, and the stores that get hurt aren't the ones that never checked; they're the ones that checked in March and had no reason to check again in July. Across full-catalog scans of more than 90,000 Shopify product pages, roughly 46% of stores carry at least one critical Google visibility issue, and roughly 73% carry at least one critical or warning-level issue. Worth baking this into whatever QA or handoff checklist you run for clients.&lt;/p&gt;

&lt;p&gt;How do you currently catch this kind of regression on client stores: manual Search Console checks, a cron job, something else? Curious what other devs and agencies have wired up.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The full version of this article — with screenshots and ongoing updates — lives on &lt;a href="https://storecanary.io/blog/search-console-page-indexing-shopify" rel="noopener noreferrer"&gt;the StoreCanary blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>webdev</category>
      <category>seo</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Shopify zero-price bug that breaks Merchant Center feeds</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Mon, 24 Aug 2026 11:56:06 +0000</pubDate>
      <link>https://dev.to/max_buildogs/the-shopify-zero-price-bug-that-breaks-merchant-center-feeds-3abb</link>
      <guid>https://dev.to/max_buildogs/the-shopify-zero-price-bug-that-breaks-merchant-center-feeds-3abb</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Merchant Center's "Invalid value for category [price]" fires whenever the price attribute Google receives is exactly 0; the only documented exception is mobile devices sold under a contract promotion.&lt;/li&gt;
&lt;li&gt;On Shopify the zero comes from one of two unrelated causes: an unfilled variant price (a data issue, no code involved) or a theme's JSON-LD snippet reading a variable that can resolve to empty (a code bug that hits every product page).&lt;/li&gt;
&lt;li&gt;Scanning structured data across 90,000+ Shopify product pages, roughly 1 in 5 stores had at least one page emitting a zero price, usually on a small slice of the catalog, which is exactly why spot-checking a few products rarely catches it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a client's Shopify product silently drops out of Google Shopping, or picks up a "Invalid value for category [price]" disapproval in Merchant Center, don't start by re-checking the price field in admin. Start with the structured data the page actually serves. Google flags this error when the price attribute it receives is 0, a value it never treats as a real offer. On Shopify that zero rarely shows up where you'd expect it: it's usually buried in one variant nobody filled in, or it's a theme snippet reading a variable that can resolve to empty even when the admin price is correct. Both produce the identical Merchant Center error, and only one of them needs a code fix.&lt;/p&gt;

&lt;p&gt;This post covers how to tell which cause you're dealing with on a client's store, and how to fix each.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does "Invalid value for category [price]" actually mean?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://support.google.com/merchants/answer/12471484" rel="noopener noreferrer"&gt;Google's own documentation&lt;/a&gt; is blunt about it: the issue "occurs when you've entered 0 for the price [price] attribute." There's exactly one exception, for a mobile device sold as part of a contract or promotion, where a price attribute of 0 is allowed. Outside that case, a price of zero isn't a valid commercial offer, so Google rejects the item outright.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does this bug hide from spot-checks?
&lt;/h2&gt;

&lt;p&gt;It's a loud failure with a quiet distribution. It rarely touches a whole catalog; it usually touches a handful of products inside a catalog that otherwise looks fine. Across a scan of more than 90,000 Shopify product pages, roughly one store in five had at least one page declaring a price of zero in its structured data, and in the typical case the affected pages were a small fraction of that store's catalog.&lt;/p&gt;

&lt;p&gt;That combination, widespread across stores but nearly invisible within any single one, is why it survives. Opening a few products in admin to eyeball the price almost never lands on the broken one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 1: is the variant price actually empty?
&lt;/h2&gt;

&lt;p&gt;Someone created the product as a draft with a placeholder price and never filled it in, or added a variant later and left its price blank. Admin is telling the truth here, and the markup is faithfully reporting it. This is the mundane case: no code change needed, just set the price.&lt;/p&gt;

&lt;p&gt;The detail that catches people out is that it's usually one variant, not the product. A product whose first variant is priced correctly looks entirely normal at a glance, while a second or third variant sits at zero and drags the whole item into a disapproval.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 2: is the theme reading the wrong price variable?
&lt;/h2&gt;

&lt;p&gt;Here admin is correct and the markup still says zero. The theme's JSON-LD snippet is reading a field that can be empty instead of the one that always resolves to the displayed variant. In Liquid, that means checking whether the snippet uses something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight liquid"&gt;&lt;code&gt;"price": "&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;selected_or_first_available_variant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;price&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;money_without_currency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than a raw &lt;code&gt;product.price&lt;/code&gt;, &lt;code&gt;product.variants.first.price&lt;/code&gt;, or any field that can be nil when a variant is out of stock or unpublished. &lt;code&gt;product.selected_or_first_available_variant&lt;/code&gt; is the one that reliably matches what's rendered on the page.&lt;/p&gt;

&lt;p&gt;This version is the one worth flagging in a code review, because it isn't a one-off data mistake, it's a snippet that runs on every product page on the store. It also survives every correction made in admin, which is what makes it confusing to debug from the client side: they fix the price, the price is right, and Google keeps rejecting the item anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you tell which one you're looking at?
&lt;/h2&gt;

&lt;p&gt;Run the product URL through the &lt;a href="https://search.google.com/test/rich-results" rel="noopener noreferrer"&gt;Rich Results Test&lt;/a&gt; and read the &lt;code&gt;price&lt;/code&gt; field on the detected Product. That's the number Google actually receives, not the one typed into admin.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the same product in &lt;strong&gt;Shopify Admin → Product → Pricing&lt;/strong&gt; and check every variant, not just the first.&lt;/li&gt;
&lt;li&gt;If a variant has no price, that's your answer: set it.&lt;/li&gt;
&lt;li&gt;If every variant price is correct in admin and the markup still shows 0, the bug is in the theme snippet: confirm it reads &lt;code&gt;product.selected_or_first_available_variant.price&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What's the actual fix, and how do you confirm it?
&lt;/h2&gt;

&lt;p&gt;For the data case, filling in the missing variant price is the entire fix. For the code case, correcting the JSON-LD variable fixes every product on the store at once, so it's worth doing properly rather than patching one product's snippet.&lt;/p&gt;

&lt;p&gt;Either way, revalidate with the Rich Results Test before telling the client it's resolved, then let the Merchant Center feed refresh (or reupload manually). Affected items show up under the &lt;strong&gt;Needs attention&lt;/strong&gt; tab once Google has re-crawled the feed.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you check an entire client catalog, not just one product?
&lt;/h2&gt;

&lt;p&gt;Merchant Center's Needs attention tab only lists products that already reached a feed. A product that never made it into a feed can carry a zero price in its structured data indefinitely without ever surfacing in that report. If you're auditing a client's store, the structured data actually served on live product pages is the source of truth, not the feed diagnostics. This is worth building into any theme handoff or Merchant Center audit checklist, since it's cheap to check and easy to miss.&lt;/p&gt;

&lt;p&gt;It's also worth distinguishing from two related failures: a &lt;a href="https://storecanary.io/blog/merchant-center-price-mismatch-shopify" rel="noopener noreferrer"&gt;price mismatch&lt;/a&gt;, where the page and the markup disagree on a real price, and a &lt;a href="https://storecanary.io/blog/google-wrong-price-shopify" rel="noopener noreferrer"&gt;stale price&lt;/a&gt;, where the price is valid but out of date. A zero price is neither: the page and the markup usually agree, on a number that can't be right.&lt;/p&gt;

&lt;p&gt;Have you run into this on a client's theme before, was it a missing variant price or a bad JSON-LD variable?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The full version of this article — with screenshots and ongoing updates — lives on &lt;a href="https://storecanary.io/blog/invalid-value-category-price-shopify" rel="noopener noreferrer"&gt;the StoreCanary blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>webdev</category>
      <category>seo</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Shopify 'crawled not indexed': triage guide for agency devs</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Fri, 21 Aug 2026 11:58:37 +0000</pubDate>
      <link>https://dev.to/max_buildogs/shopify-crawled-not-indexed-triage-guide-for-agency-devs-57f7</link>
      <guid>https://dev.to/max_buildogs/shopify-crawled-not-indexed-triage-guide-for-agency-devs-57f7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Most URLs in the "Crawled - currently not indexed" report on a Shopify store are expected duplicates (collection-scoped product URLs, &lt;code&gt;?variant=&lt;/code&gt; parameters); their absence from the index is consolidation, not a bug.&lt;/li&gt;
&lt;li&gt;A real &lt;code&gt;/products/handle&lt;/code&gt; URL that survives triage usually has one of three causes: a canonical tag pointing elsewhere, thin content shared across many stores, or broken Product structured data.&lt;/li&gt;
&lt;li&gt;The fix starts with filtering the export, not reading the raw count.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you open Search Console for a client's Shopify store and see dozens of product pages sitting under "Crawled - currently not indexed," the immediate question is: platform quirk or actual indexing failure? The distinction matters because Shopify's URL architecture generates legitimate duplicates by default, and most of what fills this report falls into that category.&lt;/p&gt;

&lt;p&gt;Google's own reference defines this status in one sentence: &lt;a href="https://support.google.com/webmasters/answer/7440203" rel="noopener noreferrer"&gt;"The page was crawled by Google but not indexed."&lt;/a&gt; Compare it with the neighboring statuses. "URL blocked by robots.txt" is an access refusal you can switch off. "URL marked 'noindex'" is an explicit directive you can remove. This one is a judgment about the page's content and signals, and there is no switch for it.&lt;/p&gt;

&lt;p&gt;This article gives you the triage process: which URL shapes to filter out immediately, the three root causes worth investigating on surviving URLs, and a five-step inspection checklist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does this report fill up with URLs that aren't the problem?
&lt;/h2&gt;

&lt;p&gt;A Shopify storefront generates far more URLs than it has products. Before reading anything into the count, export the report and filter to URLs containing &lt;code&gt;/products/&lt;/code&gt;. Everything else is a separate question.&lt;/p&gt;

&lt;p&gt;Two shapes in particular tend to dominate this list, and both are duplicates by design:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collection-scoped product URLs.&lt;/strong&gt; Shopify serves the same product at &lt;code&gt;/products/handle&lt;/code&gt; and again at &lt;code&gt;/collections/name/products/handle&lt;/code&gt;. Shopify's own documentation for the &lt;a href="https://shopify.dev/docs/api/liquid/filters/within" rel="noopener noreferrer"&gt;&lt;code&gt;within&lt;/code&gt; filter&lt;/a&gt; says it directly: "Because a standard product page and a product page in the context of a collection have the same content on separate URLs, you should consider the SEO implications of using the within filter." When Google finds several URLs with identical content, it picks one canonical and leaves the others out. That is expected consolidation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Variant parameters.&lt;/strong&gt; A &lt;code&gt;?variant=12345&lt;/code&gt; URL is the same product page with a different option preselected. There is nothing distinct to index.&lt;/p&gt;

&lt;p&gt;If the entire list is these two shapes plus non-product URLs, the right action is none.&lt;/p&gt;

&lt;h2&gt;
  
  
  What causes a real &lt;code&gt;/products/&lt;/code&gt; URL to stay unindexed?
&lt;/h2&gt;

&lt;p&gt;Once you have filtered the list and real product URLs remain, there are three causes to check in order.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does the canonical point somewhere else?
&lt;/h3&gt;

&lt;p&gt;This is the one that looks like a content problem but is mechanical. If the page's &lt;code&gt;&amp;lt;link rel="canonical"&amp;gt;&lt;/code&gt; points to a URL other than itself, Google treats the page as a duplicate. Run the URL through URL Inspection in Search Console and compare the &lt;strong&gt;User-declared canonical&lt;/strong&gt; with the &lt;strong&gt;Google-selected canonical&lt;/strong&gt;. If they differ, that is your answer. The &lt;a href="https://storecanary.io/blog/shopify-google-chose-different-canonical" rel="noopener noreferrer"&gt;canonical article&lt;/a&gt; covers why the collection URL often wins this comparison.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does the page have unique content?
&lt;/h3&gt;

&lt;p&gt;A product page carrying a supplier description shared verbatim across hundreds of stores gives Google little reason to add another copy to the index. This rarely has a code fix; it is frequently the real answer on dropshipped or catalog-imported stores. Sizing detail, materials, genuine reviews, and shipping specifics are the content that changes the calculation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is the structured data parsing correctly?
&lt;/h3&gt;

&lt;p&gt;Worth eliminating before concluding anything about content quality. If the &lt;code&gt;Product&lt;/code&gt; block has a syntax error, the whole declaration is invisible to Google, usually with no visible symptom on the page itself. The &lt;a href="https://storecanary.io/blog/unparsable-structured-data-shopify" rel="noopener noreferrer"&gt;unparsable structured data article&lt;/a&gt; shows how a single stray character silently discards an entire block.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you triage this in five minutes?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Search Console &amp;gt; Indexing &amp;gt; Pages&lt;/strong&gt;, click &lt;strong&gt;Crawled - currently not indexed&lt;/strong&gt;, and export the list.&lt;/li&gt;
&lt;li&gt;Filter to URLs containing &lt;code&gt;/products/&lt;/code&gt;, then remove anything containing &lt;code&gt;/collections/&lt;/code&gt; or &lt;code&gt;?variant=&lt;/code&gt;. What remains is the list that matters.&lt;/li&gt;
&lt;li&gt;Take one surviving URL and run &lt;strong&gt;URL Inspection&lt;/strong&gt; on it. Compare &lt;em&gt;User-declared canonical&lt;/em&gt; with &lt;em&gt;Google-selected canonical&lt;/em&gt;. If they differ, you have the cause.&lt;/li&gt;
&lt;li&gt;If the canonicals agree, open the page and assess what content exists there that does not exist anywhere else. If the honest answer is nothing, that is the finding.&lt;/li&gt;
&lt;li&gt;Run the same URL through the &lt;a href="https://search.google.com/test/rich-results" rel="noopener noreferrer"&gt;Rich Results Test&lt;/a&gt; to confirm the &lt;code&gt;Product&lt;/code&gt; structured data still parses.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The pattern behind most of these silent failures: the Shopify admin shows a healthy, active, priced product, while the public URL tells Google something different. This status is a per-URL decision, and that decision usually has a mechanical cause one layer below it.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Related:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://storecanary.io/blog/search-console-page-indexing-shopify" rel="noopener noreferrer"&gt;Every status in the Page indexing report, explained for Shopify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://storecanary.io/blog/shopify-products-not-showing-on-google" rel="noopener noreferrer"&gt;11 reasons a Shopify product disappears from Google&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://storecanary.io/blog/shopify-google-chose-different-canonical" rel="noopener noreferrer"&gt;User-declared canonical vs Google-selected on Shopify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://storecanary.io/blog/unparsable-structured-data-shopify" rel="noopener noreferrer"&gt;Unparsable structured data on Shopify: the fix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://storecanary.io/blog/shopify-phantom-noindex-google" rel="noopener noreferrer"&gt;The phantom noindex: how Shopify apps silently hide products from Google&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Have you hit this on a client store? What was the root cause when you dug in?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The full version of this article - with screenshots and ongoing updates - lives on &lt;a href="https://storecanary.io/blog/crawled-currently-not-indexed-shopify" rel="noopener noreferrer"&gt;the StoreCanary blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>webdev</category>
      <category>seo</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Shopify product removed but still live in Merchant Center</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Wed, 19 Aug 2026 11:57:48 +0000</pubDate>
      <link>https://dev.to/max_buildogs/shopify-product-removed-but-still-live-in-merchant-center-3jno</link>
      <guid>https://dev.to/max_buildogs/shopify-product-removed-but-still-live-in-merchant-center-3jno</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deleting a product in Shopify removes it from the feed but sends no explicit delete to Merchant Center; the copy stays live until it expires (30 days after the last refresh) or is removed directly.&lt;/li&gt;
&lt;li&gt;Google's automatic product discovery re-adds items without a feed if the URL still resolves with &lt;code&gt;Product&lt;/code&gt; structured data on the page.&lt;/li&gt;
&lt;li&gt;A 301 redirect (correct for SEO) keeps the Merchant Center landing page accessible, which blocks the "unavailable landing page" rule from automatically disabling the listing.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your client deleted a product in Shopify. It is gone from the catalog, the collections, and the sitemap. It is still active in Google Merchant Center, still marked in stock, still eligible for Shopping ads and free listings.&lt;/p&gt;

&lt;p&gt;The root cause: Shopify and Merchant Center are two separate systems holding two separate copies of product data. Deleting the Shopify original stops it from being submitted on the next sync. It does not propagate a delete instruction to the Merchant Center copy. What happens to that copy is governed entirely by Merchant Center's own rules.&lt;/p&gt;

&lt;p&gt;This affects any store using the Google &amp;amp; YouTube channel or a feed-based integration. The merchant reports removed products still appearing in Shopping results. This article covers the three mechanisms that keep a removed product alive in Merchant Center, a five-step diagnostic to identify which one is active, and the correct fix per cause.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why doesn't deleting in Shopify remove it from Merchant Center?
&lt;/h2&gt;

&lt;p&gt;Your Shopify catalog is the original. Merchant Center holds a copy, submitted by a data source. Deleting the original stops it from being submitted on the next sync. What happens to the copy depends entirely on Merchant Center's own rules.&lt;/p&gt;

&lt;p&gt;Shopify's &lt;a href="https://help.shopify.com/en/manual/online-sales-channels/marketplaces/google/getting-started/syncing-products" rel="noopener noreferrer"&gt;documentation on syncing products&lt;/a&gt; describes the 30-day refresh cycle: "Google sets a strict 30-day expiry policy on synced product data, so the Google &amp;amp; YouTube channel updates your products automatically within that 30-day period." That describes how live products stay alive. A product that stops being refreshed hits that same 30-day expiry clock rather than disappearing immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you confirm which mechanism is holding the listing?
&lt;/h2&gt;

&lt;p&gt;Five steps, about five minutes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In Merchant Center, open &lt;strong&gt;Products&lt;/strong&gt; and search by title or product ID. Note the status (active, expired, disapproved): it tells you which rule is holding the item.&lt;/li&gt;
&lt;li&gt;Open &lt;strong&gt;Data sources&lt;/strong&gt; and check which source the item is attributed to. An item attributed to your channel feed and an item Google found on its own need different fixes.&lt;/li&gt;
&lt;li&gt;Open the old product URL in a private window. Confirm whether it returns a 404, redirects somewhere, or still loads a product page.&lt;/li&gt;
&lt;li&gt;If it still loads, view source and search for &lt;code&gt;"@type": "Product"&lt;/code&gt;. Structured data on a still-reachable page is a live route back into Merchant Center, independent of the feed.&lt;/li&gt;
&lt;li&gt;Search Google for the product name plus the store name, then check the &lt;strong&gt;Shopping&lt;/strong&gt; tab specifically. An item can disappear from Search and remain active in Shopping.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What four mechanisms keep the listing alive?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. The removal has not propagated yet.&lt;/strong&gt; Google's &lt;a href="https://support.google.com/merchants/answer/14996874" rel="noopener noreferrer"&gt;guidance on managing products in Merchant Center&lt;/a&gt; states that when a product is removed from a data source, "it may take 24-48 hours for your product to be removed from ads and free listings." If the deletion happened yesterday, this is inside the normal window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Nothing told Merchant Center to remove it.&lt;/strong&gt; Stopping a refresh differs from sending a delete instruction. The same Google page states that "All products expire from your Merchant Center account 30 days after the last refresh." An orphaned item keeps serving until that clock runs out. This is the cause behind reports of products deleted two months ago still appearing in Merchant Center.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Google found the product by itself.&lt;/strong&gt; Google's documentation on &lt;a href="https://support.google.com/merchants/answer/12158480" rel="noopener noreferrer"&gt;adding products automatically&lt;/a&gt; describes the process: "We scan your website to find product pages", then "We read the structured data on those pages." The same page states Google checks for new products at least once every 24 hours. No feed is required. If the product page still resolves and carries &lt;code&gt;Product&lt;/code&gt; markup, Google has a route to that item that the feed does not control. This is why a manual deletion sometimes reappears.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The landing page is still reachable.&lt;/strong&gt; Merchant Center can remove a product with a dead destination via the &lt;a href="https://support.google.com/merchants/answer/12158123" rel="noopener noreferrer"&gt;"Product page unavailable" issue&lt;/a&gt;. A 404 triggers it. A single 301 redirect does not: the destination resolves cleanly, and the signal that would disable the Shopping listing never fires.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does the standard redirect advice block the auto-cleanup?
&lt;/h2&gt;

&lt;p&gt;Redirecting a dead product URL to a live collection is the correct move for search: it recovers the shopper and the link equity. On the Merchant Center side, it also keeps the stale listing active because the landing page check now passes. Two pieces of correct advice that conflict: the SEO fix prevents the Merchant Center auto-cleanup from firing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the correct fix per cause?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Remove at the source, then in the interface.&lt;/strong&gt; Deleting only the Merchant Center copy leaves the data source free to resubmit the item on the next sync. Remove it from Shopify first, then clear the Merchant Center copy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Archive instead of pause for discontinued products.&lt;/strong&gt; Google documents archiving as removing a product "from your main product list, diagnostics, and from serving on Google surfaces." &lt;a href="https://support.google.com/merchants/answer/15093272" rel="noopener noreferrer"&gt;Pause is documented as short-term&lt;/a&gt;: treated as expired after more than two weeks, it is the wrong instrument for a permanently discontinued product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the automatic source.&lt;/strong&gt; In Data sources, look at what Google added from its own crawl. If the item is there, removing it from Shopify will not clear it. It returns for as long as a page with &lt;code&gt;Product&lt;/code&gt; markup stays reachable at that URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be deliberate about the old URL.&lt;/strong&gt; Point a redirect at a genuine replacement product. Where there is no replacement, leaving the URL as a 404 is what eventually triggers the landing-page rule that disables the Shopping listing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-check after 48 hours.&lt;/strong&gt; The documented propagation window is 24-48 hours. Checking immediately or waiting a month both produce misleading conclusions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One action to avoid: uninstalling and reinstalling the Google &amp;amp; YouTube channel clears the Merchant Center account, but it re-mints product IDs and discards the performance history tied to them. That is a real trade-off, not a free reset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does this recur after every catalog cleanup?
&lt;/h2&gt;

&lt;p&gt;Every catalog cleanup regenerates this problem. A discontinued line, a seasonal range, a rebrand that renames URL handles, a bulk delete during a migration: each creates a batch of products that exist in Google's copy but no longer in Shopify. Shopify admin cannot surface that discrepancy because the products are no longer in Shopify to display.&lt;/p&gt;

&lt;p&gt;The same structural gap appears in &lt;a href="https://storecanary.io/blog/shopify-merchant-center-availability-mismatch" rel="noopener noreferrer"&gt;the availability mismatch&lt;/a&gt; (Shopify says in stock, Google Shopping shows sold out): two systems holding two versions of the truth, with no alert on the gap between them.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Related reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://storecanary.io/blog/shopify-products-not-showing-on-google" rel="noopener noreferrer"&gt;11 reasons a Shopify product disappears from Google&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://storecanary.io/blog/shopify-deleted-product-still-on-google" rel="noopener noreferrer"&gt;A deleted Shopify product still showing in Google Search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://storecanary.io/blog/shopify-merchant-center-availability-mismatch" rel="noopener noreferrer"&gt;Shopify says in stock, Google Shopping shows sold out&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://storecanary.io/blog/shopify-product-page-redirect-merchant-center" rel="noopener noreferrer"&gt;Your Shopify product page redirects to the homepage and Merchant Center disapproved it&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Have you run into a case where Google kept re-adding a removed product through its automatic crawler? What ended up clearing it?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The full version of this article — with screenshots and ongoing updates — lives on &lt;a href="https://storecanary.io/blog/shopify-removed-product-still-in-google-shopping" rel="noopener noreferrer"&gt;the StoreCanary blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>webdev</category>
      <category>seo</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>Shopify product drops from Google: 11 bugs to fix for clients</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Thu, 13 Aug 2026 11:59:34 +0000</pubDate>
      <link>https://dev.to/max_buildogs/shopify-product-drops-from-google-11-bugs-to-fix-for-clients-1ode</link>
      <guid>https://dev.to/max_buildogs/shopify-product-drops-from-google-11-bugs-to-fix-for-clients-1ode</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Three independent systems control product visibility on Google: Google's crawler, Google's indexer, and Merchant Center. They fail independently and Shopify admin reflects none of them.&lt;/li&gt;
&lt;li&gt;The 11 failure modes split across crawl blocks, indexing decisions, URL resolution errors, markup problems, and Merchant Center disapprovals.&lt;/li&gt;
&lt;li&gt;Triage starts with &lt;code&gt;site:&amp;lt;url&amp;gt;&lt;/code&gt; in Google, then URL Inspection in Search Console, before touching any code.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;When a client reports that their products have disappeared from Google, you are dealing with one of eleven distinct failure modes spread across three systems that do not communicate with each other. Google's crawler, Google's indexer, and Merchant Center each make independent decisions about every product URL, and Shopify's admin reflects none of them. The product stays green and Active while it is gone from search.&lt;/p&gt;

&lt;p&gt;This article maps all eleven failure modes, organized by which system is responsible: the crawler never fetched the page, the indexer fetched it and excluded it, the URL resolves to the wrong thing, structured data errors stripped rich results, or Merchant Center disapproved the listing. Each section names the exact mechanism and the diagnostic tool that confirms it. The triage section at the bottom walks through a five-step sequence to identify the responsible system in about ten minutes, before you change anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which three systems control whether a product appears?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Google Search&lt;/strong&gt; builds its index by crawling product pages. Whether a product appears in organic results depends on what the crawl found and what Google decided to keep.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google Merchant Center&lt;/strong&gt; is a separate pipeline. Shopping surface listings come from the feed, and Google's &lt;a href="https://support.google.com/merchants/answer/7052112" rel="noopener noreferrer"&gt;product data specification&lt;/a&gt; checks that feed against the live page, including price, availability, and structured data. A product can rank in web search and be disapproved in Merchant Center on the same afternoon.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The theme and installed apps&lt;/strong&gt; generate what both systems read: the HTML, the canonical tag, the robots directives, the JSON-LD. Neither Google system tells Shopify when it rejects something, and Shopify does not inspect what either system concluded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why did Google never fetch the page?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. A rule in &lt;code&gt;robots.txt.liquid&lt;/code&gt; is blocking the crawl.&lt;/strong&gt; Shopify generates &lt;code&gt;robots.txt&lt;/code&gt; from a template inside the theme, so the file is theme code, not a store setting. Publishing a different theme changes what Google is served, and any app with theme-code access can rewrite it. When a &lt;code&gt;Disallow&lt;/code&gt; covers product paths, Google stops fetching them and pages fall out over the following weeks.&lt;/p&gt;

&lt;p&gt;One second-order effect worth flagging: a &lt;code&gt;noindex&lt;/code&gt; directive on a page blocked in &lt;code&gt;robots.txt&lt;/code&gt; is never read. Google's documentation is explicit: "if the page is blocked by a robots.txt file or the crawler can't access the page, the crawler will never see the &lt;code&gt;noindex&lt;/code&gt; rule, and the page can still appear in search results" (&lt;a href="https://developers.google.com/search/docs/crawling-indexing/block-indexing" rel="noopener noreferrer"&gt;Google Search Central, block indexing&lt;/a&gt;). A crawl block can simultaneously hide pages you wanted indexed and keep pages you wanted removed. Full mechanism in &lt;a href="https://storecanary.io/blog/shopify-products-blocked-by-robots-txt" rel="noopener noreferrer"&gt;the robots.txt walkthrough&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why did Google fetch it but not index it?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;2. Something set the product to hidden.&lt;/strong&gt; Shopify &lt;a href="https://help.shopify.com/en/manual/promoting-marketing/seo/hide-a-page-from-search-engines" rel="noopener noreferrer"&gt;documents a supported way&lt;/a&gt; to remove a product from search: a &lt;code&gt;seo.hidden&lt;/code&gt; metafield set to &lt;code&gt;1&lt;/code&gt;. The metafield must be created as an &lt;strong&gt;Integer&lt;/strong&gt;; created as text it is accepted without complaint but behaves differently. The newer &lt;strong&gt;Unlisted&lt;/strong&gt; product status goes further, hiding the product from internet search, the sitemap, collection pages, storefront search, and recommendations, while keeping its direct URL alive. Both are legitimate features and both are what an app reaches for when it wants to hide something temporarily. Neither leaves a notice in admin. &lt;a href="https://storecanary.io/blog/shopify-phantom-noindex-google" rel="noopener noreferrer"&gt;The phantom noindex article&lt;/a&gt; covers how to audit these at catalog scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Google picked a different URL as the canonical.&lt;/strong&gt; Every Shopify product is reachable at &lt;code&gt;/products/…&lt;/code&gt; and again at &lt;code&gt;/collections/…/products/…&lt;/code&gt;. When theme links point at one and the canonical tag declares the other, Google resolves the conflict itself, and Search Console records the outcome as "duplicate, Google chose different canonical". The URL you were watching was not the selected canonical; the product itself still exists under the other path. See &lt;a href="https://storecanary.io/blog/shopify-google-chose-different-canonical" rel="noopener noreferrer"&gt;the canonical mismatch piece&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why did Google never discover the URL?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;4. The product is not in &lt;code&gt;sitemap.xml&lt;/code&gt;.&lt;/strong&gt; Shopify generates the sitemap automatically, which is exactly why nobody checks it. A product can drop out of the file while remaining active, priced, and purchasable, and Google gradually stops arriving. &lt;a href="https://storecanary.io/blog/shopify-product-missing-from-sitemap" rel="noopener noreferrer"&gt;The sitemap article&lt;/a&gt; covers what pulls a product out and how to confirm it in under a minute.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does the URL resolve to the wrong thing?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;5. The product was deleted or its handle changed.&lt;/strong&gt; Shopify does not create a redirect when you delete a product; the URL returns 404. Google can keep showing the old entry for a long time, so shoppers click a live-looking result and land on nothing. The &lt;a href="https://storecanary.io/blog/shopify-deleted-product-still-on-google" rel="noopener noreferrer"&gt;deleted-product guide&lt;/a&gt; covers how to clear these rather than waiting them out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. An out-of-stock app is redirecting the page.&lt;/strong&gt; A 301 from a temporarily sold-out product to the homepage looks tidy in admin and is a landing-page failure everywhere else. Merchant Center is direct: a product is disapproved when it specifies "a landing page (via the link attribute) that cannot be crawled by Google", including when "the landing page returns an HTTP error when crawled" (&lt;a href="https://support.google.com/merchants/answer/6098185" rel="noopener noreferrer"&gt;Merchant Center Help&lt;/a&gt;). See &lt;a href="https://storecanary.io/blog/shopify-product-page-redirect-merchant-center" rel="noopener noreferrer"&gt;the redirect case study&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is the product indexed but stripped of rich results?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;7. The rating markup broke and stars disappeared.&lt;/strong&gt; Google's &lt;a href="https://developers.google.com/search/docs/appearance/structured-data/review-snippet" rel="noopener noreferrer"&gt;review snippet documentation&lt;/a&gt; requires &lt;code&gt;aggregateRating&lt;/code&gt; to carry a &lt;code&gt;ratingValue&lt;/code&gt; plus either &lt;code&gt;ratingCount&lt;/code&gt; or &lt;code&gt;reviewCount&lt;/code&gt;, and requires that the review content be visible on the page. A theme that paints visual stars with nothing machine-readable behind them satisfies the shopper and not the parser. &lt;a href="https://storecanary.io/blog/review-stars-disappeared-google-shopify" rel="noopener noreferrer"&gt;The review stars article&lt;/a&gt; lists the five causes seen in practice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. The price in the result is stale or absent.&lt;/strong&gt; For &lt;a href="https://developers.google.com/search/docs/appearance/structured-data/merchant-listing" rel="noopener noreferrer"&gt;merchant listing markup&lt;/a&gt;, Google requires only &lt;code&gt;price&lt;/code&gt; and &lt;code&gt;priceCurrency&lt;/code&gt;; &lt;code&gt;availability&lt;/code&gt;, &lt;code&gt;priceValidUntil&lt;/code&gt;, &lt;code&gt;shippingDetails&lt;/code&gt;, and &lt;code&gt;hasMerchantReturnPolicy&lt;/code&gt; are recommended rather than required. The optional fields are also the ones Shopify never prompts you to emit, and their absence is what leaves a price looking expired. &lt;a href="https://storecanary.io/blog/google-wrong-price-shopify" rel="noopener noreferrer"&gt;The priceValidUntil article&lt;/a&gt; covers this in detail.&lt;/p&gt;

&lt;p&gt;Google's guideline applies to both: "Your structured data must be a true representation of the page content." Marking up content not visible to readers can draw a manual action that costs the page its rich-result eligibility (&lt;a href="https://developers.google.com/search/docs/appearance/structured-data/sd-policies" rel="noopener noreferrer"&gt;structured data general guidelines&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why did Merchant Center drop it while organic search kept it?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;9. Price mismatch.&lt;/strong&gt; A page can carry more than one price: the theme's JSON-LD, a discount app's markup, and the visible price can disagree. Merchant Center disapproves the inconsistency. &lt;a href="https://storecanary.io/blog/merchant-center-price-mismatch-shopify" rel="noopener noreferrer"&gt;The price mismatch trap&lt;/a&gt; explains how three prices end up on one page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. Availability mismatch.&lt;/strong&gt; Shopify's inventory and the feed update on different schedules. A restocked product can sit at "out of stock" in Shopping for hours or days. The specification requires availability to match across the landing page, checkout, and structured data; a drift in any one is a failure. Details in &lt;a href="https://storecanary.io/blog/shopify-merchant-center-availability-mismatch" rel="noopener noreferrer"&gt;the availability mismatch article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;11. Missing product identifiers.&lt;/strong&gt; A blank GTIN, MPN, or brand does not always disapprove a product. Depending on the category, it can instead limit how often the listing appears, which is harder to notice than an outright rejection. &lt;a href="https://storecanary.io/blog/shopify-google-shopping-missing-identifiers" rel="noopener noreferrer"&gt;The identifiers article&lt;/a&gt; explains the two tracks.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you identify which failure you are dealing with in ten minutes?
&lt;/h2&gt;

&lt;p&gt;Run these in order and stop at the first that gives you an answer. The goal is to identify the responsible system before changing anything, because a fix aimed at the wrong layer is indistinguishable from no fix at all.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check whether the URL is indexed.&lt;/strong&gt; Search Google for &lt;code&gt;site:&lt;/code&gt; followed by the exact product URL. A result means the page is in the index and the problem is downstream. No result means the problem is crawling, indexing, or discovery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspect the URL in Search Console.&lt;/strong&gt; URL Inspection names the reason directly. Its rendered view is the only reliable check: a raw &lt;code&gt;view-source&lt;/code&gt; is not proof either way, because a theme or app can inject or strip a canonical, a robots meta, or structured data after the initial HTML.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the page indexing report by reason, not by URL.&lt;/strong&gt; The report groups URLs by why they are not indexed, separating a crawl block from an indexing decision from a canonical choice. Reading by reason tells you whether one product broke or a rule broke across many.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test the page in the Rich Results Test.&lt;/strong&gt; This shows whether the product markup parses and what it contains, which is where a missing price or absent &lt;code&gt;aggregateRating&lt;/code&gt; becomes visible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the item separately in Merchant Center.&lt;/strong&gt; A disapproval or limited-performance warning is an entirely separate verdict from Google Search, and neither system reports the other.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why does this recur after every theme update or app install?
&lt;/h2&gt;

&lt;p&gt;Nearly every fix above is small: delete a &lt;code&gt;Disallow&lt;/code&gt; line, change a metafield, emit one more field in the offer.&lt;/p&gt;

&lt;p&gt;The harder part is what triggers these in the first place. Theme updates, app installs, bulk edits, feed syncs, new collections that mint fresh duplicate URLs. All are routine client operations. None announces what it broke. The gap between a product disappearing and anyone noticing is measured in whatever the client happens to look at, usually a revenue report weeks later.&lt;/p&gt;

&lt;p&gt;The question to add to every post-launch checklist is not "which one is it today" but "what would catch this next time, on the day it happens". That can be a habit of re-inspecting a sample of URLs after every theme change, a scheduled audit, or automated catalog monitoring. It cannot be the Shopify admin, which keeps showing a green Active badge across all eleven failure modes.&lt;/p&gt;




&lt;p&gt;Which of these 11 have you hit most in client stores? Any patterns by app category or theme?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The full version of this article — with screenshots and ongoing updates — lives on &lt;a href="https://storecanary.io/blog/shopify-products-not-showing-on-google" rel="noopener noreferrer"&gt;the StoreCanary blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>webdev</category>
      <category>seo</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Shopify canonical: fix the collection URL override for clients</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Mon, 10 Aug 2026 11:58:07 +0000</pubDate>
      <link>https://dev.to/max_buildogs/shopify-canonical-fix-the-collection-url-override-for-clients-24m4</link>
      <guid>https://dev.to/max_buildogs/shopify-canonical-fix-the-collection-url-override-for-clients-24m4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Shopify serves every product at two URLs; most themes link to the collection-scoped version via the &lt;code&gt;within&lt;/code&gt; filter, which contradicts the canonical tag.&lt;/li&gt;
&lt;li&gt;Google treats internal links as a canonicalization signal. When hundreds of links point to &lt;code&gt;/collections/name/products/handle&lt;/code&gt; and the tag declares &lt;code&gt;/products/handle&lt;/code&gt;, the tag loses.&lt;/li&gt;
&lt;li&gt;The fix is replacing &lt;code&gt;product.url | within: collection&lt;/code&gt; with &lt;code&gt;product.url&lt;/code&gt; in product card snippets, then aligning the sitemap and Merchant Center feed to the same URL.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you audit a client's Shopify store in Search Console, you may find a pile of product URLs under "Duplicate, Google chose different canonical than user." The Google-selected canonical is &lt;code&gt;/collections/name/products/handle&lt;/code&gt;, not the clean &lt;code&gt;/products/handle&lt;/code&gt; the canonical tag declares.&lt;/p&gt;

&lt;p&gt;This happens because Shopify's &lt;code&gt;within&lt;/code&gt; Liquid filter (used in most theme product cards) generates collection-scoped URLs. Every collection page and every related-products block links to the scoped version, casting hundreds of internal-link votes against the canonical declaration. Google weighs internal links as a canonicalization signal, so the declaration loses.&lt;/p&gt;

&lt;p&gt;This article covers why Shopify produces two URLs per product, how to confirm the issue in Search Console, the Liquid change that fixes it, and the sitemap/feed alignment needed to close the loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does "Duplicate, Google chose different canonical than user" actually mean?
&lt;/h2&gt;

&lt;p&gt;Two statuses describe similar situations and are worth keeping distinct. &lt;a href="https://support.google.com/webmasters/answer/7440203" rel="noopener noreferrer"&gt;Google's page indexing documentation&lt;/a&gt; defines &lt;strong&gt;"Duplicate, Google chose different canonical than user"&lt;/strong&gt; as a page "marked as canonical for a set of pages, but Google thinks another URL makes a better canonical" and has indexed that other URL instead. &lt;strong&gt;"Duplicate without user-selected canonical"&lt;/strong&gt; is the variant where no canonical was declared, so Google picked freely.&lt;/p&gt;

&lt;p&gt;A third status, "Alternate page with proper canonical tag," is the healthy outcome: the collection-scoped copy defers to the clean product URL and Google agrees. If your client's collection URLs sit there, nothing here needs fixing.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you confirm the issue on a client store?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Search Console → Indexing → Pages&lt;/strong&gt;, find "Duplicate, Google chose different canonical than user," and read the sample URLs. If they are &lt;code&gt;/products/...&lt;/code&gt;, the clean URLs are the ones losing.&lt;/li&gt;
&lt;li&gt;Run one of those URLs through &lt;strong&gt;URL Inspection&lt;/strong&gt; and compare &lt;em&gt;User-declared canonical&lt;/em&gt; with &lt;em&gt;Google-selected canonical&lt;/em&gt;. Google &lt;a href="https://support.google.com/webmasters/answer/9012289" rel="noopener noreferrer"&gt;states plainly&lt;/a&gt; there is no guarantee it will choose yours.&lt;/li&gt;
&lt;li&gt;Search &lt;code&gt;site:client-store.com inurl:/collections/ inurl:/products/&lt;/code&gt; in Google. Every result is a collection-scoped product URL Google has kept in its index.&lt;/li&gt;
&lt;li&gt;Open a collection page, click through to a product, and read the address bar. If it shows &lt;code&gt;/collections/.../products/...&lt;/code&gt;, that is the URL the theme is linking to. View source and check the &lt;code&gt;&amp;lt;link rel="canonical"&amp;gt;&lt;/code&gt; href: it should resolve to &lt;code&gt;/products/handle&lt;/code&gt; with no query string.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why does Google override the canonical tag?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Internal links are a canonicalization signal.&lt;/strong&gt; Google's &lt;a href="https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls" rel="noopener noreferrer"&gt;canonicalization documentation&lt;/a&gt; calls &lt;code&gt;rel="canonical"&lt;/code&gt; "a strong signal," a sitemap entry only "a weak signal," and then says to "link to the canonical URL rather than a duplicate URL." A store that declares one URL and links to another from every collection page has cast a vote against its own declaration at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;within&lt;/code&gt; filter.&lt;/strong&gt; Shopify's &lt;a href="https://shopify.dev/docs/api/liquid/filters/within" rel="noopener noreferrer"&gt;Liquid reference for &lt;code&gt;within&lt;/code&gt;&lt;/a&gt; shows it turning &lt;code&gt;/products/handle&lt;/code&gt; into &lt;code&gt;/collections/name/products/handle&lt;/code&gt;, and warns that "a standard product page and a product page in the context of a collection have the same content on separate URLs." Most themes call &lt;code&gt;within: collection&lt;/code&gt; in the product card snippet, so every collection page and every related-products block links to the scoped version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A canonical carrying a query parameter.&lt;/strong&gt; If the tag resolves to &lt;code&gt;/products/handle?variant=123&lt;/code&gt;, each variant is declaring itself the canonical version of the product, which is the opposite of consolidating them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two canonical tags.&lt;/strong&gt; Apps with theme access can inject their own &lt;code&gt;&amp;lt;link rel="canonical"&amp;gt;&lt;/code&gt; into the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;. Two contradictory declarations give Google permission to ignore both and choose freely.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you fix the canonical mismatch?
&lt;/h2&gt;

&lt;p&gt;The goal is to stop the site contradicting itself. All four signals need to point to the same URL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Internal links (highest impact)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the theme's product card snippet and related-products snippets, replace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight liquid"&gt;&lt;code&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;within&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;collection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight liquid"&gt;&lt;code&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the change that moves the needle, because it removes the volume of votes against the canonical declaration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The canonical tag&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One tag per page, resolving to &lt;code&gt;/products/handle&lt;/code&gt;, with no query string. Check for apps that may be injecting a second tag by viewing page source and searching for &lt;code&gt;rel="canonical"&lt;/code&gt; (there should be exactly one).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The sitemap&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;/sitemap_products_1.xml&lt;/code&gt; and confirm the URL form listed there matches what the canonical declares.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The Merchant Center feed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Google's &lt;a href="https://support.google.com/merchants/answer/6324399" rel="noopener noreferrer"&gt;specification for the &lt;code&gt;link&lt;/code&gt; attribute&lt;/a&gt; requires the product's landing page on the verified domain. Submitting the collection-scoped URL while canonicalizing to the clean one adds another contradiction for Google to resolve. Align the feed's &lt;code&gt;link&lt;/code&gt; value with the canonical.&lt;/p&gt;

&lt;p&gt;After making these changes, run the clean URL through Search Console's URL Inspection tool and request indexing. Canonical selection is recalculated on Google's own schedule; expect the status to change over days.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does this issue keep coming back after a theme update?
&lt;/h2&gt;

&lt;p&gt;The Liquid edit is small; keeping it is harder. A theme update or a new theme can reintroduce &lt;code&gt;within: collection&lt;/code&gt; without any explicit decision. Any app with theme access can add a second canonical tag to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;. Every new collection creates a fresh set of duplicate URLs for products that were previously clean.&lt;/p&gt;

&lt;p&gt;The same pattern shows up in related issues: a product can look healthy in the Shopify admin while the disagreement lives between the theme output, the feed, and Google's index. Something has to read the live pages and compare what they actually emit.&lt;/p&gt;




&lt;p&gt;Have you run into cases where a theme update silently reintroduced the &lt;code&gt;within&lt;/code&gt; filter after a fix? How do you catch it in your deployment workflow?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The full version of this article, with screenshots and ongoing updates, lives on &lt;a href="https://storecanary.io/blog/shopify-google-chose-different-canonical" rel="noopener noreferrer"&gt;the StoreCanary blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>webdev</category>
      <category>seo</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Shopify robots.txt.liquid is blocking your clients' products</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Wed, 05 Aug 2026 11:58:39 +0000</pubDate>
      <link>https://dev.to/max_buildogs/shopify-robotstxtliquid-is-blocking-your-clients-products-4g51</link>
      <guid>https://dev.to/max_buildogs/shopify-robotstxtliquid-is-blocking-your-clients-products-4g51</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;robots.txt&lt;/code&gt; on Shopify is generated from &lt;code&gt;robots.txt.liquid&lt;/code&gt; inside the theme. Any app or developer with theme access can add a &lt;code&gt;Disallow&lt;/code&gt; rule that blocks Googlebot from product pages, with zero warning in the admin.&lt;/li&gt;
&lt;li&gt;The only signals are in Google Search Console (Page indexing report, "Blocked by robots.txt" status) and Merchant Center diagnostics.&lt;/li&gt;
&lt;li&gt;Adding &lt;code&gt;noindex&lt;/code&gt; to a blocked page will not fix it. Googlebot must be unblocked first so it can read the tag.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;A single line in &lt;code&gt;robots.txt.liquid&lt;/code&gt; can silently drop every product in a client's Shopify catalogue from Google's crawl queue. The store looks healthy: pages are active, the theme is live, Shopify reports no errors. Google just stops fetching the URLs.&lt;/p&gt;

&lt;p&gt;This happens because Shopify generates &lt;code&gt;/robots.txt&lt;/code&gt; from a Liquid template inside the theme, not from a static file. Any app, developer, or agency with theme code access can modify it. The most common pattern: an SEO app or a previous developer adds a &lt;code&gt;Disallow&lt;/code&gt; rule that overreaches, catching &lt;code&gt;/products/&lt;/code&gt; paths alongside the intended targets like &lt;code&gt;/admin&lt;/code&gt; or &lt;code&gt;/cart&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This article covers how to detect the block in Search Console, read the correct user-agent group in &lt;code&gt;robots.txt.liquid&lt;/code&gt;, remove the offending rule, and verify that both &lt;code&gt;Googlebot&lt;/code&gt; and &lt;code&gt;Googlebot-image&lt;/code&gt; can reach product pages and images. It also explains why &lt;code&gt;noindex&lt;/code&gt; on a blocked page will not work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is a crawl block the same as a noindex?
&lt;/h2&gt;

&lt;p&gt;No, and the difference is the source of most failed fixes.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;noindex&lt;/code&gt; tag says: fetch this page, then keep it out of the index. A &lt;code&gt;robots.txt&lt;/code&gt; &lt;code&gt;Disallow&lt;/code&gt; says: do not fetch this page at all. The second sounds stricter. It is actually weaker, because a page Google never fetches is a page Google knows nothing about, including whether you wanted it indexed.&lt;/p&gt;

&lt;p&gt;Google Search Console names two separate statuses here. &lt;strong&gt;"Blocked by robots.txt"&lt;/strong&gt; means the URL was blocked; Google &lt;a href="https://support.google.com/webmasters/answer/7440203" rel="noopener noreferrer"&gt;notes this does not guarantee the page stays out of the index&lt;/a&gt;. &lt;strong&gt;"Indexed, though blocked by robots.txt"&lt;/strong&gt; means it appeared in results anyway, built from signals on pages that link to it.&lt;/p&gt;

&lt;p&gt;For a product page, the second status is the worst outcome: the URL surfaces in results, but Google has no title, description, price, or structured data to build a listing from.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does this appear in client stores that nobody touched?
&lt;/h2&gt;

&lt;p&gt;Because &lt;code&gt;robots.txt&lt;/code&gt; on Shopify is not a file you upload once. It is generated from &lt;code&gt;robots.txt.liquid&lt;/code&gt;, a template in the &lt;code&gt;templates/&lt;/code&gt; folder of the published theme. Shopify's own &lt;a href="https://help.shopify.com/en/manual/promoting-marketing/seo/editing-robots-txt" rel="noopener noreferrer"&gt;documentation for editing robots.txt.liquid&lt;/a&gt; calls this an unsupported customization, warns that Shopify Support cannot help with edits, and states that incorrect use of the feature can result in loss of all traffic.&lt;/p&gt;

&lt;p&gt;Anything with theme code access can write to it: an SEO app installed and later forgotten, an agency hardening the file during a migration, a developer restoring a theme backup. A &lt;a href="https://community.shopify.com/t/help-needed-fixing-blocked-by-robots-txt-issue-on-my-shopify-store/398495" rel="noopener noreferrer"&gt;Shopify Community thread from March 2025&lt;/a&gt; shows the typical pattern: 34 blocked product pages traced to &lt;code&gt;Disallow&lt;/code&gt; rules added by three installed apps.&lt;/p&gt;

&lt;p&gt;Shopify's defaults are sensible: &lt;code&gt;/admin&lt;/code&gt;, &lt;code&gt;/cart&lt;/code&gt;, &lt;code&gt;/checkout&lt;/code&gt;, &lt;code&gt;/search&lt;/code&gt;, &lt;code&gt;/policies/&lt;/code&gt;, and filtered collection URLs are blocked out of the box and should stay that way. The damage comes from what gets added beneath them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why will adding noindex to a blocked page not fix it?
&lt;/h2&gt;

&lt;p&gt;This catches experienced developers. If a page shows as "Indexed, though blocked by robots.txt", the instinct is to add a &lt;code&gt;noindex&lt;/code&gt; meta tag. Google's documentation says directly that this &lt;a href="https://developers.google.com/search/docs/crawling-indexing/block-indexing" rel="noopener noreferrer"&gt;will not work&lt;/a&gt;: for a &lt;code&gt;noindex&lt;/code&gt; rule to be honored, the page must not be blocked in &lt;code&gt;robots.txt&lt;/code&gt;, because a blocked crawler never reads the tag.&lt;/p&gt;

&lt;p&gt;The correct order: unblock first, so Googlebot can read the instruction. This is the inverse of the &lt;a href="https://storecanary.io/blog/shopify-phantom-noindex-google" rel="noopener noreferrer"&gt;phantom noindex pattern&lt;/a&gt;. There, Googlebot is allowed in but told to forget the page. Here, Googlebot is not allowed in at all, so it receives no instruction.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does this cost in Merchant Center?
&lt;/h2&gt;

&lt;p&gt;Google Merchant Center re-crawls the landing page behind every feed item. Its documentation lists a specific failure for pages it cannot reach: &lt;a href="https://support.google.com/merchants/answer/6098185?hl=en" rel="noopener noreferrer"&gt;product landing pages that robots.txt forbids Googlebot to download&lt;/a&gt;. Those products remain disapproved and stop showing in Shopping ads and free listings until the crawler can reach the page.&lt;/p&gt;

&lt;p&gt;Two user agents matter: &lt;code&gt;Googlebot&lt;/code&gt; for landing pages and &lt;code&gt;Googlebot-image&lt;/code&gt; for images. A rule that only blocks an image CDN path breaks image crawling while landing pages appear unaffected.&lt;/p&gt;

&lt;p&gt;One parsing rule worth knowing before editing: Google's &lt;a href="https://developers.google.com/search/docs/crawling-indexing/robots/robots_txt" rel="noopener noreferrer"&gt;robots.txt specification&lt;/a&gt; resolves conflicts by longest matching path. A named &lt;code&gt;User-agent: Googlebot&lt;/code&gt; block replaces the wildcard block entirely for that crawler, so a permissive &lt;code&gt;*&lt;/code&gt; group gives no protection if a restrictive &lt;code&gt;Googlebot&lt;/code&gt; group exists below it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you check a client store in five minutes?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read the live file.&lt;/strong&gt; Open &lt;code&gt;https://clientstore.com/robots.txt&lt;/code&gt; in a browser. It is public with no login needed. Read every &lt;code&gt;Disallow&lt;/code&gt; line and check whether any could match a product URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open Search Console, Page indexing.&lt;/strong&gt; Look for "Blocked by robots.txt" and "Indexed, though blocked by robots.txt". Each status lists the affected URLs, which is faster than reasoning about wildcard patterns by hand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run URL Inspection on a real product URL.&lt;/strong&gt; It reports directly whether crawling was allowed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find the template.&lt;/strong&gt; Online Store, Themes, Edit code on the published theme, then the &lt;code&gt;templates/&lt;/code&gt; folder. If &lt;code&gt;robots.txt.liquid&lt;/code&gt; is listed, the store runs a customized file worth reading in full.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check Merchant Center diagnostics&lt;/strong&gt; for product-page crawl issues, which name the URLs Google could not download.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How do you fix it?
&lt;/h2&gt;

&lt;p&gt;Remove or narrow the overreaching &lt;code&gt;Disallow&lt;/code&gt; rule in &lt;code&gt;robots.txt.liquid&lt;/code&gt; on the published theme. Leave Shopify's defaults for &lt;code&gt;/cart&lt;/code&gt;, &lt;code&gt;/checkout&lt;/code&gt;, and &lt;code&gt;/admin&lt;/code&gt; intact; they are there for good reasons.&lt;/p&gt;

&lt;p&gt;Check which user-agent group actually applies to Googlebot. A named &lt;code&gt;User-agent: Googlebot&lt;/code&gt; block replaces the wildcard block for that crawler; a clean &lt;code&gt;*&lt;/code&gt; group gives no protection if a restrictive &lt;code&gt;Googlebot&lt;/code&gt; group exists below it.&lt;/p&gt;

&lt;p&gt;Do not check immediately after saving. Google generally caches &lt;code&gt;robots.txt&lt;/code&gt; for up to 24 hours, and Merchant Center states a change may take 24 to 72 hours to reflect. Re-run URL Inspection after that window.&lt;/p&gt;

&lt;p&gt;If the pages genuinely need to stay out of the index: unblock in &lt;code&gt;robots.txt.liquid&lt;/code&gt; first, then add &lt;code&gt;noindex&lt;/code&gt;, in that order.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does this bug come back?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;robots.txt.liquid&lt;/code&gt; belongs to a theme, not to the store. Whichever theme is published determines what Google is served. Shopify documents that uploading a theme through the admin's Themes section will not import &lt;code&gt;robots.txt.liquid&lt;/code&gt;, while ThemeKit or command-line deployments preserve it. Swapping the published theme can quietly replace the rules Google reads with no visible change in the admin.&lt;/p&gt;

&lt;p&gt;Add an app with theme code access or a developer restoring a backup, and the same &lt;code&gt;Disallow&lt;/code&gt; line can reappear months later with no symptom in the admin, only on Google's side.&lt;/p&gt;




&lt;p&gt;Have you traced a client's lost Merchant Center traffic back to a &lt;code&gt;robots.txt.liquid&lt;/code&gt; change? What process do you use to catch it before the client does?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The full version of this article, with screenshots and ongoing updates, lives on &lt;a href="https://storecanary.io/blog/shopify-products-blocked-by-robots-txt" rel="noopener noreferrer"&gt;the StoreCanary blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>webdev</category>
      <category>seo</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Shopify deleted products: the 404 redirect fix for clients</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Mon, 03 Aug 2026 11:58:45 +0000</pubDate>
      <link>https://dev.to/max_buildogs/shopify-deleted-products-the-404-redirect-fix-for-clients-25jg</link>
      <guid>https://dev.to/max_buildogs/shopify-deleted-products-the-404-redirect-fix-for-clients-25jg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shopify does not create a redirect when a product is deleted; the old URL starts returning 404 immediately.&lt;/li&gt;
&lt;li&gt;Google keeps serving the old listing on its own recrawl schedule, independent of when the deletion happened.&lt;/li&gt;
&lt;li&gt;Fix: create a 301 in Content &amp;gt; Menus &amp;gt; URL Redirects pointing to the nearest live product or collection.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;When a client deletes a Shopify product, the platform removes it from the catalog and from the sitemap. That is where Shopify's involvement ends. No redirect is created, no 410 (Gone) response is served, no canonical update fires. The old URL returns 404 on the next request.&lt;/p&gt;

&lt;p&gt;For developers managing client stores, this becomes a recurring issue after catalog cleanups, rebrands, and bulk migrations. A batch of product URLs goes dead with no redirect plan. The client sees crawl errors accumulating in Search Console. You're on cleanup duty.&lt;/p&gt;

&lt;p&gt;This article covers why Shopify behaves this way, how to find every affected URL in a client store quickly, and the exact steps to set up redirects so the catalog stays clean after any deletion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does Shopify leave deleted product URLs returning 404?
&lt;/h2&gt;

&lt;p&gt;Shopify's own documentation describes URL redirects as something a merchant sets up after deleting a product. The platform does not prompt for a redirect destination at delete time and does not create one automatically. Delete a product and the URL simply starts returning 404 on the next request, whether that is a shopper with an old bookmark, a link from a collection page, or Googlebot revisiting a URL it already indexed.&lt;/p&gt;

&lt;p&gt;This is the default outcome after any product deletion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does Google keep showing the deleted product in search results?
&lt;/h2&gt;

&lt;p&gt;A 404 response tells Google the page does not exist, but Google does not drop a URL from its index the moment it crawls a 404. Per &lt;a href="https://developers.google.com/search/docs/crawling-indexing/troubleshoot-crawling-errors" rel="noopener noreferrer"&gt;Google's crawling documentation&lt;/a&gt;, removal happens on Google's own recrawl schedule for that specific URL. For a lower-traffic product page, that schedule can take weeks.&lt;/p&gt;

&lt;p&gt;The sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Product deleted. URL returns 404.&lt;/li&gt;
&lt;li&gt;Google has not revisited that URL yet.&lt;/li&gt;
&lt;li&gt;Google keeps serving the old listing with a cached snippet.&lt;/li&gt;
&lt;li&gt;Users click through and land on a 404.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How do you find every affected URL in a client store?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In Google Search Console: &lt;strong&gt;Indexing &amp;gt; Pages&lt;/strong&gt;, look at the "Not found (404)" entry under "Why pages aren't indexed." This lists every URL Google has crawled and found dead, including old product pages.&lt;/li&gt;
&lt;li&gt;In Shopify admin: &lt;strong&gt;Content &amp;gt; Menus &amp;gt; URL Redirects&lt;/strong&gt;. Search for the old product handles. If no redirect appears, the URL is a dead end.&lt;/li&gt;
&lt;li&gt;For a quick check on a single URL: paste it into a browser or run it through a header-checker tool to confirm the HTTP status code.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What are the common causes across client stores?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Product deleted with no redirect set up.&lt;/strong&gt; The default outcome after any deletion. Nothing in the delete flow prompts for one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Product handle changed during a rename or migration.&lt;/strong&gt; Changing the URL slug has the same effect as a deletion from Google's perspective. The old handle 404s unless a redirect was created at the same time the handle changed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Bulk catalog cleanup with no redirect plan.&lt;/strong&gt; Agencies doing larger migrations (discontinuing a product line, merging duplicates, restructuring collections) routinely end up with a batch of dead URLs because nothing in the bulk-delete flow surfaces the need for redirects first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Google has not recrawled the dead URL yet.&lt;/strong&gt; Even after a correct 404 is in place, the listing can persist in Google's index for an extended period. This compounds with the other causes: a 404 with no redirect just waits for Google to get around to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you set up the 301 redirects in Shopify admin?
&lt;/h2&gt;

&lt;p&gt;Go to &lt;strong&gt;Content &amp;gt; Menus &amp;gt; URL Redirects &amp;gt; Create redirect&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redirect from&lt;/strong&gt;: the old product path, e.g. &lt;code&gt;/products/old-handle&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redirect to&lt;/strong&gt;: the closest live equivalent. A similar product if one exists, or the parent collection if there is no direct replacement. Avoid pointing everything at the homepage. Google treats a batch of homepage redirects from unrelated URLs as soft 404s.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a bulk cleanup, export the full list of URLs before deleting anything, then use a CSV import to create the redirects in one pass. Rebuilding the list from Search Console error reports after the fact is slower and misses URLs Google has not crawled yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you confirm the fix and speed up Google's recrawl?
&lt;/h2&gt;

&lt;p&gt;Once redirects are live:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the &lt;strong&gt;URL Inspection&lt;/strong&gt; tool in Search Console on a few of the affected URLs. Confirm Google now sees a 301 response rather than a 404.&lt;/li&gt;
&lt;li&gt;Resubmit the sitemap under &lt;strong&gt;Indexing &amp;gt; Sitemaps&lt;/strong&gt;. This gives Google a signal to recrawl sooner than the passive schedule would trigger.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The inverse of this problem is covered in the &lt;a href="https://storecanary.io/blog/shopify-product-missing-from-sitemap" rel="noopener noreferrer"&gt;Shopify product missing from sitemap&lt;/a&gt; article: there, a live product drops out of what Google can discover; here, a dead product stays in what Google already found.&lt;/p&gt;




&lt;p&gt;When you hand off a store after a catalog migration, do you include a redirect audit in your checklist, or does this only come up after the client reports Search Console errors?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The full version of this article — with screenshots and ongoing updates — lives on &lt;a href="https://storecanary.io/blog/shopify-deleted-product-still-on-google" rel="noopener noreferrer"&gt;the StoreCanary blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>webdev</category>
      <category>seo</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
