<?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>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>
    <item>
      <title>Missing GTIN in Shopify throttles your clients' Shopping feed</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Thu, 30 Jul 2026 11:59:44 +0000</pubDate>
      <link>https://dev.to/max_buildogs/missing-gtin-in-shopify-throttles-your-clients-shopping-feed-2il5</link>
      <guid>https://dev.to/max_buildogs/missing-gtin-in-shopify-throttles-your-clients-shopping-feed-2il5</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;Missing GTIN, MPN, or brand does not disapprove a Shopify product in Merchant Center; it triggers "limited performance," cutting impressions with no warning visible in Shopify admin.&lt;/li&gt;
&lt;li&gt;The flag lives exclusively in Merchant Center Diagnostics, invisible from the Shopify product dashboard and the Google &amp;amp; YouTube channel app.&lt;/li&gt;
&lt;li&gt;Fix: add a real GTIN to the barcode field where one exists; for products with no identifier at all, submit &lt;code&gt;identifier_exists: false&lt;/code&gt; rather than leaving fields empty.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;When a Shopify product is missing its GTIN, MPN, or brand attribute in the Google feed, Merchant Center does not disapprove it. It throttles it. The listing stays approved, keeps syncing, and shows "Active" in Shopify admin, but Google quietly reduces how often it appears in Shopping ads and free listings. The flag is called "limited performance due to missing identifiers" and it lives exclusively in Merchant Center Diagnostics.&lt;/p&gt;

&lt;p&gt;This pattern shows up on stores where products were imported from a supplier without barcodes, or where the Google &amp;amp; YouTube channel app was configured once and never revisited. As a developer or agency building Shopify stores for clients, this warning never surfaces in the Shopify dashboard, which is why it can persist for months unnoticed.&lt;/p&gt;

&lt;p&gt;This article covers: where the warning appears, which Shopify fields map to which feed attributes, and the correct fix (including when &lt;code&gt;identifier_exists: false&lt;/code&gt; is the right call).&lt;/p&gt;

&lt;h2&gt;
  
  
  What does "limited performance due to missing identifiers" actually mean?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://support.google.com/merchants/answer/13693497" rel="noopener noreferrer"&gt;Google's own page on fixing Merchant Center disapprovals&lt;/a&gt; is explicit that this is a separate category from a disapproval: products with missing identifiers "will continue to show on Shopping ads and free listings, but may appear with limited performance."&lt;/p&gt;

&lt;p&gt;The outcome is not identical for every product. For categories where Google strictly requires a GTIN, a missing one can result in disapproval rather than throttling. Which path applies depends on your product category and which identifier is missing. In the common case (missing brand or MPN, or a GTIN-optional category), the listing survives but stops competing on equal footing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where do GTIN, MPN, and brand live in the Shopify data model?
&lt;/h2&gt;

&lt;p&gt;The GTIN maps from the barcode field, a field many merchants never fill in. &lt;a href="https://help.shopify.com/en/manual/apps/apps-by-shopify/google-shopping-app/adding-product-data" rel="noopener noreferrer"&gt;Shopify's documentation for the Google &amp;amp; YouTube channel&lt;/a&gt; is direct: "add a barcode (GTIN): in your Shopify admin, go to Products, open the product, and add the barcode in the Inventory section." Nothing in the standard product-creation flow requires it, so a product built by hand, imported from a supplier feed, or duplicated from a template can go live and sell for months without one.&lt;/p&gt;

&lt;p&gt;Brand and MPN are not native Shopify product fields. Brand is usually inferred from the Vendor field when the Google &amp;amp; YouTube app builds the feed. MPN has no Shopify-side home and typically requires an override inside the channel app itself, or a supplemental feed. On stores that installed the app once and never revisited the settings, MPN is simply absent from every product.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does this slip through into production undetected?
&lt;/h2&gt;

&lt;p&gt;A common scenario: a store imports its catalog from a supplier CSV that never populated the barcode field, because the supplier did not provide GTINs for private-label or custom items. The products publish fine, the channel app syncs fine, and Merchant Center approves the listings. A missing identifier alone does not block approval. It shows up only as a "Needs attention" warning in Diagnostics, quietly capping impressions against competitors with complete identifiers.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you find which products are affected?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In Shopify admin, open a sample of products under &lt;strong&gt;Products&lt;/strong&gt; and check the &lt;strong&gt;Inventory&lt;/strong&gt; section. Is the barcode field blank?&lt;/li&gt;
&lt;li&gt;Open the &lt;strong&gt;Google &amp;amp; YouTube&lt;/strong&gt; sales channel app and look for a "Needs attention" or diagnostics count, not just an overall "connected" status.&lt;/li&gt;
&lt;li&gt;In Google Merchant Center, go to the &lt;strong&gt;Diagnostics&lt;/strong&gt; tab (or &lt;strong&gt;Needs attention&lt;/strong&gt; under Products) and filter for identifier-related issues. &lt;a href="https://support.google.com/merchants/answer/14899834" rel="noopener noreferrer"&gt;Google's guide to fixing a missing or incorrect GTIN&lt;/a&gt; walks through this view and a bulk-CSV fix for stores with many affected products.&lt;/li&gt;
&lt;li&gt;Spot-check whether the Vendor field is populated for every product. An empty Vendor field typically means an empty brand attribute in the feed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  When is &lt;code&gt;identifier_exists: false&lt;/code&gt; the right fix?
&lt;/h2&gt;

&lt;p&gt;If the product has a real-world GTIN (most branded, mass-produced goods do), add it to the barcode field and let it flow through the feed.&lt;/p&gt;

&lt;p&gt;For products with no identifier (handmade goods, custom items, private-label products with no assigned barcode), submit &lt;code&gt;identifier_exists&lt;/code&gt; as &lt;code&gt;false&lt;/code&gt; alongside an MPN and brand where you have them. &lt;a href="https://support.google.com/merchants/answer/6324478" rel="noopener noreferrer"&gt;Google's documentation for the &lt;code&gt;identifier_exists&lt;/code&gt; attribute&lt;/a&gt; is specific on this point: setting it to &lt;code&gt;false&lt;/code&gt; on a product Google can find evidence has a real identifier "will receive a warning" of its own. It is an honest declaration, not a workaround.&lt;/p&gt;

