# Mega Analyzer goes Shopify-aware — six new detectors and one suppressor

Auditing a Shopify storefront in a generic SEO checker drowns the merchant in false positives: visually-hidden H1s, http schema.org context, paired og:image http+secure_url, /policies/* paths the checker misses. The Mega Analyzer now fingerprints Shopify and applies a Shopify-aware rule pack — six new Shopify-only detectors and one suppressor that fixes a long-standing /policies/* false negative. Patterns generalized from a real audit; brand details anonymized.

Author: J.A. Watte
Published: May 19, 2026
Source: https://jwatte.com/blog/blog-tool-mega-analyzer-shopify-aware/

---

I audited a Shopify storefront last week and the report drowned in false positives. The visually-hidden H1 next to the logo (Shopify's standard "logo-as-H1" pattern) tripped duplicate-H1 warnings. The pair of `og:image` tags — one HTTP, one HTTPS — looked like mixed content to the analyzer. The Trust dim said the store had no privacy or terms pages, even though both lived at `/policies/privacy-policy` and `/policies/terms-of-service`, where Shopify hosts them by default.

Meanwhile, the real defects went undetected. Every Product schema on the store said `"brand":{"name":"examplehats.shop"}` — the merchant's Vendor field was unfilled, so Shopify was using the bare domain. The first product in the products sitemap was out-of-stock at price `$0.00`, sending every Google referral to an empty landing page. Multiple product `description` and `category` fields emitted as empty strings (`""`) rather than being omitted, which Google Merchant Center quietly rejects.

So the Mega Analyzer got a Shopify-aware rule pack. Six new detectors fire only when the analyzer fingerprints a Shopify storefront, and one suppressor fixes a long-standing `/policies/*` false negative across the Trust dim.

## How the platform fingerprint works

A site is classified as Shopify if any of the following hold:

```js
const fingerprints = [
  /cdn\.shopify\.com/i,
  /shopify\.com\/s\//i,
  /Shopify\.theme/i,
  /data-shopify-/i,
  /<header-component\b/i,
  /<predictive-search\b/i,
  /\bShopify\.shop\b/i
];
isShopify = fingerprints.some(re => re.test(html))
  || /shopify/i.test(headers['powered-by'] || '')
  || /shopify/i.test(headers['x-shopify-stage'] || '');
```

The fingerprint is intentionally permissive — any one signal is enough. False positives here are cheap (the rule pack on a non-Shopify site contributes a couple of no-op findings); false negatives are expensive (the merchant doesn't see the Shopify-specific defects). Most of the false-positive risk is around Liquid-templated themes hosted elsewhere, but those still get flagged by the platform-default behaviors and the merchant-facing remediation paths (Shopify Admin links) still mostly work as a Liquid theme cheat-sheet.

The dimension itself is pushed conditionally into the analyzer's `dims[]` array — the same pattern `dimFieldPerformance` uses for CrUX data. On non-Shopify sites the dim isn't added, so the `overall` score average doesn't shift.

## The six detectors

### 1. Vendor field = bare domain

The most common Shopify merchant misconfiguration. When a merchant doesn't fill the **Vendor** field on a product, Shopify falls back to using the store's domain. The resulting Product schema looks like this:

```json
"brand": { "@type": "Brand", "name": "examplehats.shop" }
```

That's a Google Shopping eligibility killer. The Knowledge Graph reconciler treats the bare domain as a different entity from the brand the customer is searching for. The fix lives in Shopify Admin → Products → bulk-edit → Vendor.

The detector test:

```js
const brandVal = brandValMatch[1];
const looksLikeDomain = /^[a-z0-9-]+(\.[a-z0-9-]+){1,}$/i.test(brandVal);
const matchesHost    = host && brandVal.toLowerCase() === host.toLowerCase();
if (looksLikeDomain || matchesHost) add('fail', ...);
```

Either condition fires the finding. Pure-domain values catch the bare-vendor case; `matchesHost` catches mixed-case variants (e.g. `ExampleHats.Shop`) that wouldn't match the strict-lowercase domain regex.

### 2. Out-of-stock product at $0 in the sitemap

The audit that motivated this detector found a product page returning `availability: OutOfStock` with `price: "0.00"`, and that product was the first entry in `sitemap_products_1.xml`. So Google's crawler — which prioritises first-listed products in a sitemap during initial discovery — was sending users to an empty product page. The detector:

```js
const isOos  = /availability"\s*:\s*"[^"]*OutOfStock"/i.test(offerStr);
const isZero = /"price"\s*:\s*"0(?:\.0+)?"/i.test(offerStr);
if (isOos && isZero) add('fail', ...);
```

Fix: either unpublish the product (Shopify Admin → set Status to Draft) or set up a 301 redirect to the parent collection (Admin → Online Store → Navigation → URL Redirects).

### 3. Empty `description` or `category` strings in Product schema

Shopify emits empty strings rather than omitting a field. Most validators (including schema.org's) accept `""`. Google Merchant Center does not. Detector:

```js
const emptyDescription = /"description"\s*:\s*""/.test(productStr);
const emptyCategory    = /"category"\s*:\s*""/.test(productStr);
```

Fix path is in the Shopify admin, not the theme — the merchant has to actually fill the Description and Google Product Category fields.

### 4. Theme-color meta present but empty

```html
<meta name="theme-color" content="">
```

Shopify's Studio theme emits the meta even when the merchant hasn't picked a color in Theme settings. Browsers default the mobile chrome to white, breaking branded Add-to-Home-Screen and undermining the visual brand. The fix is one click in Online Store → Themes → Customize → Theme settings → Colors.

### 5. Missing `<link rel="icon">`

Shopify only emits the favicon link when the merchant has uploaded a favicon in theme settings. If it isn't uploaded, the browser tab shows the default file-icon and `/favicon.ico` returns 404. The detector flags either the missing link OR the 404 on the file; the remediation is the same: upload a square PNG at Online Store → Themes → Customize → Theme settings → Favicon.

### 6. Duplicate H1, with Shopify "logo-as-H1" awareness

Shopify themes (including Dawn, Sense, Studio, and most third-party themes) put the brand name inside an `<h1 class="visually-hidden">` next to the logo, so screen readers announce the brand without a visible H1 competing with the logo image. That pattern is fine. The defect is the *second* H1 — usually emitted by a re-used section like a newsletter banner or hero promo — which then duplicates on every page that includes that section.

The detector counts H1s. If one is visually-hidden and the other(s) are not, it explicitly tells the merchant: keep the hidden one, demote the visible one. If both H1s are visible, it flags the page generically (since neither is special).

The same logic landed in `wcag-quick-checks.js`'s `h1-present` rule so the WCAG Quick Check and the Mega Analyzer agree on which H1 is the defect.

## The one suppressor

`dimTrust` was checking for `href="/privacy"`, `href="/terms"`, and `href="/cookie"` to count policy links. Shopify stores never use those paths — every policy lives under `/policies/*`. The check now also accepts `/policies/privacy-policy`, `/policies/terms-of-service`, and `/policies/cookie-policy`:

```js
const hasPrivacy = /href=["'](?:[^"']*\/)?(?:privacy(?:-policy)?|policies\/privacy(?:-policy)?)\b/i.test(html);
const hasTerms   = /href=["'](?:[^"']*\/)?(?:terms(?:-of-(?:service|use))?|policies\/terms(?:-of-(?:service|use))?)\b/i.test(html);
const hasCookies = /href=["'](?:[^"']*\/)?(?:cookies?(?:-policy)?|policies\/cookies?(?:-policy)?)\b/i.test(html);
```

The change is strictly more tolerant: any site that previously passed the check still passes. Shopify sites with proper policies in `/policies/*` stop false-positiving.

## What the analyzer does NOT flag on Shopify

Several patterns that look like defects on a generic SEO checker are Shopify defaults and shouldn't be flagged at all. The analyzer doesn't currently emit warnings for any of these:

- `og:image` HTTP paired with `og:image:secure_url` HTTPS (OG-spec compliant Shopify default — both Facebook and LinkedIn honor `secure_url`).
- `@context: http://schema.org` (Shopify emits HTTP; Google + schema.org both accept).
- HSTS `max-age` around 91 days (Shopify infrastructure default, not preload-eligible by design).
- `_shopify_y / _shopify_s / _shopify_essential` cookies (cart/session necessary).
- `<meta http-equiv="X-UA-Compatible" content="IE=edge">` (legacy IE compat; Shopify still emits).
- Relative `@id` on Product schema (`/products/foo#product`) — Shopify's reconciler handles relative IDs fine.
- Multiple JSON-LD blocks where the `@type` scopes differ (Organization + Product is NOT page-type dilution; both blocks have distinct entity scope).

If a future audit shows one of these false-positiving, the suppressor list is the right place to land the fix.

## Back-test results

Before deploying, the new code was back-tested against three live sites:

| Fixture | Platform | dimShopify fires? | Note |
|---|---|---|---|
| Shopify storefront (Studio theme v3.5.1) | Shopify | yes | All six detectors fire on the appropriate page types; Vendor=domain + OOS+$0 + empty description all caught |
| jwatte.com | Eleventy / Netlify | no | `detectShopifyContext.isShopify === false`; dim returns `skip:true` and is not pushed to `dims[]`; existing scores unchanged |
| Second non-Shopify Eleventy site (control) | Eleventy / Netlify | no | Same — no new dim added |

The non-Shopify sites' `overall` score, plus every individual dim score except `dimTrust` (which can only improve, never regress, thanks to the more-tolerant policy regex), is byte-for-byte identical to v2 output.

## How to run it

The Mega Analyzer is at [/tools/mega-analyzer/](/tools/mega-analyzer/). Paste any URL — Shopify-aware checks are automatic. If the analyzer fingerprints Shopify, you get a new "Shopify storefront checks" dimension card alongside the existing eight (or nine, with CrUX). Each finding includes a 📖 Learn link back to this post.

The companion fix-generator pills for Shopify findings deep-link into [/tools/schema-validator/](/tools/schema-validator/) (for the brand=domain + empty-string bugs), [/tools/sitemap-audit/](/tools/sitemap-audit/) (for the OOS+$0 trap), and [/tools/headings-outline/](/tools/headings-outline/) (for the duplicate-H1 finding). The Audit pills are wired the same way.

## Related reading

- [The Mega Analyzer launch — one URL, every audit in one pass](/blog/blog-tool-mega-analyzer/)
- [Date-only YYYY-MM-DD in JSON-LD trips Search Console — ISO 8601 fix](/blog/blog-tool-jsonld-datetime-iso8601/)
- [ProfilePage cross-page refs need typed `{@type, name}` references](/blog/blog-tool-profilepage-mainentity-typed-reference/)
- [JSON-LD `@id` trailing-slash consistency — why `#org` and `/#org` are different entities](/blog/blog-eeat-schema-structured-data/)
- [The Agent Protocol Stack — UCP, MCP, ACP, NLWeb, WebMCP](/blog/blog-agent-protocol-stack/)

If you run a Shopify storefront, the audit-then-fix loop is: run the Mega Analyzer, work through the Shopify-storefront-checks card top to bottom, and most fixes will land in Shopify Admin — no theme code required. The harder ones (expanded Organization schema, BreadcrumbList on products, WebSite + SearchAction on the homepage) live in the theme's `theme.liquid` and need a developer; the schema snippets are in [The $97 Launch](https://www.amazon.com/dp/B0F1BL5Y8Y) chapter 6 if you want the copy-paste-ready versions.

*This post is informational, not legal or e-commerce advice. Mentions of Shopify, Google Merchant Center, and other third parties are nominative fair use. No affiliation is implied.*


---

Canonical HTML: https://jwatte.com/blog/blog-tool-mega-analyzer-shopify-aware/
RSS: https://jwatte.com/feed.xml
JSON Feed: https://jwatte.com/feed.json
Hero image: https://jwatte.com/images/blog-tool-mega-analyzer-shopify-aware.webp
