Дублированный контент может создавать неоднозначность при индексации мультиязычных сайтов и страниц с параметрами. Управлять этим помогают два механизма.
Canonical указывает предпочтительный URL
Он сообщает поисковикам, какой URL предпочтителен, когда один контент доступен по нескольким адресам (трекинг-параметры, слэши, пагинация):
export async function generateMetadata({ params }): Promise<Metadata> {
return {
alternates: {
canonical: `/blog/${params.slug}`,
},
};
}
hreflang связывает языковые версии
Он сообщает поисковикам, какие языковые версии существуют, и помогает выбрать подходящую. Способ получения адресов зависит от модели контента; пример предполагает, что getLocalizedUrls() возвращает реальный URL каждой локали:
export async function generateMetadata({ params }): Promise<Metadata> {
const { lang } = params;
const localizedUrls = await getLocalizedUrls(params);
return {
alternates: {
canonical: localizedUrls[lang],
languages: {
...localizedUrls,
"x-default": localizedUrls.en,
},
},
};
}
Типичные ошибки реализации
- hreflang должен быть взаимным. Если EN-страница ссылается на PL-версию, PL-страница обязана ссылаться обратно — иначе Google может не учитывать всю группу.
- Каждая страница должна включать саму себя в карту
languages. - Локализованные слаги: если польский пост живёт по другому слагу, hreflang должен указывать на реальный локализованный URL, а не на механический
/pl/+ тот же слаг. Генерируйте карту из источника контента, не хардкодьте. x-default— для пользователей, чей язык вы не поддерживаете; направьте его на основную локаль.- Canonical должен вести на страницу с кодом 200 и без
noindex— canonical на редиректящий URL игнорируется.
Чеклист:
- Самоссылающийся canonical на каждой индексируемой странице
- hreflang сгенерирован из реальных локализованных слагов и взаимен во все стороны
- Есть
x-default - URL-параметры (utm, фильтры, сортировки) канонизируются на чистый URL
