We had ~8,436 broken cross-store hreflang alternates emitting across the four Brainzyme® Shopify stores — 95% of the damage concentrated on UK blog articles. This dashboard is the permanent record: what broke, why it broke, the fix we shipped, how to verify it, and what to do when new products or pages get added.
Hreflang tags tell Google which page serves which country / language. Our four stores (UK, US, DE, FR) are meant to cross-link to each other on shared pages (homepage, products, a handful of shared info pages) and self-link on store-unique pages (blog articles, collections, unlisted pages).
The old theme.liquid block had a single blind fallback branch:
{% else %}
<link rel="alternate" hreflang="en-GB" href="https://www.brainzyme.com{{ request.path }}">
<link rel="alternate" hreflang="en-US" href="https://www.brainzyme.us{{ request.path }}">
<link rel="alternate" hreflang="de-DE" href="https://www.brainzyme.de{{ request.path }}">
<link rel="alternate" hreflang="fr-FR" href="https://brainzyme.fr{{ request.path }}">
{% endif %}
That tells Google "the same URL exists on every store — go crawl it." For product pages and the homepage, that was true.
For UK's 2,622 /blogs/focus-learning-center/* articles, those URLs don't exist on DE, FR or US. Google crawled them,
got 404s, and logged "missing return tag" errors against every one.
The audit was originally scoped to fix two assumed defects (UK ELITE slug, FR www-missing). On inspection, both were non-defects — UK's ELITE alias 301s to the canonical handle before Liquid runs, and FR's canonical domain is legitimately non-www (inverted from UK/US/DE). The real problem — the blog-article fallback emitting ~8,000 bad alternates — was out of scope on the original brief.
Replace the blind {% else %} fallback with a tiered structure that only emits cross-store alternates on pages
that actually exist on all four stores. Everything else falls through to self + x-default.
The old fallback assumed path equivalence across stores. The new structure inverts the default: cross-link only when we know the other stores have the matching page, never by assumption.
FR's canonical domain is https://brainzyme.fr (non-www). www.brainzyme.fr
301s to the non-www host. Every fr-FR alternate in all four snippets points to brainzyme.fr (no www).
Switching FR to www would create a redirect-chain defect on every international signal.
All four stores have the v2 snippet live in layout/theme.liquid section 43 as of 14 April 2026.
Verified via cache-bypass HTML fetch (browser DOM cache is unreliable — always verify against raw HTML).
| Page type | UK | US | DE | FR | Expected |
|---|---|---|---|---|---|
Homepage / | 5 | 5 | 5 | 5 | 5 tags |
| ELITE product page | 5 | 5 | 5 | 5 | 5 tags |
| PRO product page | 5 | 5 | 5 | 5 | 5 tags |
| ORIGINAL product page | 5 | 5 | 5 | 5 | 5 tags |
| COMBO product page | 5 | 5 | 5 | 5 | 5 tags |
/pages/ingredients | 5 | 5 | 5 | 5 | 5 tags |
/collections/all | 2 | 2 | 2 | 2 | 2 tags (self-only) |
| Blog article | 2 | 2 | 2 | 2 | 2 tags (self-only) |
Browser DOM (view-source, document.querySelectorAll) can show stale edge-cached markup even after
a hard refresh. Always bypass the cache via a Chrome console fetch:
fetch(location.href + '?nc=' + Date.now(), {cache: 'reload'})
.then(r => r.text())
.then(html => html.match(/<link[^>]*rel=["']alternate["'][^>]*hreflang=[^>]*>/gi));
Three playbooks. Pick the one that matches what you're adding. Every step is copy-paste ready. If you finish and verification fails, scroll to the troubleshooting table at the bottom of this section.
/pages/xyz live on all 4 stores (e.g. new testimonials page)canonical_url, template, product.handle, page.handle in <head> (Be Yours 8.1.1 does — verified)layout/theme.liquid under the comment <!-- 43) Adding the theme diversion code for SEO purposes -->F:/Claude Root/config/hreflang_snippets.json, the paste-ready Liquid at F:/Claude Root/hreflang-audit/corrected-snippets.mdYou've created (or are about to create) a product that lives on UK, US, DE, and FR. The hreflang snippet won't know about it until you add it to the product map. Example product below: “Focus Plus”.
handle from each storeShopify stores the handle per-store. UK/US/DE usually use short English handles; FR uses long localised slugs. Do not guess them. Open each store's products JSON feed in your browser:
https://www.brainzyme.com/products.json?limit=250
https://www.brainzyme.us/products.json?limit=250
https://www.brainzyme.de/products.json?limit=250
https://brainzyme.fr/products.json?limit=250
Find your new product by title (Ctrl+F). Copy the exact "handle" value. You should end up with 4 handles (one per store).
config/hreflang_snippets.jsonOpen F:/Claude Root/config/hreflang_snippets.json and add a new entry under product_map. Use a short ALL_CAPS code as the key:
"FOCUS_PLUS": {
"handles": [
"brainzyme-focus-plus",
"brainzyme-focus-plus",
"brainzyme-focus-plus",
"brainzyme-focus-plus-long-french-slug-here"
],
"urls": {
"en-GB": "https://www.brainzyme.com/products/brainzyme-focus-plus",
"en-US": "https://www.brainzyme.us/products/brainzyme-focus-plus",
"de-DE": "https://www.brainzyme.de/products/brainzyme-focus-plus",
"fr-FR": "https://brainzyme.fr/products/brainzyme-focus-plus-long-french-slug-here",
"x-default":"https://www.brainzyme.com/products/brainzyme-focus-plus"
}
}
x-default always points to the UK URL. fr-FR is always non-www (brainzyme.fr, no leading www).
{% case product.handle %} block in all 4 store snippetsOpen F:/Claude Root/hreflang-audit/corrected-snippets.md. You'll see 4 store sections. For each of UK, US, DE, FR, add a new {% when ... %} clause inside the elsif template contains 'product' branch, above the {% else %} fallback.
The when clause lists every store's handle for this product (so the same clause catches the product regardless of which store Liquid is running on):
{%- when 'brainzyme-focus-plus', 'brainzyme-focus-plus-long-french-slug-here' -%}
<link rel="alternate" hreflang="en-GB" href="https://www.brainzyme.com/products/brainzyme-focus-plus" />
<link rel="alternate" hreflang="en-US" href="https://www.brainzyme.us/products/brainzyme-focus-plus" />
<link rel="alternate" hreflang="de-DE" href="https://www.brainzyme.de/products/brainzyme-focus-plus" />
<link rel="alternate" hreflang="fr-FR" href="https://brainzyme.fr/products/brainzyme-focus-plus-long-french-slug-here" />
<link rel="alternate" hreflang="x-default" href="https://www.brainzyme.com/products/brainzyme-focus-plus" />
Paste this clause into all 4 snippets (UK, US, DE, FR). The URLs inside are the same across all 4 snippets — only the self-ref logic differs per snippet.
For each of UK, US, DE, FR:
layout/theme.liquid<!-- 43) Adding the theme diversion code for SEO purposes -->{%- if template == 'index' -%} through the matching {%- endif -%}) with the updated store-specific snippetOpen the new product page on each store. Open Chrome DevTools → Console. Paste:
fetch(location.href + '?nc=' + Date.now(), {cache: 'reload'})
.then(r => r.text())
.then(html => {
const tags = html.match(/<link[^>]*rel=["']alternate["'][^>]*hreflang=[^>]*>/gi) || [];
console.log('Count:', tags.length);
tags.forEach(t => console.log(t));
});
Expected: 5 tags. One each for en-GB, en-US, de-DE, fr-FR, x-default. If you get 2 tags, the when clause didn't catch — most likely a handle typo. See troubleshooting.
For each of the 4 GSC properties, Sitemaps → Submit /sitemap.xml. Google should re-index the new product with correct hreflang within 24–72 hours.
/pages/xyz on all 4 stores
A page that exists at the same URL path on all four stores (e.g. /pages/testimonials launched on UK, US, DE, FR simultaneously).
These go into the mirrored-pages allowlist. Rule: slug must be identical on all 4 stores — if slugs differ per locale, this is actually a Playbook A case (product-style case block needed, not a simple allowlist entry).
mirrored_pages_allowlistOpen F:/Claude Root/config/hreflang_snippets.json. Add the new handle (without /pages/ prefix) to the mirrored_pages_allowlist array:
"mirrored_pages_allowlist": [
"brain-issues-quiz",
...existing entries...,
"testimonials"
]
Open F:/Claude Root/hreflang-audit/corrected-snippets.md. Each snippet has a {% elsif template contains 'page' %} branch with a path check. Add the new path:
{%- elsif template contains 'page' and page.handle == 'brain-issues-quiz'
or page.handle == 'ingredients'
or page.handle == 'testimonials' {%- comment -%}NEW{%- endcomment -%}
...etc -%}
(Exact Liquid syntax is in corrected-snippets.md — don't reinvent it, just extend the existing condition.)
Same as Playbook A step 4. Replace the section-43 block on each store's layout/theme.liquid.
Expect 5 tags on /pages/testimonials on each of the 4 stores. If you get 2 tags, the allowlist didn't catch — check the handle spelling exactly matches what Shopify stores as page.handle (lowercase, hyphenated).
You've changed a product's URL on (for example) the FR store. Shopify will 301 the old URL to the new one, but the hreflang snippet's when clause still lists the old handle. Update it — keep the old handle as an alias for redirect safety.
Admin → Products → [product] → scroll to the URL field at the bottom of the page. Copy the exact handle.
Open F:/Claude Root/config/hreflang_snippets.json. In the affected product's entry:
handles array (keep the old one as a safety alias)urls.[locale] to the new full URLOpen corrected-snippets.md. In the affected store's snippet, find the {% when %} clause for this product. Add the new handle to the list. Update the locale's <link> href inside the clause.
Paste the updated snippet into the affected store only. Verify via cache-bypass fetch on both the old URL (should 301 and still show 5 tags) and the new URL (should show 5 tags directly).
{% when %} didn't catch. 99% of the time: handle typo, or you missed a store's handle. Re-check the exact product.handle value from /products.json. Paste it verbatim.page.handle (lowercase, hyphenated, no /pages/ prefix). Check the path spelling in the elsif chain.<link> href. Check the config/hreflang_snippets.json urls map first (source of truth), then the Liquid.https://www.brainzyme.fr/... (with www)layout/theme.liquid. Or paste the v2 snippet from corrected-snippets.md to reset to the last-known-good state.
Any future Claude session can run the full SOP end-to-end. The relevant memory files
(reference_hreflang_snippets.md, feedback_shopify_product_handle_per_store.md) are indexed in Pinecone
and surface automatically on any hreflang-related query. Say "add Focus Plus to hreflang" and it'll walk through all 4 steps.
Post-deploy verification caught a live bug: FR ELITE and FR PRO pages emitted only 2 tags instead of 5. Root cause was an invisible trap in Shopify's multi-store behaviour.
product.handle on each Shopify store returns that store's actual handle, not a canonical cross-store identifier.
UK/US/DE use short handles (brainzyme-elite, brainzyme-elite-3-in-1-formula, brainzyme-professional).
FR uses long localised slugs like:
brainzyme-elite-stimulation-agreable-concentration-memoire-
un-complement-nutritionnel-d-optimisation-de-performance-
cognitive-nootropique-naturel
The v1 snippet's {% case product.handle %} only listed UK/US handles. FR products fell through every when
branch, hit the self-only fallback, and emitted 2 tags.
v2 adds all three FR long handles to ELITE / PRO / ORIGINAL when clauses (COMBO already had it). Each product's full handle set:
| Product | UK | US | DE | FR long handle |
|---|---|---|---|---|
| ELITE | brainzyme-elite | brainzyme-elite-3-in-1-formula | brainzyme-elite | brainzyme-elite-stimulation-agreable-... |
| PRO | brainzyme-professional-stronger-formula | brainzyme-professional-stronger-formula | brainzyme-professional | brainzyme-professionnel-formule-renforcee-... |
| ORIGINAL | brainzyme-original-milder-formula | brainzyme-original-milder-formula | brainzyme-original | brainzyme-original-formule-douce-... |
| COMBO | brainzyme-combo-set-1x-elite-... | brainzyme-combo-set-1x-elite-... | brainzyme-combo | nouveau-brainzyme-pack-combo-x1-elite-... |
In any multi-store Liquid that branches on product.handle, every store's actual handle must be enumerated.
Don't assume canonical aliasing. Before shipping, query each store's /products.json?limit=250 and inspect the
real handle values.
Memory file: feedback_shopify_product_handle_per_store.md.
Natural follow-up question: if FR's long handles are the trap, why not just make all four stores use identical slugs? The answer: technically yes, practically no.
{% case product.handle %} block entirely.canonical_url domain-swap: extract the path once, emit all 4 cross-links.brainzyme-elite-stimulation-agreable-concentration-memoire-... matches French search queries. Collapsing to
a short English handle would lose French keyword relevance in the URL.
brainzyme-professional; UK/US: brainzyme-professional-stronger-formula).
when clause on 4 stores when launching a new product is a ~5–10 minute job. You launch maybe 1–2 products a year.
The current snippet is the right trade-off. Self-healing for the 99% case (new blog/collection content) and a small, predictable code change for the rare product or mirrored-page addition.
Everything needed to understand, maintain, or re-deploy the fix is banked. Files below are absolute paths on the working machine.
Assignee: Calum. Implementer: Lu. Parent task has description + audit trail; subtasks carry the inline paste-ready Liquid per store.
All 5 tasks resolved. FR subtask carries the v2 patch notice post — original v1 code is superseded.