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
wwwvs 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-defaultjeśli masz)
Typowe źródła duplicate content na stronie usługowej
- www vs non-www i http vs https
- Trailing slash (
/pl/ofertavs/pl/oferta/) - Parametry (UTM,
fbclid, sortowanie) - Locale fallback (middleware serwuje EN pod
/i pod/en) - Stare URL po redesignie bez 301
- Paginacja / tagi bloga o thin content
- Preview / draft zindeksowane przez pomyłkę
- 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
- Page indexing → „Duplicate without user-selected canonical” / „Google chose different canonical”
- URL Inspection → „User-declared canonical” vs „Google-selected canonical”
- 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
- Jedna polityka host (https + www/apex)
- Jedna polityka trailing slash + 301
- Self-canonical na każdej stronie publicznej
- Pełny hreflang pl/en/de (+ x-default)
- UTM nie w canonical
- Stare URL → 301 na nowe
- Staging noindex / osobna domena
- Sitemap tylko canonical URL-e
- Po deployu: Inspection 5–10 kluczowych stron
- Monitor 2–4 tygodnie raportu duplikatów
- Spójne linki wewnętrzne z canonical
- 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”?
- Umów audyt canonical / hreflang, Metadata API, 301, DevStudioIT Cloud
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.