&lt;p&gt;This issue sits in the same family as price and landing-page mismatches. Merchant Center compares what your feed claims against what it can verify, then adjusts how much it trusts the listing. For the pricing version of the same pattern, see &lt;a href="https://storecanary.io/blog/merchant-center-price-mismatch-shopify" rel="noopener noreferrer"&gt;the price mismatch trap&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Have you run into a client store where this "Needs attention" flag had been sitting in Diagnostics for months before anyone caught it? What triggered the 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-google-shopping-missing-identifiers" 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 out-of-stock redirect breaks Merchant Center feed</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Wed, 29 Jul 2026 11:58:23 +0000</pubDate>
      <link>https://dev.to/max_buildogs/shopify-out-of-stock-redirect-breaks-merchant-center-feed-a7</link>
      <guid>https://dev.to/max_buildogs/shopify-out-of-stock-redirect-breaks-merchant-center-feed-a7</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;A product URL that 301-redirects to the homepage triggers a Merchant Center "landing page mismatch" disapproval; the feed URL no longer resolves to the product it advertises.&lt;/li&gt;
&lt;li&gt;The redirect almost always comes from an out-of-stock app with a redirect toggle enabled by default. A manual rule added in Shopify Admin is rarely the source.&lt;/li&gt;
&lt;li&gt;Fix: keep the page live, set &lt;code&gt;offers.availability&lt;/code&gt; to &lt;code&gt;OutOfStock&lt;/code&gt; in JSON-LD and &lt;code&gt;out_of_stock&lt;/code&gt; in the feed. Reserve 301s for discontinued products, pointed at a specific replacement.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is the failure chain: a client's product goes out of stock, an app redirects the product URL to the homepage, and within the next Merchant Center crawl cycle, the listing is disapproved for a "landing page mismatch." No error appears in Shopify admin. The product is still active, still priced, still in the feed. The only symptom is a disapproval accumulating in the Diagnostics tab.&lt;/p&gt;

&lt;p&gt;This is a common side effect when out-of-stock apps are installed with default settings. The redirect toggle is often on by default, intended to stop shoppers from hitting a "sold out" page with no path forward. The unintended consequence: Google's feed crawler follows the same product URL and finds a homepage instead of a product, which fails Merchant Center's &lt;code&gt;link&lt;/code&gt; attribute requirement.&lt;/p&gt;

&lt;p&gt;This article covers the failure mechanism, where to locate the redirect rule in a client's store, the correct schema pattern, and an audit checklist.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the redirect actually break?
&lt;/h2&gt;

&lt;p&gt;Three things break simultaneously.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The product's SEO equity.&lt;/strong&gt; A product page that has been live for months carries indexed history, internal links, and sometimes external backlinks. A 301 to the homepage transfers none of that value to anything useful; it tells Google the product no longer exists at that URL, which is usually false.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Merchant Center listing.&lt;/strong&gt; The &lt;code&gt;link&lt;/code&gt; attribute in the feed still points at the original product URL. Once that URL redirects to a non-product page, Google's crawler flags a mismatch and disapproves the listing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The paid and organic click.&lt;/strong&gt; A shopper who clicked a Shopping ad or an organic result for that specific product lands on a homepage with no context. Most will not search again for the item; the click is lost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does the redirect rule come from?
&lt;/h2&gt;

&lt;p&gt;The source is almost always an out-of-stock app. Most out-of-stock, back-in-stock, or "sold out" apps include a setting to redirect unavailable product URLs elsewhere. The default destination is usually &lt;code&gt;/&lt;/code&gt; or a collection page. When that toggle is on, every product that hits zero inventory across all variants gets a redirect applied to its URL automatically.&lt;/p&gt;

&lt;p&gt;Check the app's settings first. The redirect rule may not appear in Shopify Admin's URL Redirects list at all if the app manages it internally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does Merchant Center flag this as a landing page issue?
&lt;/h2&gt;

&lt;p&gt;Google's product data specification requires the &lt;code&gt;link&lt;/code&gt; attribute to point directly to the page for that specific product. The &lt;a href="https://support.google.com/merchants/answer/6324399" rel="noopener noreferrer"&gt;link attribute specification&lt;/a&gt; and the &lt;a href="https://support.google.com/merchants/answer/7052112" rel="noopener noreferrer"&gt;product data specification overview&lt;/a&gt; both state the destination must let the shopper view and purchase the exact item advertised.&lt;/p&gt;

&lt;p&gt;A redirect to the homepage fails that requirement regardless of the reason for the redirect. Google's crawler does not distinguish between "product gone forever" and "temporarily out of stock." It follows the URL, finds a non-product page, and disapproves.&lt;/p&gt;

&lt;p&gt;The error label in Diagnostics is typically "landing page does not match" or "destination mismatch," and it lists the exact affected URLs.&lt;/p&gt;

&lt;p&gt;This pattern is a close cousin of the price-mismatch disapproval covered in &lt;a href="https://storecanary.io/blog/merchant-center-price-mismatch-shopify" rel="noopener noreferrer"&gt;Products disapproved in Google Merchant Center: the price mismatch trap&lt;/a&gt;. Both failures come from a gap between what the feed claims and what the live page actually delivers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the correct implementation for out-of-stock products?
&lt;/h2&gt;

&lt;p&gt;Keep the page live. Signal unavailability through data, not through the URL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the theme's JSON-LD&lt;/strong&gt;, set &lt;code&gt;offers.availability&lt;/code&gt; to &lt;code&gt;OutOfStock&lt;/code&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;"@context"&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://schema.org"&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;"Product"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Product Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&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;"@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;"https://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;"49.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;"EUR"&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="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;&lt;strong&gt;In the product feed&lt;/strong&gt;, set &lt;code&gt;availability&lt;/code&gt; to &lt;code&gt;out_of_stock&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add a restock notification form&lt;/strong&gt; on the product page. This keeps the page converting intent rather than abandoning the visitor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reserve 301 redirects for genuinely discontinued products&lt;/strong&gt;, and point them at a specific replacement product. A redirect to a real substitute preserves the shopper's intent and most of the page's SEO value; a redirect to &lt;code&gt;/&lt;/code&gt; preserves neither.&lt;/p&gt;

&lt;p&gt;This is the same discipline as in &lt;a href="https://storecanary.io/blog/shopify-phantom-noindex-google" rel="noopener noreferrer"&gt;The phantom noindex: how Shopify apps silently hide your products from Google&lt;/a&gt;: a product can appear completely fine inside Shopify admin while the public-facing URL tells Google something entirely different.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you audit a client's store for this issue?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open Shopify Admin &amp;gt; Online Store &amp;gt; Navigation &amp;gt; &lt;strong&gt;URL Redirects&lt;/strong&gt; and search for rules pointing product handles at &lt;code&gt;/&lt;/code&gt; or a collection URL.&lt;/li&gt;
&lt;li&gt;Open the settings of every out-of-stock, back-in-stock, or sold-out app installed. Look for a redirect toggle (often labeled "redirect to another page" or "redirect when sold out") and disable it or change the behavior to keep the product page live.&lt;/li&gt;
&lt;li&gt;In Merchant Center, open the &lt;strong&gt;Diagnostics&lt;/strong&gt; tab and filter for "landing page" or "destination" errors. These list the exact affected URLs.&lt;/li&gt;
&lt;li&gt;Spot-check a sample of currently out-of-stock products: visit each URL in an incognito window and confirm it loads the product page rather than the homepage.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of this produces an error inside Shopify admin. The product stays active, the feed submits without a validation warning at first glance, and the only symptom is a slow accumulation of Merchant Center disapprovals.&lt;/p&gt;




