Canonical и дубли страниц
Главный инструмент против дублей: указываем поисковику, какая версия страницы — настоящая.
canonical — тег
<link rel="canonical" href="...">, который говорит поисковику: «среди похожих/одинаковых URL вот этот — основной, индексируй его».
Откуда берутся дубли
Дубли — это когда один и тот же контент доступен по нескольким URL. Источники, с которыми разработчик сталкивается постоянно:
- параметры:
?utm_source=...,?ref=...,?sort=...; - фильтры и пагинация каталога;
- версии с/без завершающего слэша, с/без
www, http/https; - один товар в нескольких категориях:
/phones/xи/sale/x.
Дубли вредны: поисковик дробит ссылочные сигналы между копиями, тратит бюджет краулинга и может выбрать в индекс «не ту» версию.
Как ставить canonical
Тег живёт в <head> и указывает на абсолютный URL канонической версии. На основной странице он ссылается сам на себя (self-canonical) — это норма и хорошая практика.
<!-- На странице /shop/keyboards?sort=price -->
<head>
<link rel="canonical" href="https://codechick.io/shop/keyboards">
</head>
<!-- На самой /shop/keyboards — ссылка на себя -->
<link rel="canonical" href="https://codechick.io/shop/keyboards">
На codechick.io self-canonical стоит на страницах туториалов и статей — это страхует от случайных дублей с метками и параметрами.
canonical — это подсказка, а не приказ
Важный нюанс: для Google canonical — лишь сильная рекомендация. Если сигналы противоречивы (внутренние ссылки ведут на одну версию, а canonical на другую), поисковик может выбрать иначе. Поэтому canonical должен быть согласован с остальными сигналами: ссылками, sitemap, редиректами.
Как работает под капотом: нормализация и выбор канона
Чтобы понять, что два URL — дубли, их сначала нормализуют (единый регистр, убрать трекинговые параметры, унифицировать слэш), а затем сравнивают. Модель:
from urllib.parse import urlsplit, parse_qsl, urlencode, urlunsplit
TRACKING = {"utm_source", "utm_medium", "utm_campaign", "ref", "fbclid", "gclid"}
def canonicalize(url):
parts = urlsplit(url.lower())
# выкидываем трекинговые параметры, сортируем оставшиеся
params = [(k, v) for k, v in parse_qsl(parts.query) if k not in TRACKING]
params.sort()
path = parts.path.rstrip("/") or "/"
return urlunsplit((parts.scheme, parts.netloc, path, urlencode(params), ""))
urls = [
"https://CodeChick.io/shop/keyboards/",
"https://codechick.io/shop/keyboards?utm_source=tg",
"https://codechick.io/shop/keyboards?ref=mail",
]
for u in urls:
print(canonicalize(u))
Вывод:
https://codechick.io/shop/keyboards https://codechick.io/shop/keyboards https://codechick.io/shop/keyboards
Все три URL свелись к одному канону — именно его и стоит указать в теге canonical.
Частые ошибки
- canonical на несуществующий/закрытый URL — указали на страницу с noindex или 404.
- Относительный или неполный URL вместо абсолютного — рискованно, лучше полный с протоколом и доменом.
- Все страницы canonical на главную — массовая ошибка шаблона, выкидывает из индекса всё, кроме главной.
- Конфликт сигналов: canonical говорит одно, внутренние ссылки и sitemap — другое.
Итог
canonicalуказывает поисковику главную версию среди дублей; на основной странице — self-canonical.- Это сильная рекомендация, а не приказ: согласуйте её со ссылками, sitemap и редиректами.
- Используйте абсолютные URL и не направляйте canonical на закрытые/несуществующие страницы.