Shopware SEO setup in 60 minutes: URL templates, canonicals & hreflang

Want a clean, crawlable Shopware 6 store in under an hour? Here’s a fast, do-this-now checklist with copy-paste templates and exact admin paths.


Prereqs (2–3 min)

  • Shopware 6 admin access
  • At least one Sales Channel with domains set
  • If you’re multilingual: languages already created + per-language domains or paths

0–10 min — Baseline checks

  1. Find the SEO URL templates
    Admin → Settings → Shop → SEO. You can scope templates globally or per sales channel and use Twig variables. After edits, you’ll rebuild the SEO index.
  2. Turn on safe redirects (optional but recommended)
    Same screen → Forwarding behavior → enable 301 redirect when URLs change.

10–30 min — URL templates that don’t bite

Product detail URLs

Start simple (short, stable) and optionally add structure later.

Option A — Just the name (shortest):

{{ product.translated.name|lower }}

Option B — Category trail + name (great for context):

{% for part in product.categories.sortByPosition().first.breadcrumb|slice(1) %}{{ part|lower }}/{% endfor %}{{ product.translated.name|lower }}

Option C — Name + SKU (helps uniqueness at scale):

{{ product.translated.name|lower }}-{{ product.productNumber|lower }}

Notes

  • Variables must be complete (e.g., product.translated.name, not just product.translated).
  • Use filters like |lower to normalize case.
  • If you rely on category paths, set a Main/SEO category per product to prevent duplicate paths.

Category URLs

Keep the breadcrumb for nested structure:

{% for part in category.seoBreadcrumb %}{{ part|lower }}/{% endfor %}

You can also use just {{ category.translated.name|lower }} for flat sites.

Landing pages (if you use them)

Default is the landing page’s own URL; you can also reference its fields:

{{ landingPage.url }}

(See available variables in the SEO settings doc.)

Apply & rebuild

After saving templates, rebuild the SEO index so friendly URLs generate:

UI: Settings → System → Caches & Indexes

CLI:

php bin/console dal:refresh:index

30–45 min — Canonical tags that de-duplicate correctly

Product variants → one canonical

For variant families, enable Use single canonical URL for all variants on the product, then pick the variant that should own the URL. This avoids multiple near-identical pages competing. Admin → Products → (open product) → SEO.

Per-sales-channel canonical path & main category

Still on the product’s SEO tab, you can set a canonical path per sales channel and choose its Main category so your product URL always builds off the same category trail.

Global redirects on URL change

If you changed templates, that earlier Forwarding behavior ensures 301s from old to new URLs (no link equity loss).

Advanced: If you need all variants to canonicalize to the parent (even when each has its own detail page), you can implement a “parent canonical” concept or use a plugin built for variant canonicalization.

45–58 min — Hreflang that Google actually understands

Turn on hreflang (core)

Per Sales Channel: Admin → Settings → Sales channel → Hreflang

  • Activate Hreflang.
  • Select the default domain used as fallback (x-default behavior).
  • Make sure each language/country variant has a unique domain or path (e.g., example.com/en-us, example.de/de-de).

Follow Google’s hreflang rules

  • Every localized version must reference all siblings (including itself).
  • Use language-region codes (en-US, de-DE) when regional content differs.
  • Provide an x-default for the global/chooser page.

Quick QA

  • View-source on a product page and confirm a set of <link rel="alternate" hreflang="…"> entries for every language, plus x-default.
  • Run Chrome Lighthouse → SEO audit; it flags missing/invalid hreflang.

58–60 min — Final QA checklist

  • Change a product name; confirm a 301 from old → new URL.
  • For a variant family, confirm only the chosen variant is canonical.
  • On a localized product, confirm the full set of hreflang alternates exists and match real pages.
  • Rebuilt SEO index & cleared caches after template changes.

Copy-paste recipe box

Suggested starting templates

Product (flat):

{{ product.translated.name|lower }}

Product (category trail + name):

{% for part in product.categories.sortByPosition().first.breadcrumb|slice(1) %}{{ part|lower }}/{% endfor %}{{ product.translated.name|lower }}

Product (name + SKU):

{{ product.translated.name|lower }}-{{ product.productNumber|lower }}

Category:

{% for part in category.seoBreadcrumb %}{{ part|lower }}/{% endfor %}

Tip: Use Main/SEO category on each product to stabilize URLs if products live in multiple categories.

Common pitfalls (and quick fixes)

  • Endless URL churn (dates, prices, stock in URLs). Avoid dynamic fields in templates; these cause ranking instability.
  • Duplicate product URLs via multiple categories. Set a Main/SEO category per sales channel.
  • Variant duplicates. Use the single canonical for all variants toggle.
  • Partial hreflang sets. Each locale must declare all alternates + itself (and x-default if used).

If you want, I can tailor the templates to your exact category tree and languages, or rewrite this into a ready-to-publish post for your agency blog.

Leave a Comment

Your email address will not be published. Required fields are marked *