&lt;p&gt;Have you run into this redirect default on a client's store? Did you catch it through Merchant Center, or through something else?&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-product-page-redirect-merchant-center" 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 sitemap drops live products: detect and fix it</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Tue, 28 Jul 2026 11:57:35 +0000</pubDate>
      <link>https://dev.to/max_buildogs/shopify-sitemap-drops-live-products-detect-and-fix-it-2djm</link>
      <guid>https://dev.to/max_buildogs/shopify-sitemap-drops-live-products-detect-and-fix-it-2djm</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Shopify product can be active, priced, and purchasable while completely absent from &lt;code&gt;sitemap.xml&lt;/code&gt;, with no warning anywhere in admin.&lt;/li&gt;
&lt;li&gt;Common triggers: out-of-stock filters in SEO apps, handle renames, tag/collection-scoped sitemap logic, bulk-import sync gaps.&lt;/li&gt;
&lt;li&gt;Detection requires cross-referencing the live catalog against the sitemap file, or using Search Console's URL Inspection tool.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;This is a bug that affects Shopify stores you build for clients: a product can be active, correctly priced, and fully purchasable while completely absent from &lt;code&gt;sitemap.xml&lt;/code&gt;. No error in Shopify admin. No broken page. Nothing flags the mismatch.&lt;/p&gt;

&lt;p&gt;The product is orphaned from Google's crawl signal. It still sells. Your client sees no problem. But over weeks, rankings erode and the product can end up in "crawled - currently not indexed" in Search Console.&lt;/p&gt;

&lt;p&gt;This happens because Shopify generates &lt;code&gt;sitemap.xml&lt;/code&gt; and child files like &lt;code&gt;sitemap_products_1.xml&lt;/code&gt; as a downstream artifact, separate from catalog status. Several routine operations can break the connection: an SEO app filtering out-of-stock items, a handle rename that takes a sync cycle to propagate, or a bulk catalog import that doesn't cleanly trigger sitemap regeneration.&lt;/p&gt;

&lt;p&gt;This article covers the mechanism, the common triggers, and the steps to diagnose and fix it in a client's store.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does a live product fall out of the Shopify sitemap?
&lt;/h2&gt;

&lt;p&gt;Shopify auto-generates &lt;code&gt;sitemap.xml&lt;/code&gt; from the published catalog, but several ordinary changes can pull one specific product out of it without touching its "Active" status:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A sold-out filter in a sitemap or SEO app.&lt;/strong&gt; Several popular apps let you exclude out-of-stock products from the generated sitemap. A product that goes briefly out of stock and comes back can stay excluded if the app's cache or rule doesn't re-add it promptly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A handle change.&lt;/strong&gt; Renaming a product's URL handle removes the old handle from the sitemap. If the new handle takes a sync cycle to appear, or the internal link update lags, the product is temporarily invisible via sitemap from either URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A tag, collection, or metafield rule.&lt;/strong&gt; Custom sitemap logic keyed off tags or collections can silently drop a product the moment it's untagged or moved out of scope, even if it still sells on its own product page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A bulk edit or CSV re-import.&lt;/strong&gt; Large catalog updates sometimes unpublish and republish products in the same batch; if the publish event doesn't cleanly propagate to the sitemap generator, the product is left out until the next full regeneration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In every case, the product URL loads fine. The page renders, the price is correct, checkout works. The only missing piece is the sitemap entry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why doesn't Shopify admin surface this problem?
&lt;/h2&gt;

&lt;p&gt;Shopify admin reports on your catalog, not on the sitemap's contents. There's no screen listing "products missing from sitemap.xml," because from Shopify's perspective the product is fine: active, priced, in stock. The sitemap is generated separately, and its accuracy relative to the live catalog isn't something the admin dashboard audits.&lt;/p&gt;

&lt;p&gt;That gap is why this failure can run for months. Catching it requires manually diffing your published catalog against the sitemap file, which nobody does by hand at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does a missing sitemap entry actually affect rankings?
&lt;/h2&gt;

&lt;p&gt;A sitemap isn't required for indexing. Google can still discover a page through internal links, and per &lt;a href="https://developers.google.com/search/docs/crawling-indexing/sitemaps/overview" rel="noopener noreferrer"&gt;Google's documentation on sitemaps&lt;/a&gt;, a sitemap is a hint about which URLs to crawl. But it's one of the strongest recrawl signals you control, particularly for pages with weak internal linking.&lt;/p&gt;

&lt;p&gt;Products fit that profile precisely. Many stores link to a product from only one or two collection pages. If that collection link also drops (the product went briefly out of stock and got auto-removed from its collection) at the same time the sitemap entry disappears, the product loses both discovery paths at once.&lt;/p&gt;

&lt;p&gt;Google doesn't deindex it immediately, but without a recrawl signal the result over time is stale index snippets (old price, wrong stock status), a slow ranking decline, or the page settling into "crawled - currently not indexed" in Search Console.&lt;/p&gt;

&lt;p&gt;This is related to the redirect failure covered in &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&lt;/a&gt;: different mechanism (missing discovery signal vs. broken destination), same eventual outcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you diagnose this in a client's store?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;yourdomain.com/sitemap.xml&lt;/code&gt;, follow the link to &lt;code&gt;sitemap_products_N.xml&lt;/code&gt;, and use browser find-in-page to search for the product's handle or canonical URL.&lt;/li&gt;
&lt;li&gt;Target high-risk products first: recently out-of-stock items that came back in stock, recently renamed products, and anything touched by a bulk edit or CSV import.&lt;/li&gt;
&lt;li&gt;In Google Search Console, run &lt;strong&gt;URL Inspection&lt;/strong&gt; on the product URL and check the "Sitemaps" field. If it's blank, Google has no sitemap pointing to that URL.&lt;/li&gt;
&lt;li&gt;In the Search Console &lt;strong&gt;Sitemaps&lt;/strong&gt; report, watch the "Discovered URLs" count over time. An unexplained drop that doesn't match a real catalog reduction is the signal to dig in.&lt;/li&gt;
&lt;li&gt;Open the settings of any sitemap-filtering or SEO app and look for a "hide out-of-stock products from sitemap" toggle. This is the most common cause.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What's the fix once you've confirmed the gap?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identify the exclusion rule&lt;/strong&gt; in whichever app or theme customization controls the sitemap, and adjust it so active products are never filtered based on a temporary stock dip.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Force a regeneration.&lt;/strong&gt; Resaving the product (even with no real change) typically triggers Shopify to re-include it in the next sitemap cycle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request indexing directly&lt;/strong&gt; for the affected URL via Search Console's URL Inspection tool, rather than waiting for the next natural crawl.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reinforce internal linking&lt;/strong&gt; so the product isn't solely dependent on the sitemap for discovery. Keep it linked from at least one collection or "related products" block at all times, including while temporarily out of stock.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Shopify admin accurately reflects catalog state, but not how Google currently sees the store. Those two views can drift apart quietly, and a sitemap gap is one of the quieter ways it happens.&lt;/p&gt;




