Динамическое формирование ключей кэша на границе для обеспечения SEO‑последовательности в многоязычных сайтах
Когда веб‑сайт обслуживает один и тот же контент на нескольких языках, слой edge становится критической точкой управления. Хорошо продуманная стратегия ключей кэша может мгновенно доставлять нужную языковую версию и сохранять доверие поисковых систем к сайту. В этой статье мы разберём, почему традиционные статические ключи не подходят, как работает динамическое составление ключей на границе и какие шаблоны позволяют сохранять сигналы SEO в разных регионах.
Проблема «один размер подходит всем» для ключей
Большинство CDN‑провайдеров по умолчанию используют простой хеш запрашиваемого URL. Это работает для монолитных (одноязычных) сайтов, но когда пользователь запрашивает /en/about, а другой — /fr/about, хеш отличается лишь языковым сегментом. CDN сохраняет два отдельные объекта, что, в принципе, нормально, однако такой ключ всё равно не учитывает нюансы hreflang и canonical. Поисковые системы рассматривают эти объекты как отдельные ресурсы; если одна и та же страница кэшируется под несколькими ключами без корректных сигналы, могут возникнуть штрафы за дублирующий контент, а бюджет обхода (crawl budget) будет тратиться впустую на повторную загрузку одинаковых ресурсов.
Основные компоненты многоязычного ключа кэша
Надёжный многоязычный ключ кэша обычно комбинирует следующие измерения:
- Код языка — извлекается из пути URL, субдомена или заголовка
Accept-Language. - Подсказка о регионе — опционально, берётся из данных GeoIP, чтобы учитывать региональные различия (валюта, единицы измерения).
- Класс устройства — мобильный vs. десктоп, важно для адаптивного дизайна.
- Вариативные заголовки запроса — например
Accept-Encodingдля gzip vs. brotli илиUser‑Agentдля A/B‑тестов.
Эти измерения конкатенируются в детерминированном порядке и хешируются только после нормализации. В результате получается лёгкая строка, которую edge‑сервер использует для поиска, при этом полностью отражая контекст запроса.
Конвейер нормализации на границе
Прежде чем собрать ключ, edge‑функция должна нормализовать запрос:
- Убрать параметры слежения — удалить строки запроса
utm_*, чтобы аналитические метки не фрагментировали кэш. - Канонизировать путь — устранить дублирующиеся слеши, относительные сегменты (
../) и завершающие слеши в соответствии с политикой URL сайта. - Сопоставить алиасы языков — считать
/en-usи/enэквивалентными, если сайт обслуживает одну английскую версию. - Привести к нижнему регистру — URL обычно нечувствительны к регистру; принудительное приведение к нижнему устраняет лишние варианты.
Нормализованный запрос затем передаётся генератору ключа. Ниже — упрощённый пример псевдокода, который может работать в Cloudflare Workers, AWS Lambda@Edge или любой другой V8‑базированной среде edge.
// edge-key-generator.js
export async function handle(event) {
const { request } = event;
const url = new URL(request.url);
// 1️⃣ Remove analytics parameters
for (const param of url.searchParams.keys()) {
if (param.startsWith('utm_')) url.searchParams.delete(param);
}
// 2️⃣ Normalize pathname
url.pathname = decodeURI(url.pathname)
.replace(/\/{2,}/g, '/')
.replace(/\/$/, '');
// 3️⃣ Detect language from path or header
const langMatch = url.pathname.match(/^\/([a-z]{2})(?:-[A-Z]{2})?(?=\/|$)/);
const language = langMatch ? langMatch[1] : request.headers.get('Accept-Language')?.split(',')[0]?.slice(0,2) || 'en';
// 4️⃣ Build key components
const region = request.headers.get('CF-IPCountry') || 'XX';
const device = request.headers.get('User-Agent')?.includes('Mobile') ? 'mobile' : 'desktop';
// 5️⃣ Assemble deterministic key
const rawKey = `${language}|${region}|${device}|${url.pathname}`;
const cacheKey = crypto.subtle.digest('SHA-256', new TextEncoder().encode(rawKey))
.then(buf => [...new Uint8Array(buf)].map(b => b.toString(16).padStart(2, '0')).join(''));
// Attach the cache key for downstream caching logic
request.headers.set('X-Edge-Cache-Key', await cacheKey);
return request;
}