Canonical URLs and Duplicate Content in Next.js, PL/EN/DE, hreflang, Trailing Slash (2026)

canonical5 min readJuly 25, 2026

Author: DevStudio.it

TL;DR

A canonical tells Google “this is the preferred URL for this content.” On a multilingual corporate site (PL/EN/DE) each locale gets its own self-canonical, you do not point the Polish page at the English URL as canonical. hreflang means “this is a translation”; canonical means “this is the original among technical duplicates.” In the Next.js App Router you set alternates.canonical + alternates.languages, add 301s for trailing slash/www, and strip UTMs from the indexed URL. Below: duplicate map, Metadata API code, hreflang↔canonical conflicts, and a GSC checklist.

Who is this for

  • Owners of pl/en/de sites with duplicate warnings in GSC
  • Next.js developers after a migration or URL restructure
  • SEOs fighting www vs non-www, slash vs no-slash, UTMs in the index
  • Teams whose preview/ISR creates extra URLs
  • Companies with the “same offer” under three paths

Keyword

canonical url nextjs, duplicate content pl en de, hreflang vs canonical, trailing slash seo, alternates.canonical app router, preferred url 2026

Canonical vs hreflang, different jobs

Mechanism Question it answers Example
Canonical Which URL is primary for this same content/variant? /en/services canonical vs ...?utm_source=linkedin
hreflang Which URLs are translations of the same page? /pl/oferta/en/services/de/leistungen
301 redirect Which host/path is the only HTTP destination? www → apex, /services//services

Classic mistake: setting the Polish page’s canonical to English “because EN is the main market.” Google may ignore hreflang and consolidate onto EN, PL disappears from local results.

Correct:

  • PL → canonical https://example.com/pl/oferta
  • EN → canonical https://example.com/en/services
  • DE → canonical https://example.com/de/leistungen
  • All three → mutual hreflang (+ x-default if you use one)

Common duplicate sources on service sites

  1. www vs non-www and http vs https
  2. Trailing slash (/en/services vs /en/services/)
  3. Parameters (UTM, fbclid, sorting)
  4. Locale fallback (middleware serves EN on / and /en)
  5. Old URLs after redesign without 301s
  6. Blog pagination / tags with thin content
  7. Preview / draft indexed by accident
  8. Same CMS content under two slugs

Case studies in Branchly need one slug per locale and a stable canonical in metadata, do not invent a second “campaign URL.”

Next.js App Router, Metadata API

// app/[locale]/services/page.tsx
import type { Metadata } from 'next';

const BASE = 'https://devstudioit.com';

const paths = {
  pl: '/pl/oferta',
  en: '/en/services',
  de: '/de/leistungen',
} as const;

export async function generateMetadata({
  params,
}: {
  params: Promise<{ locale: keyof typeof paths }>;
}): Promise<Metadata> {
  const { locale } = await params;
  const path = paths[locale];

  return {
    title: '…',
    description: '…',
    alternates: {
      canonical: `${BASE}${path}`,
      languages: {
        pl: `${BASE}${paths.pl}`,
        en: `${BASE}${paths.en}`,
        de: `${BASE}${paths.de}`,
        'x-default': `${BASE}${paths.en}`,
      },
    },
  };
}

x-default is the fallback for unrecognized languages (often EN or a language picker), not “the most important market.”

Blog, shared slug, locale in path

canonical: https://devstudioit.com/en/blog/{slug}
hreflang pl/en/de → /{locale}/blog/{slug}

Prefer path prefixes over ?lang=en as the canonical model.

Trailing slash, pick one convention

Next.js: trailingSlash: true | false in next.config.

Convention Canonical Redirect
No slash (common for many apps) /en/services /en/services/ → 301
With slash /en/services/ reverse

Host one policy on DevStudioIT Cloud aligned with Next config. Mismatch = soft duplicates in GSC.

const nextConfig = {
  trailingSlash: false,
};

UTM and tracking parameters

Landing canonical without UTMs. Tracking stays in GA4; the index sees a clean URL.

Incoming URL Canonical
/en/services?utm_source=linkedin /en/services
/en/services?ref=partner /en/services (if identical)
/en/services?plan=pro (different content) its own URL + canonical

Conflicts Google tends to ignore

  • Canonical A→B while B points back to A (loop)
  • hreflang pointing at noindex URLs
  • hreflang pointing at 404 / redirect chains
  • Cross-domain canonical without a strong reason
  • HTML canonical X while the sitemap only lists Y inconsistently

Rule: every URL in an hreflang cluster must be 200, indexable, self-canonical, with a full alternate set.

Diagnose in Search Console

  1. Page indexing → duplicate / “Google chose different canonical”
  2. URL Inspection → user-declared vs Google-selected canonical
  3. If Google disagrees: strengthen self-canonical, align content, point internal links at the preferred URL
GSC signal Action
Google chose different canonical Check content, links, slash, www
Alternate page with proper canonical OK, intentional consolidation
Duplicate, Google didn’t canonicalize Fix canonical + 301s

A canonical is a hint, Google also weighs internal links. If the nav, blog, and footer point to /en/services/ while the canonical says /en/services, you send conflicting signals. After you pick a slash convention:

  • update navigation, breadcrumbs, and CTAs
  • regenerate the sitemap
  • fix older CMS / case-study URLs in Branchly

On hosting (DevStudioIT Cloud), keep the 301 as close to the edge as possible, shorter chains mean cleaner crawls.

Rollout checklist

  1. One host policy (https + www/apex)
  2. One trailing-slash policy + 301
  3. Self-canonical on every public page
  4. Full hreflang pl/en/de (+ x-default)
  5. No UTMs in canonical
  6. Old URLs → 301 to new
  7. Staging noindex / separate domain
  8. Sitemap lists only canonical URLs
  9. After deploy: inspect 5–10 key pages
  10. Monitor the duplicate report for 2–4 weeks
  11. Align internal links with the canonical
  12. No canonical A↔B loops

FAQ

Does canonical replace a 301?

No. 301 moves users and link equity at HTTP level. Canonical is a hint for near-duplicate indexing.

Does every page need hreflang?

Only when an equivalent translation exists. A PL-only page should not fake an EN/DE cluster.

What about blog pagination?

Self-canonical on each paginated page or a view-all strategy, pick one. Avoid pointing every page at page=1 if page=2 has unique value.

Should JSON-LD repeat the canonical?

mainEntityOfPage / @id should match the canonical URL, consistency helps; mismatch confuses.

CTA

Duplicates across PL/EN/DE, slash chaos, or “Google chose a different canonical”?

Related posts

FAQPage Schema and Rich Results, When Google Shows FAQs (2026)
5 min read
Sitemap.xml and RSS Feed in Next.js App Router, Technical SEO 2026
6 min read
robots.txt in Next.js App Router, Crawl Budget and Disallow Pitfalls (2026)
5 min read

About the author

We build fast websites, web/mobile apps, AI chatbots and hosting setups — with a focus on SEO and conversion.

Recommended links

From theory to production — Branchly, our hosting stack and shipped work.

Like how we think? Let's build something together.

Start project configuration