&lt;p&gt;Have you run into this in client stores? What's your current approach to monitoring sitemap coverage across a portfolio of Shopify sites?&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-product-missing-from-sitemap" 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 noindex bug: detect and fix the seo.hidden metafield</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Mon, 27 Jul 2026 11:57:44 +0000</pubDate>
      <link>https://dev.to/max_buildogs/shopify-noindex-bug-detect-and-fix-the-seohidden-metafield-156m</link>
      <guid>https://dev.to/max_buildogs/shopify-noindex-bug-detect-and-fix-the-seohidden-metafield-156m</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shopify's &lt;code&gt;seo.hidden&lt;/code&gt; metafield (value &lt;code&gt;1&lt;/code&gt;) emits a &lt;code&gt;noindex&lt;/code&gt; directive with no admin warning anywhere in the store.&lt;/li&gt;
&lt;li&gt;Three common sources: CSV imports carrying the field over, SEO app bulk rules with loose conditions, theme edits left in by mistake.&lt;/li&gt;
&lt;li&gt;Detection takes under 5 minutes via Search Console URL Inspection; the fix is one metafield deletion.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;You're auditing a client's Shopify store and a product that used to rank has vanished from Google entirely. No penalty notice, no manual action, nothing in the admin flags it. The product is published, the page loads, checkout works. The issue is a single &lt;code&gt;noindex&lt;/code&gt; directive in the HTTP response or HTML head that Google is honoring silently.&lt;/p&gt;

&lt;p&gt;This is one of the most common technical SEO bugs in Shopify builds, and it almost never comes from a deliberate decision. It surfaces after CSV imports, SEO app bulk rules, or a theme edit that didn't get reverted. The symptoms are invisible to every human visitor. Only what Google reads when it crawls the store is different.&lt;/p&gt;

&lt;p&gt;This article covers where the directive originates (three specific causes), how to confirm it in under 5 minutes, and the steps to remove it without breaking legitimate noindex cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is actually happening?
&lt;/h2&gt;

&lt;p&gt;Somewhere in the page response, a &lt;code&gt;meta robots&lt;/code&gt; tag contains &lt;code&gt;noindex&lt;/code&gt;, or the HTTP response carries an &lt;code&gt;X-Robots-Tag: noindex&lt;/code&gt; header. Either instruction is taken literally by Google: stop indexing this page. On Shopify, this is almost always triggered by something other than a deliberate decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does the noindex come from on Shopify?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;seo.hidden&lt;/code&gt; metafield.&lt;/strong&gt; This is the most common source. It gets set to &lt;code&gt;1&lt;/code&gt; (or &lt;code&gt;true&lt;/code&gt;) via three typical paths: a bulk CSV import that carried the field from a previous platform, a migration tool that defaults new products to hidden until marked ready, or a developer toggling it on a handful of products without reverting. It sits on the product record, invisible unless you scroll to the Metafields section in the product editor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An SEO app with a misconfigured indexing rule.&lt;/strong&gt; Several Shopify SEO apps offer bulk rules like "noindex all products under $X" or "noindex out-of-stock products." If the rule's condition is broader than intended, or if it keeps running after a cleanup campaign is done, it continues noindexing products that match the too-loose condition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A CSV import carrying hidden/draft flags.&lt;/strong&gt; Bulk product imports from a supplier feed, a platform migration, or a spreadsheet update can write a hidden flag into &lt;code&gt;seo.hidden&lt;/code&gt; without the person running the import ever seeing that field in the sheet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Theme edits.&lt;/strong&gt; Someone editing &lt;code&gt;theme.liquid&lt;/code&gt; or a product template while debugging something else leaves a stray &lt;code&gt;{% if %}&lt;/code&gt; block that outputs a noindex tag under unintended conditions, or hardcodes it during testing and forgets to remove it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to confirm a noindex in under 5 minutes?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;site:&lt;/code&gt; search.&lt;/strong&gt; In Google, search &lt;code&gt;site:yourstore.com/products/the-handle&lt;/code&gt;. Zero results on an established product almost certainly indicates a noindex directive in effect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search Console URL Inspection.&lt;/strong&gt; Paste the exact product URL. Look for "Excluded by 'noindex' tag." This is the definitive confirmation: Google saw the tag and honored it, as opposed to simply not having crawled the page yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;View source.&lt;/strong&gt; Open the page, search (Ctrl+F) for &lt;code&gt;noindex&lt;/code&gt; in the HTML &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;. Also check response headers via browser dev tools (Network tab, document request, Response Headers) for &lt;code&gt;X-Robots-Tag&lt;/code&gt;. That header won't appear in view-source since it's server-level.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the metafield directly.&lt;/strong&gt; In Shopify admin, open the product, scroll to Metafields, and look for &lt;code&gt;seo.hidden&lt;/code&gt;. If it's set to &lt;code&gt;1&lt;/code&gt;, that's your answer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Run this across the full catalog. CSV imports and app rules typically hit dozens of products at once, silently, which is why the problem usually surfaces by accident months after it happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is the noindex always a bug?
&lt;/h2&gt;

&lt;p&gt;No. Free-sample products, internal test SKUs, gift-with-purchase items, and wholesale-only listings are often deliberately noindexed. Before removing a tag, confirm this is a public-facing, sellable product that should rank. Removing noindex from an internal SKU or a page the client genuinely wants hidden just creates a different problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to fix each cause?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Metafield:&lt;/strong&gt; Shopify Admin, open the product, go to Metafields, find &lt;code&gt;seo.hidden&lt;/code&gt;, then delete it or set it to &lt;code&gt;0&lt;/code&gt;/false.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SEO app rule:&lt;/strong&gt; Open the app's indexing/visibility rules, identify the condition matching the affected products, then narrow or disable it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Theme edit:&lt;/strong&gt; Search &lt;code&gt;theme.liquid&lt;/code&gt; and the relevant product template/snippets for &lt;code&gt;noindex&lt;/code&gt;, then remove or correct the condition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Header-level noindex:&lt;/strong&gt; If the signal comes from an &lt;code&gt;X-Robots-Tag&lt;/code&gt; response header rather than the HTML, it is usually set by an app or a custom Shopify Function/edge rule. Check installed apps that touch page headers before looking at the theme.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The fix typically takes 5 to 15 minutes once you have found the source. The hard part is discovering it exists at all: a noindexed page renders and behaves normally for every visitor; only what Google reads is different.&lt;/p&gt;




