Skip to main content

Hreflang overview

Multi-store / multi-language stores need hreflang tags to tell Google which version of a page belongs to which audience. The SEO Suite emits them automatically from your store-view configuration — and validates the result.

What it emits

For each storefront page on a multi-store install:

<link rel="alternate" hreflang="en-gb" href="https://uk.example.com/product.html">
<link rel="alternate" hreflang="en-us" href="https://us.example.com/product.html">
<link rel="alternate" hreflang="de-de" href="https://de.example.com/product.html">
<link rel="alternate" hreflang="x-default" href="https://uk.example.com/product.html">

The x-default is configurable to be either:

  • The "default store" of the website, or
  • A specific store ID you choose

Configuration

Stores → Configuration → SEO Suite → Hreflang Configuration (per-website scope)

FieldDefault
Enable hreflangNo
Include region variationsNo (just en instead of en-gb)
Enable x-defaultNo
X-default store(default store of the website)

Region variations (en-gb vs en) are recommended when you operate stores in the same language but different markets (en-gb vs en-us vs en-au). Without them, Google has no signal to differentiate.

How URLs are matched across stores

The default behaviour is "same URL on every store" — the current request path is used unchanged. This works for catalog pages where Magento generates the same URL key per store (or where you've configured byte8_seosuite/hreflang to match by SKU/url_key).

For CMS pages, manual pairing via the hreflang_identifier field on the CMS page edit form: pages sharing the same hreflang_identifier are linked together regardless of their identifier.

For product/category pages with diverging URL paths across stores, use the URL Relationship manager (Marketing → SEO Suite → Manage URL Relationship) to set canonical/alternate mappings explicitly.

Locale code rules

Magento's store locale (e.g. en_GB) is normalised:

  • With "Include region variations" = Yes → en-gb (lowercase, _-)
  • With "Include region variations" = No → en (first two chars only)

A store with locale de_AT becomes de-at or de depending on the toggle.

What about CMS pages?

CMS pages have a hreflang_identifier text field on the edit form. Pages across stores sharing the same identifier get linked.

Workflow:

  1. Create a "Returns Policy" CMS page in the UK store with identifier = returns-policy and hreflang_identifier = returns-policy-global
  2. Create a German equivalent in the DE store with identifier = ruckgaberecht and hreflang_identifier = returns-policy-global
  3. Visit the UK page → see <link rel="alternate" hreflang="de-de" href=".../ruckgaberecht"> and vice versa

If you share the same identifier across stores (no hreflang_identifier), the suite still picks them up — hreflang_identifier is only needed when the URL keys differ.

Validation

Two paths to verify your setup is correct:

  • CLIbin/magento seosuite:hreflang:validate — full report, JSON output for CI
  • Admin gridMarketing → SEO Suite → Hreflang Health — same data persisted, plus per-row Auto-Match action

See Validator for the validator deep-dive and Auto-matcher for the auto-pairing tool.

Common issues

  • Two stores emit the same hreflang locale → Google picks one arbitrarily. Enable "Include region variations" so they're distinguishable.
  • x-default on a localised page → only the home page should typically be flagged x-default. Set x_default_store to your default-locale store and only enable x-default on home-page-equivalent CMS pages if needed.
  • Self-referencing tag missing → Google's spec requires every page to also tag itself as a hreflang variant. The suite emits this automatically — if it's missing, your locale config is invalid (run the validator).

Headless / GraphQL

Hreflang tags are NOT yet exposed via GraphQL. Roadmapped for v2.9. For now, headless storefronts that need them must either:

  • Render the storefront HTML server-side and scrape the <head>, or
  • Re-implement the locale loop client-side using the seo.canonical from each store's GraphQL query

Next

  • Validator — what gets checked, how to read the output
  • Auto-matcher — bulk-discover cross-store product/category pairs