Canonical URL i duplicate content w Next.js, PL/EN/DE, hreflang, trailing slash (2026)

canonical5 min czytania25 lipca 2026

Autor: DevStudio.it

TL;DR

Canonical wskazuje Google „to jest preferowany URL tej treści”. Na wielojęzycznej stronie firmowej (PL/EN/DE) każdy locale ma własny self-canonical, nie wskazujesz angielskiej wersji jako kanonicznej dla polskiej. hreflang mówi „to tłumaczenie”, canonical mówi „to oryginał wśród duplikatów technicznych”. W Next.js App Router ustawiasz to w alternates.canonical + alternates.languages, dopinasz 301 dla trailing slash/www i czyścisz parametry UTM. Poniżej: mapa typowych duplikatów, kod Metadata API, konflikty hreflang↔canonical i checklista GSC.

Dla kogo

  • Właścicieli stron pl/en/de z ostrzeżeniami o duplikatach w GSC
  • Developerów Next.js po migracji lub zmianie struktury URL
  • SEO walczących z www vs non-www, slash vs no-slash, UTM w indeksie
  • Zespołów z preview/ISR generującym dodatkowe URL-e
  • Firm, u których „ta sama oferta” istnieje pod trzema ścieżkami

Fraza (SEO)

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

Canonical vs hreflang, nie myl ról

Mechanizm Pytanie, na które odpowiada Przykład
Canonical Który URL jest „główny” dla tej samej treści / wariantu? /pl/oferta kanoniczne wobec /pl/oferta?utm_source=linkedin
hreflang Które URL-e są tłumaczeniami tej samej strony? /pl/oferta/en/services/de/leistungen
301 redirect Który host/ścieżka jest jedyna na poziomie HTTP? www → apex, /oferta//oferta

Błąd klasyczny: ustawienie canonical polskiej strony na URL angielski „bo EN jest główny”. Google może zignorować hreflang i skonsolidować ruch na EN, PL wypada z wyników lokalnych.

Poprawnie:

  • PL → canonical https://example.com/pl/oferta
  • EN → canonical https://example.com/en/services
  • DE → canonical https://example.com/de/leistungen
  • Wszystkie trzy → wzajemne hreflang (+ x-default jeśli masz)

Typowe źródła duplicate content na stronie usługowej

  1. www vs non-www i http vs https
  2. Trailing slash (/pl/oferta vs /pl/oferta/)
  3. Parametry (UTM, fbclid, sortowanie)
  4. Locale fallback (middleware serwuje EN pod / i pod /en)
  5. Stare URL po redesignie bez 301
  6. Paginacja / tagi bloga o thin content
  7. Preview / draft zindeksowane przez pomyłkę
  8. Ta sama treść CMS pod dwoma slugami

Case studies trzymane w Branchly muszą mieć jeden slug per locale i stabilny canonical w metadata, nie generuj drugiego URL „dla kampanii”.

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 zwykle wskazuje wersję dla nierozpoznanego języka (często EN lub selektor języka), nie „najważniejszy rynek” w sensie biznesowym, tylko fallback językowy.

Blog, ten sam slug, inny locale

W Waszym modelu slug jest wspólny (trust-signals-...), locale w path:

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

Nie używaj query ?lang=pl jako kanonicznego modelu, path prefix jest czytelniejszy dla użytkowników i crawlerów.

Trailing slash, wybierz jedną konwencję

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

Konwencja Canonical Redirect
Bez slash (zalecane dla wielu SaaS) /pl/oferta /pl/oferta/ → 301
Ze slash /pl/oferta/ odwrotnie

Hostuj jedną politykę na DevStudioIT Cloud (edge/nginx/CDN) zgodną z Next config. Rozjazd: soft duplikaty w GSC.

// next.config.ts (fragment)
const nextConfig = {
  trailingSlash: false,
};

Parametry UTM i tracking

Canonical strony docelowej bez UTM. Tracking zostaje w GA4; indeks widzi czysty URL.

URL wejściowy Canonical
/pl/oferta?utm_source=linkedin /pl/oferta
/pl/oferta?ref=partner /pl/oferta (jeśli treść identyczna)
/pl/oferta?plan=pro (inna treść) osobny URL + własny canonical