&lt;p&gt;Have you run into a noindex source not on this list (a specific app, a Shopify Function, a webhook gone wrong)? Drop it in the comments.&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-phantom-noindex-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 availability mismatch breaking your clients' feeds</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Fri, 24 Jul 2026 11:57:51 +0000</pubDate>
      <link>https://dev.to/max_buildogs/shopify-availability-mismatch-breaking-your-clients-feeds-24ni</link>
      <guid>https://dev.to/max_buildogs/shopify-availability-mismatch-breaking-your-clients-feeds-24ni</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 inventory updates instantly; the Merchant Center feed does not. Drift between them silently suppresses products in Shopping with no alert in Shopify admin.&lt;/li&gt;
&lt;li&gt;Three main triggers: scheduled feed fetches (not push-on-change), duplicate submissions from two competing apps, and theme JSON-LD that doesn't update when a variant sells out.&lt;/li&gt;
&lt;li&gt;Fix: one feed source, push-on-change sync, and verified &lt;code&gt;offers.availability&lt;/code&gt; updates in page schema after any theme or app change.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a bug your clients won't notice until it costs them revenue. Shopify's inventory count updates the instant a sale, restock, or manual edit happens. Google Merchant Center reads availability from a separate feed pipeline with its own update cadence. When the two drift, products either vanish from Shopping results (shown as out of stock when they're not) or keep appearing after selling out, burning impressions on a page that can't convert.&lt;/p&gt;

&lt;p&gt;Neither system alerts you to the gap. Shopify admin shows inventory as healthy; Merchant Center shows a suppressed or disapproved listing that Shopify cannot see. This article covers the four mechanisms that cause the drift, how to detect it across a client's store, and the structural fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does Shopify availability diverge from the Merchant Center feed?
&lt;/h2&gt;

&lt;p&gt;Shopify's inventory count is authoritative and updates instantly. Merchant Center reads from a separate feed pipeline with its own cadence. Four mechanisms cause the drift:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scheduled fetches instead of push-on-change.&lt;/strong&gt; Some feed setups pull a fresh file every 12 or 24 hours rather than pushing changes the moment inventory shifts. Between fetches, Google works from a snapshot that is already stale.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two feed sources submitting the same product.&lt;/strong&gt; Stores running both Shopify's native Google &amp;amp; YouTube channel app and a legacy or third-party feed app can submit the same product twice, with each app reporting different availability for the same GTIN.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Page JSON-LD contradicting the feed.&lt;/strong&gt; Google cross-checks the feed's &lt;code&gt;availability&lt;/code&gt; value against the &lt;code&gt;offers.availability&lt;/code&gt; field in the live page's JSON-LD. If a theme caches that value, or a variant-level app updates the visible "Sold out" badge without touching the schema, the page and the feed tell Google conflicting things.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Variant-level stock not reflected at product level.&lt;/strong&gt; A product where nine of ten variants are sold out can report availability inconsistently depending on which variant the feed treats as canonical, especially after a variant is deleted or reordered.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why is this invisible in Shopify admin?
&lt;/h2&gt;

&lt;p&gt;Shopify admin shows Shopify's own view only: current inventory, current variant status, current "Active" listing state. It has no visibility into what Merchant Center currently believes about that same product, because that belief lives on Google's side, driven by a feed pipeline Shopify doesn't monitor after handoff. A product can look perfectly healthy in Shopify admin while its Merchant Center listing is quietly suppressed or showing the wrong stock state. You only find out from Merchant Center's own diagnostics, if you check them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the mismatch actually cost?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;In-stock product showing as out of stock:&lt;/strong&gt; Google's Shopping algorithm deprioritizes unavailable listings. The product effectively disappears from Shopping results even though it's fully sellable, with no error raised anywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Out-of-stock product still showing as in stock:&lt;/strong&gt; worse. You're spending impressions (and often ad budget) sending shoppers to a product they can't buy. A pattern of "available" listings that turn out unsellable is exactly what Google's &lt;a href="https://support.google.com/merchants/answer/6150127" rel="noopener noreferrer"&gt;misrepresentation policy&lt;/a&gt; targets. Repeated mismatches can escalate from a warning to an account-level suspension.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you check a client's store for this?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In Google Merchant Center, open &lt;strong&gt;Products &amp;gt; Diagnostics&lt;/strong&gt; and filter for availability-related warnings. This shows exactly which items Google currently considers mismatched.&lt;/li&gt;
&lt;li&gt;Pick five currently out-of-stock products and view each page source. Search for &lt;code&gt;"availability"&lt;/code&gt; inside the JSON-LD block and confirm it reads &lt;code&gt;OutOfStock&lt;/code&gt;, not &lt;code&gt;InStock&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If the store runs more than one sales-channel or feed app connected to Google, check Merchant Center's &lt;strong&gt;Apps&lt;/strong&gt; and &lt;strong&gt;Products&lt;/strong&gt; tabs for duplicate submissions of the same item under different feed sources.&lt;/li&gt;
&lt;li&gt;Check the feed's update frequency. Shopify's native Google &amp;amp; YouTube app pushes on inventory change, but a secondary feed app may be on a manual or scheduled sync. Confirm it isn't running on a 24-hour-or-longer delay for fast-moving SKUs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is the same category of failure as the &lt;a href="https://storecanary.io/blog/shopify-product-page-redirect-merchant-center" rel="noopener noreferrer"&gt;redirect-to-homepage mismatch&lt;/a&gt; and the &lt;a href="https://storecanary.io/blog/merchant-center-price-mismatch-shopify" rel="noopener noreferrer"&gt;price mismatch trap&lt;/a&gt;: Merchant Center compares feed claims against the live page, and any gap in price, landing page, or availability gets treated as misrepresentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the structural fix?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use a single feed source.&lt;/strong&gt; If the store runs Shopify's native Google &amp;amp; YouTube channel alongside a separate feed app, pick one and disconnect the other. Duplicate submissions are the most common cause of contradictory availability for the same product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Push on inventory change, not on a fixed schedule,&lt;/strong&gt; especially for products that sell out quickly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirm theme JSON-LD updates &lt;code&gt;offers.availability&lt;/code&gt; on variant sellout,&lt;/strong&gt; not just the visible "Sold out" label. The schema and the badge can drift independently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-check after any theme or app update.&lt;/strong&gt; A theme update or new inventory app is the single most common moment this breaks, because it touches the code that writes the schema.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Have you run into a case where a feed app and Shopify's native channel were both pushing to Merchant Center at the same time? What did the conflict look like in diagnostics?&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-merchant-center-availability-mismatch" 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 schema bugs killing clients' Google review stars</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Thu, 23 Jul 2026 11:57:52 +0000</pubDate>
      <link>https://dev.to/max_buildogs/shopify-product-schema-bugs-killing-clients-google-review-stars-klk</link>
      <guid>https://dev.to/max_buildogs/shopify-product-schema-bugs-killing-clients-google-review-stars-klk</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;Duplicate &lt;code&gt;Product&lt;/code&gt; schema (theme + review app both active) causes Google to drop or randomly pick a version, removing review stars.&lt;/li&gt;
&lt;li&gt;A malformed JSON-LD block fails silently: the storefront renders normally but Google ignores the entire structured data payload.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;noindex&lt;/code&gt; tag from a metafield, app rule, or theme edit removes the product page from Google entirely, stars included.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;When a client reports their Google review stars disappeared, the storefront is usually working fine. The bug lives in the structured data layer, invisible to shoppers and often invisible to the developer who introduced it. No error from Google, no console warning: just a star rating that vanished and a click-through rate in quiet decline.&lt;/p&gt;

&lt;p&gt;The five causes below cover most cases on Shopify. Three are fixable in under an hour. All five can be diagnosed with free tools before touching a single line of code.&lt;/p&gt;

&lt;p&gt;This article is aimed at developers and agencies maintaining Shopify stores: you will find the diagnostic steps, the specific theme variables and app settings involved, and the correct fix for each case.&lt;/p&gt;




&lt;h2&gt;
  
  
  How do you verify the schema state before touching anything?
&lt;/h2&gt;

&lt;p&gt;Two tools, two minutes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Google's &lt;a href="https://search.google.com/test/rich-results" rel="noopener noreferrer"&gt;Rich Results Test&lt;/a&gt; and paste the product page URL.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"No items detected"&lt;/strong&gt; → markup is missing or was never present (causes 1, 2, 3).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Items detected, with errors&lt;/strong&gt; → markup exists but is broken (causes 2, 4).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything green&lt;/strong&gt; → markup is valid; the problem is indexability or eligibility (cause 5).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;In Search Console, go to Enhancements &amp;gt; "Products". The valid-items chart shows exactly &lt;em&gt;when&lt;/em&gt; the drop happened; it is almost always the day of a theme update or an app change.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why does a review app create duplicate Product schema?
&lt;/h2&gt;

&lt;p&gt;This is the most common cause. Review apps (Judge.me, Loox, Yotpo, and similar) inject their own &lt;code&gt;Product&lt;/code&gt; schema. Two failure modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The duplicate&lt;/strong&gt;: the theme already outputs a &lt;code&gt;Product&lt;/code&gt; block; the app adds a second one with a different price or rating aggregate. Google receives two contradictory descriptions of the same listing and either picks one arbitrarily or shows nothing. Classic symptom: stars appear some days and not others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript injection&lt;/strong&gt;: the app writes nothing into server-rendered HTML and instead injects schema after page load via JavaScript. Google processes it late and unreliably. If that script is blocked, updated, or fails silently, the structured data disappears with no visible sign on the storefront.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;: one source of truth. If your review app has a "structured data" or "SEO markup" toggle, disable either the app's output or the theme's native product schema block. Never leave both active at the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What breaks when a JSON-LD block becomes unparseable?
&lt;/h2&gt;

&lt;p&gt;Schema lives inside &lt;code&gt;&amp;lt;script type="application/ld+json"&amp;gt;&lt;/code&gt; blocks. One app concatenating two JSON documents, or a stray control character, makes the entire block invalid. Google silently ignores the whole payload. The store functions normally; shoppers see nothing wrong; the stars switch off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnose&lt;/strong&gt;: paste the URL into &lt;a href="https://validator.schema.org" rel="noopener noreferrer"&gt;validator.schema.org&lt;/a&gt; and look for parsing errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;: find which app or theme snippet generates the broken block (search for a recognizable string from the broken JSON in the theme code editor), then fix or disable it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does a theme update silently drop structured data?
&lt;/h2&gt;

&lt;p&gt;OS 2.0 themes (Dawn and derivatives) generate product schema natively. A theme redesign, a purchased theme switch, or a developer cleaning up &lt;code&gt;theme.liquid&lt;/code&gt; can strip the snippet without anyone noticing. It produces nothing visible, so nobody catches it until stars vanish weeks later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;: compare the current theme against a backup, or reintroduce a &lt;code&gt;structured-data.liquid&lt;/code&gt; snippet that pulls price, currency, availability, and image from the live product object:&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;",
"priceCurrency": "&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;iso_code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;",
"availability": "&lt;span class="cp"&gt;{%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&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;available&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;%}&lt;/span&gt;InStock&lt;span class="cp"&gt;{%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;%}&lt;/span&gt;OutOfStock&lt;span class="cp"&gt;{%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;endif&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;Never hardcode values. The schema must reflect the current product state on every render.&lt;/p&gt;

&lt;h2&gt;
  
  
  When does a price mismatch invalidate the schema?
&lt;/h2&gt;

&lt;p&gt;A sale applied mid-day, a currency-conversion app, a hardcoded price left in a theme edit: if the price in the structured data diverges from what shoppers see, Google loses trust in the listing. On Google Shopping, Merchant Center can disapprove the product for an "inconsistent value."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnose&lt;/strong&gt;: compare the price the Rich Results Test detects against the price displayed on the page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;: in the theme, pull the schema price from the exact same Liquid variable as the displayed price:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight liquid"&gt;&lt;code&gt;product.selected_or_first_available_variant.price
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Never duplicate the variable or hardcode a fallback.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does a noindex tag remove review stars entirely?
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;noindex&lt;/code&gt; meta tag applied by mistake removes the page from Google entirely. Stars disappear because there is no listing left to attach them to. Common sources: a &lt;code&gt;seo.hidden&lt;/code&gt; metafield inherited from a CSV import, an SEO app exclusion rule, or a stray theme edit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagnose&lt;/strong&gt;: run &lt;code&gt;site:yourstore.com/products/your-product&lt;/code&gt; in Google. Zero results means the page is deindexed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;: find and remove the &lt;code&gt;noindex&lt;/code&gt; directive. Check the product's metafields in the Shopify admin, your SEO app's exclusion rules, and any conditional logic around the robots meta tag in &lt;code&gt;theme.liquid&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;None of these failures produce a visible symptom on the storefront. They surface weeks later in a Search Console chart while click-through rate drifts to competitors who still have their stars.&lt;/p&gt;




&lt;p&gt;When you debug structured data issues for clients, which of these five causes do you run into most often?&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/review-stars-disappeared-google-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>Fix Shopify price mismatch killing clients' Merchant Center feed</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Wed, 22 Jul 2026 11:58:21 +0000</pubDate>
      <link>https://dev.to/max_buildogs/fix-shopify-price-mismatch-killing-clients-merchant-center-feed-4cn5</link>
      <guid>https://dev.to/max_buildogs/fix-shopify-price-mismatch-killing-clients-merchant-center-feed-4cn5</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;Google's "automatic item updates" cross-checks your feed price against two extra on-page signals: the &lt;code&gt;Product&lt;/code&gt; JSON-LD block and the &lt;code&gt;og:price:amount&lt;/code&gt; Open Graph tag.&lt;/li&gt;
&lt;li&gt;The most common Shopify culprits: discount apps that update the visible price but leave JSON-LD stale, and multi-currency apps that rewrite the displayed price without touching &lt;code&gt;cart.currency.iso_code&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Fix: route &lt;code&gt;offers.price&lt;/code&gt;, &lt;code&gt;priceCurrency&lt;/code&gt;, and the displayed price through the same Liquid variable — no hardcoded values, no divergent app outputs.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you build or maintain Shopify stores for clients running Google Shopping campaigns, this is the kind of silent failure that generates support tickets weeks after launch: a wall of Merchant Center disapprovals citing "inconsistent value" or "price mismatch," on products whose Shopify prices look perfectly correct.&lt;/p&gt;

&lt;p&gt;The cause isn't the feed. It's a three-way signal conflict on the live page — and Merchant Center is comparing all three.&lt;/p&gt;

&lt;p&gt;This article covers how Google's automatic item updates work, which Shopify-specific patterns cause the three signals to diverge, and the single Liquid pattern that eliminates the root cause.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does Google reject prices it can already read from the feed?
&lt;/h2&gt;

&lt;p&gt;Merchant Center doesn't only trust the price you submit in your product feed. Google's crawlers also perform &lt;strong&gt;automatic item updates&lt;/strong&gt; — they visit the live page, read the price from the structured data (&lt;code&gt;Product&lt;/code&gt; JSON-LD) and the Open Graph tags (&lt;code&gt;og:price:amount&lt;/code&gt;), and compare that against your feed. If any of those three sources disagree, Google either overrides the feed price silently or disapproves the listing outright for "inconsistent value."&lt;/p&gt;

&lt;p&gt;A store can have a perfectly correct Shopify product price and still get disapproved because one of the other two signals is out of sync.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where does the divergence come from in Shopify?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A half-applied sale.&lt;/strong&gt; A discount app updates the displayed price but never touches the JSON-LD block, which keeps rendering the pre-sale figure. Shoppers see the sale price; Google's structured-data reader sees the old one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A multi-currency or currency-conversion app.&lt;/strong&gt; This is the single biggest source of this bug. These apps rewrite the price a visitor &lt;em&gt;sees&lt;/em&gt; based on geolocation or a currency switcher, but frequently don't touch &lt;code&gt;cart.currency.iso_code&lt;/code&gt; or the JSON-LD price/currency fields — those keep reporting the shop's base currency. Google reads two different currencies or amounts for the same offer, triggering the &lt;code&gt;currency_mismatch&lt;/code&gt; / &lt;code&gt;price_mismatch&lt;/code&gt; pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A hardcoded price in a theme edit.&lt;/strong&gt; Someone typed a price directly into a Liquid template instead of pulling it from the product object. It looks fine at launch and quietly goes stale the next time the price changes in Shopify admin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Duplicate schema from an app.&lt;/strong&gt; If a review app or SEO app injects its own &lt;code&gt;Product&lt;/code&gt; block alongside the theme's native one, you end up with two different prices on the same page. Google picks one arbitrarily — if it picks the stale one, that's what gets compared to your feed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the fix look like in Liquid?
&lt;/h2&gt;

&lt;p&gt;Every price on the page — the visible price, the Open Graph tag, and the JSON-LD &lt;code&gt;offers.price&lt;/code&gt; — must come from the &lt;em&gt;same&lt;/em&gt; Liquid variable, live, at render time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight liquid"&gt;&lt;code&gt;"offers": {
  "@type": "Offer",
  "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;divided_by&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;",
  "priceCurrency": "&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;iso_code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;",
  "availability": "&lt;span class="cp"&gt;{%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&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;available&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;%}&lt;/span&gt;https://schema.org/InStock&lt;span class="cp"&gt;{%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;%}&lt;/span&gt;https://schema.org/OutOfStock&lt;span class="cp"&gt;{%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;endif&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;No hardcoded numbers, no values from a separate app config, no currency assumed from the shop's default market. If the client runs Shopify Markets or a currency-conversion app, &lt;code&gt;cart.currency.iso_code&lt;/code&gt; must reflect the &lt;em&gt;converted&lt;/em&gt; currency the shopper is actually seeing — verify the app writes to that value rather than only restyling numbers client-side.&lt;/p&gt;

&lt;p&gt;Availability deserves the same treatment: drive it from &lt;code&gt;product.available&lt;/code&gt;, not a hardcoded value, or it becomes its own disapproval vector.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Open the disapproved product on the storefront. View source or open DevTools → find the &lt;code&gt;&amp;lt;script type="application/ld+json"&amp;gt;&lt;/code&gt; block. Note the &lt;code&gt;price&lt;/code&gt; and &lt;code&gt;priceCurrency&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Search the same page source for &lt;code&gt;og:price:amount&lt;/code&gt; and &lt;code&gt;og:price:currency&lt;/code&gt; — these must match exactly.&lt;/li&gt;
&lt;li&gt;Compare both against what the shopper actually sees and against the price in the Merchant Center feed.&lt;/li&gt;
&lt;li&gt;If the client runs a multi-currency or localization app, repeat from a session simulating a different country/currency — the visible price often switches while schema and OG tags stay in the base currency.&lt;/li&gt;
&lt;li&gt;If any two of those four numbers disagree, you've found the mismatch. Trace which app or template owns the wrong value and route it through the Liquid variable above.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This failure produces zero visible symptoms for weeks — the storefront looks normal, sales keep coming in, and the only signal is a slow bleed of disapproved SKUs in a Merchant Center tab the merchant rarely checks.&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/review-stars-disappeared-google-shopify" rel="noopener noreferrer"&gt;Your review stars disappeared from Google? The 5 Shopify causes&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 your products from Google&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Have you run into a currency or discount app that handles the schema side correctly out of the box — or have you always had to patch it manually?&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-price-mismatch-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>Google is showing the wrong price for my product — Shopify</title>
      <dc:creator>Max</dc:creator>
      <pubDate>Tue, 21 Jul 2026 15:44:24 +0000</pubDate>
      <link>https://dev.to/max_buildogs/google-is-showing-the-wrong-price-for-my-product-shopify-kih</link>
      <guid>https://dev.to/max_buildogs/google-is-showing-the-wrong-price-for-my-product-shopify-kih</guid>
      <description>&lt;p&gt;You search your own product on Google, or check a Shopping result, and the price shown doesn't match what's actually on your Shopify store — sometimes an old price from before your last change, sometimes no price rich result at all. Nothing is wrong in Shopify admin: the product is active, the price field is correct, checkout charges the right amount. The mismatch only exists on Google's side.&lt;/p&gt;

&lt;p&gt;This is a real, recurring complaint in the Shopify Community — merchants report Google holding onto a stale price for sold-out or repriced products for days or weeks, with generic advice ("wait for Google to recrawl," "hit Fetch Now") that treats the symptom but not the underlying cause. In our scans, the underlying cause is almost always structural: the product's JSON-LD is missing the one field that tells Google how long a price stays valid.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to confirm it yourself in 5 minutes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Run a &lt;code&gt;site:&lt;/code&gt; search for the affected product URL and compare the price shown in the snippet against your live storefront price.&lt;/li&gt;
&lt;li&gt;Paste the same URL into &lt;a href="https://search.google.com/test/rich-results" rel="noopener noreferrer"&gt;Google's Rich Results Test&lt;/a&gt; and open the detected &lt;code&gt;Product&lt;/code&gt; markup — check the &lt;code&gt;offers&lt;/code&gt; object for a &lt;code&gt;priceValidUntil&lt;/code&gt; field and whether its date is in the future.&lt;/li&gt;
&lt;li&gt;In Google Search Console, open &lt;strong&gt;Enhancements → Merchant listings&lt;/strong&gt; and check for any warnings logged against the affected URL — this report tracks Product structured data issues separately from an actual price-mismatch error.&lt;/li&gt;
&lt;li&gt;If you also run Merchant Center for Shopping ads, check its Diagnostics tab for "mismatched value (price)" — that's a related but distinct feed-level issue, not the same as this Search-side symptom.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Causes, ranked by how often we actually see them
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. priceValidUntil is missing from the product's structured data entirely.&lt;/strong&gt; This is by far the most common cause: in our aggregated scans across 822 Shopify stores, &lt;strong&gt;514 (62%) have no &lt;code&gt;price_valid_until&lt;/code&gt; value at all.&lt;/strong&gt; That's not a merchant mistake — Shopify's own &lt;a href="https://shopify.dev/docs/api/liquid/filters/structured_data" rel="noopener noreferrer"&gt;&lt;code&gt;structured_data&lt;/code&gt; Liquid filter&lt;/a&gt;, the built-in filter Dawn and most modern themes use to generate &lt;code&gt;Product&lt;/code&gt; JSON-LD, outputs an &lt;code&gt;offers&lt;/code&gt; object with &lt;code&gt;availability&lt;/code&gt;, &lt;code&gt;price&lt;/code&gt;, &lt;code&gt;priceCurrency&lt;/code&gt;, and &lt;code&gt;url&lt;/code&gt; — no &lt;code&gt;priceValidUntil&lt;/code&gt;. It has to be added manually via a theme edit or an app. Without it, Google's Offer object has no explicit signal for how long the current price should be trusted, and per &lt;a href="https://developers.google.com/search/docs/appearance/structured-data/merchant-listing" rel="noopener noreferrer"&gt;Google's own merchant listing documentation&lt;/a&gt;, it's a recommended property of the Offer type — it isn't required for basic eligibility, but its absence removes one of the freshness signals Google uses to decide whether to trust and (re)display a price.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. priceValidUntil was set for a past promotion and never updated.&lt;/strong&gt; A sale or discount app writes a &lt;code&gt;priceValidUntil&lt;/code&gt; matching the promo's end date, the sale ends, and nothing goes back to refresh or remove that date. Google's documentation is explicit here: &lt;strong&gt;"Your listing may not display if the priceValidUntil property indicates a past date."&lt;/strong&gt; Google separately recommends pairing a start date with the end date specifically for sale pricing, so both ends of the window stay accurate. A leftover expired date is worse than having none.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Google simply hasn't recrawled since your last price change.&lt;/strong&gt; Indexing lag alone can produce a stale price for days to weeks — this is the explanation Shopify Community threads on this exact symptom usually land on, and it's genuine. But it compounds with cause 1: a page with no freshness signal in its Offer data gives Google less reason to treat the price as time-sensitive and prioritize a recheck.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Malformed price or currency fields.&lt;/strong&gt; Less common, but worth a quick check: &lt;code&gt;price&lt;/code&gt; must be a bare number string with no currency symbol, and &lt;code&gt;priceCurrency&lt;/code&gt; must be a valid ISO 4217 code. A stray symbol or the wrong code can make Google discard the price signal from your structured data entirely and fall back to whatever it last crawled from the visible page.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add priceValidUntil to your theme's Product JSON-LD&lt;/strong&gt; if it isn't there, computed live rather than hardcoded — most implementations set it to a rolling window (commonly 30 days to a year out) from the current date, recalculated on every render, so it never quietly goes stale on its own:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"priceValidUntil": "{{ 'now' | date: '%s' | plus: 2592000 | date: '%Y-%m-%d' }}"
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;(the example above adds 30 days; a sale-price app should instead set this to the actual promo end date, and clear or extend it the moment the sale is edited.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Audit any sale/discount app for a leftover expired date&lt;/strong&gt; — check its settings for a hardcoded promo end date that outlived the promo itself.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Request reprocessing&lt;/strong&gt; in Search Console's URL Inspection tool for the affected URL once the fix is live, rather than only waiting for the next organic crawl.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Validate &lt;code&gt;price&lt;/code&gt; and &lt;code&gt;priceCurrency&lt;/code&gt;&lt;/strong&gt; formatting with the Rich Results Test while you're in there.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this shows up as an error anywhere in Shopify admin — the product looks completely correct on your side. The only visible symptom is a shopper-facing price on Google that quietly drifted out of sync with your store, which is exactly the kind of gap that costs a sale before the shopper ever reaches checkout.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next in this cluster
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://storecanary.io/blog/merchant-center-price-mismatch-shopify" rel="noopener noreferrer"&gt;Products disapproved in Google Merchant Center: the on-page price mismatch trap&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 — the availability mismatch&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 Google Merchant Center just disapproved it&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://storecanary.io/blog/google-wrong-price-shopify" rel="noopener noreferrer"&gt;the StoreCanary blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>seo</category>
      <category>ecommerce</category>
      <category>googlesearch</category>
    </item>
  </channel>
</rss>
