Оптимизация SEO для мультиязычного сайта (hreflang audit)
Мультиязычный сайт без правильно настроенного hreflang — источник систематических SEO-проблем. Google показывает русскую версию немецким пользователям, английскую страницу ранжирует в Украине вместо украинской, дублирующиеся переводы съедают crawl budget. Hreflang — это атрибут, который сообщает поисковику: «для пользователей с языком X и регионом Y используй эту версию страницы».
Синтаксис hreflang
<link rel="alternate" hreflang="ru" href="https://example.com/ru/about/" />
<link rel="alternate" hreflang="en" href="https://example.com/en/about/" />
<link rel="alternate" hreflang="de" href="https://example.com/de/about/" />
<link rel="alternate" hreflang="uk" href="https://example.com/uk/about/" />
<link rel="alternate" hreflang="x-default" href="https://example.com/about/" />
Три места для размещения hreflang:
-
<head>HTML-страницы (показано выше) - HTTP-заголовок
Link:(для PDF и нон-HTML ресурсов) - XML Sitemap
Критическое правило: hreflang должен быть взаимным. Если страница A ссылается на страницу B через hreflang, страница B обязана ссылаться обратно на страницу A. Без этого Google игнорирует атрибут.
Распространённые ошибки
1. Отсутствие самоссылки. Каждая страница должна содержать hreflang на саму себя:
<!-- На /ru/about/ -->
<link rel="alternate" hreflang="ru" href="https://example.com/ru/about/" />
<!-- + ссылки на все остальные языковые версии -->
2. Неправильные коды языков. Используется ISO 639-1 (язык) и ISO 3166-1 alpha-2 (регион):
ru → русский (без привязки к региону)
ru-RU → русский, Россия
ru-UA → русский, Украина
uk → украинский
en-US → английский, США
en-GB → английский, Великобритания
zh-Hans → китайский упрощённый
zh-Hant-TW → китайский традиционный, Тайвань
Ошибка: hreflang="ua" (нет такого кода; украинский язык = uk).
3. Смешивание www и non-www, http и https. URL в hreflang должен совпадать с canonical версией:
<!-- Неправильно: canonical = https://example.com, hreflang = http:// -->
<link rel="canonical" href="https://example.com/ru/about/" />
<link rel="alternate" hreflang="ru" href="http://example.com/ru/about/" /> <!-- ошибка -->
4. hreflang на noindex-страницы. Google рекомендует не указывать в hreflang страницы, закрытые от индексации.
Audit: обнаружение ошибок
Screaming Frog:
- Crawl сайта
- Reports → Hreflang → Hreflang Pages
- Фильтры: «Missing Return Links», «Incorrect Language/Region Codes», «Non-Canonical Return Link»
Python-скрипт для валидации взаимных ссылок:
import requests
from bs4 import BeautifulSoup
from collections import defaultdict
from urllib.parse import urljoin
import time
def fetch_hreflang(url: str) -> dict:
"""Возвращает {lang: href} для URL"""
try:
resp = requests.get(url, timeout=10, headers={
'User-Agent': 'Mozilla/5.0 (compatible; HreflangAudit/1.0)'
})
soup = BeautifulSoup(resp.text, 'html.parser')
links = soup.find_all('link', rel='alternate')
return {
link.get('hreflang'): link.get('href')
for link in links
if link.get('hreflang')
}
except Exception as e:
return {}
def audit_hreflang(urls: list[str]) -> list[dict]:
hreflang_map = {}
errors = []
# Собираем hreflang для всех страниц
for url in urls:
hreflang_map[url] = fetch_hreflang(url)
time.sleep(0.5)
# Проверяем взаимность
for url, alternates in hreflang_map.items():
for lang, alt_url in alternates.items():
if lang == 'x-default':
continue
if alt_url not in hreflang_map:
errors.append({
'type': 'missing_page',
'source': url,
'lang': lang,
'target': alt_url
})
continue
# Проверяем обратную ссылку
target_alternates = hreflang_map.get(alt_url, {})
# Ищем ссылку обратно на source
back_links = [v for v in target_alternates.values() if v == url]
if not back_links:
errors.append({
'type': 'missing_return_link',
'source': url,
'lang': lang,
'target': alt_url,
'message': f'{alt_url} не содержит hreflang обратно на {url}'
})
return errors
Проверка через GSC:
Search Console → International Targeting → Language. Здесь видны ошибки hreflang, обнаруженные Googlebot. Раздел не показывает все ошибки, но критические всплывают.
Реализация hreflang в Sitemap
Для больших сайтов (1000+ страниц) поддерживать hreflang в <head> каждой страницы сложно — проще через sitemap:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<url>
<loc>https://example.com/ru/about/</loc>
<xhtml:link rel="alternate" hreflang="ru" href="https://example.com/ru/about/"/>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/about/"/>
<xhtml:link rel="alternate" hreflang="uk" href="https://example.com/uk/about/"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/about/"/>
</url>
<url>
<loc>https://example.com/en/about/</loc>
<xhtml:link rel="alternate" hreflang="ru" href="https://example.com/ru/about/"/>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/about/"/>
<xhtml:link rel="alternate" hreflang="uk" href="https://example.com/uk/about/"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/about/"/>
</url>
</urlset>
Генерация в Laravel:
// routes/web.php или SitemapController
public function multilingual(): Response
{
$pages = Page::where('status', 'published')->get();
$locales = ['ru', 'en', 'uk', 'de'];
return response()
->view('sitemap.hreflang', compact('pages', 'locales'))
->header('Content-Type', 'application/xml');
}
x-default: когда и зачем
x-default указывает страницу для пользователей, чей язык/регион не совпадает ни с одним из указанных hreflang. Типичные сценарии:
- Страница выбора языка:
hreflang="x-default" href="https://example.com/" - Английская версия как дефолтная для незнакомых рынков
- Отсутствие x-default если нет нейтральной версии — это нормально
Структуры URL для мультиязычных сайтов
Три варианта, каждый с плюсами:
| Структура | Пример | SEO |
|---|---|---|
| Поддомен | ru.example.com | Проще разделить GWT property, сложнее технически |
| Подпапка | example.com/ru/ | Проще, ссылочный вес делится с основным доменом |
| ccTLD | example.ru | Сильный геосигнал, дорого в поддержке |
Подпапка (/ru/, /en/) — рекомендуемый вариант для большинства проектов.
Сроки
Аудит hreflang на существующем сайте (до 5 языков, до 500 страниц) — 2–3 рабочих дня: краулинг, анализ ошибок, отчёт. Реализация исправлений + настройка автогенерации hreflang в шаблоне/sitemap — 3–5 дней. Повторный аудит через 4 недели для подтверждения результата входит в задачу.