Nie polegaj wyłącznie na „Google ogarnie UTM”, przy słabych sygnałach bywa inaczej.

Konflikty, które Google lubi ignorować

  • Canonical A→B, ale B canonical z powrotem na A (pętla)
  • hreflang wskazuje URL z noindex
  • hreflang wskazuje 404 / redirect chain
  • Canonical na inną domenę bez silnego uzasadnienia (cross-domain ostrożnie)
  • W HTML canonical X, w sitemap tylko Y bez spójności

Zasada: każdy URL w hreflang clusterze musi być 200, indexable, z self-canonical i pełnym zestawem alternates.

Diagnostyka w Search Console

  1. Page indexing → „Duplicate without user-selected canonical” / „Google chose different canonical”
  2. URL Inspection → „User-declared canonical” vs „Google-selected canonical”
  3. Jeśli Google wybiera inaczej: wzmocnij self-canonical, ujednolić treść, poprawić linki wewnętrzne do preferowanego URL, upewnij się że nie ma silniejszego duplikatu
Sygnał GSC Akcja
Google wybrał inny canonical Sprawdź treść, linki, slash, www
Alternate page with proper canonical OK, wariant świadomie skonsolidowany
Duplicate, Google didn’t canonicalize Uporządkuj canonical + 301

Linki wewnętrzne wzmacniają canonical

Canonical to wskazówka, Google bierze też pod uwagę wewnętrzne linki. Jeśli w menu, blogu i stopce linkujesz do /pl/oferta/, a canonical mówi /pl/oferta, wysyłasz sprzeczne sygnały. Po wyborze konwencji slash:

  • zaktualizuj nawigację, breadcrumbs, CTA
  • regeneruj sitemap
  • popraw stare wpisy CMS / case studies w Branchly

Na hostingu (DevStudioIT Cloud) trzymaj redirect 301 jak najbliżej edge, krótszy łańcuch = czystszy crawl.

Checklista wdrożenia

  1. Jedna polityka host (https + www/apex)
  2. Jedna polityka trailing slash + 301
  3. Self-canonical na każdej stronie publicznej
  4. Pełny hreflang pl/en/de (+ x-default)
  5. UTM nie w canonical
  6. Stare URL → 301 na nowe
  7. Staging noindex / osobna domena
  8. Sitemap tylko canonical URL-e
  9. Po deployu: Inspection 5–10 kluczowych stron
  10. Monitor 2–4 tygodnie raportu duplikatów
  11. Spójne linki wewnętrzne z canonical
  12. Brak pętli canonical A↔B

FAQ

Czy canonical zastępuje 301?

Nie. 301 przenosi użytkowników i link equity na poziomie HTTP. Canonical to wskazówka dla indeksu przy podobnych treściach.

Czy każda podstrona musi mieć hreflang?

Tylko jeśli istnieje równoważne tłumaczenie. Strona tylko po PL nie powinna udawać clusteru EN/DE.

Co z paginacją bloga?

rel=canonical zwykle self na każdej stronie paginacji albo strategia „view-all”, wybierz jedną i bądź konsekwentny. Unikaj canonical wszystkich stron na page=1 jeśli treść page=2 jest istotna i unikalna.

Czy JSON-LD musi powtarzać canonical?

mainEntityOfPage / @id powinny być zgodne z canonical URL, spójność encji pomaga, niespójność myli.

CTA

Masz bałagan z duplikatami PL/EN/DE, slashami albo „Google wybrał inny canonical”?

Powiązane wpisy

Schema FAQPage i rich results, kiedy Google pokazuje FAQ (2026)
5 min czytania
robots.txt w Next.js App Router, crawl budget i pułapki Disallow (2026)
5 min czytania
Long-tail keywords na stronie usługowej, research, struktura i blog vs usługi (2026)
6 min czytania

O autorze

Budujemy szybkie strony WWW, aplikacje web/mobile, chatboty AI i hosting — z naciskiem na SEO i konwersję.

Przydatne linki

Od teorii do produkcji — Branchly, hosting i realizacje.

Podoba Ci się nasze podejście? Zbudujmy coś razem.

Rozpocznij konfigurację projektu