Техническое SEO

JavaScript SEO: как Google индексирует SPA и React-сайты

JavaScript SEO: индексирование SPA и React

Двухволновое индексирование, ловушки CSR, разница SSR/SSG/ISR и практические способы убедиться, что JS-сайт правильно попадает в индекс.

Как Google рендерит JavaScript: принцип работы

Для обычной HTML-страницы Google делает один шаг: скачивает HTML и сразу видит контент. Для JavaScript-страницы шагов три: скачать HTML → загрузить JS-бандл → запустить его в headless Chromium, чтобы получить финальный DOM.

Google использует собственный браузерный движок на базе Chromium — актуальной версии, обновляемой регулярно. Поэтому современные JavaScript-фичи (ES2020+, dynamic import, WebAssembly) Googlebot понимает. Проблема не в поддержке синтаксиса, а в задержке рендеринга и ресурсных ограничениях обхода.

Три стратегии рендеринга JavaScript: SSR и SSG/ISR отдают готовый HTML, CSR строит DOM в браузере и попадает во вторую волну индексации.

Рендеринг в цифрах

3–4 с

Таймаут рендеринга

Если JS не исполнился за ~3–4 секунды, Google индексирует частично отрендеренную страницу

≈ 150 мс

Время на CSS

Ресурсы, загружаемые дольше 150 мс, могут блокировать рендеринг и ухудшать краулинг

2 волны

Индексирование

Контент из JS попадает в индекс с задержкой относительно HTML-контента — иногда дни и недели

100%

Evergreen Google

Googlebot обновляется до последней стабильной версии Chromium — JS-фичи поддерживаются полностью

Двухволновое индексирование: почему JS-контент попадает в индекс позже

Google разделяет обход на два этапа. Первая волна — краулинг: Googlebot скачивает HTML и все обнаруженные ссылки. Вторая волна — рендеринг: робот ставит страницы в очередь на рендеринг, запускает JS и индексирует финальный DOM.

Между этими волнами может пройти от нескольких часов до нескольких недель — в зависимости от авторитетности сайта, краулингового бюджета и размера очереди рендеринга Google. Если ваши ключевые тексты, заголовки H1, мета-теги или ссылки генерируются только через JS — они попадут в индекс с задержкой.

Практический вывод: Весь критически важный для SEO контент — title, description, H1, ссылки, структурированные данные — должен присутствовать в HTML-ответе сервера. Тогда он попадает в первую волну индексирования без задержки.
Двухволновое индексирование — причина, по которой новые SPA-страницы «пропадают» из индекса на 2–4 недели после публикации, хотя HTML физически доступен.

CSR — главная ловушка для SEO

Client-Side Rendering (CSR) — это когда сервер отдаёт пустой <div id="root"></div>, а весь HTML строится в браузере после загрузки JS-бандла. React, Vue и Angular в режиме SPA по умолчанию работают именно так.

С точки зрения SEO CSR — проблема по нескольким причинам: 1) рендеринг происходит только во второй волне; 2) ресурсы, заблокированные краулером (сторонние скрипты, CDN с robots.txt), не загружаются — страница рендерится неполностью; 3) JS-ошибка на любом этапе = пустая страница в индексе.

HTML
<!-- CSR: что видит Google в первой волне -->
<html>
  <head>
    <title></title> <!-- пусто -->
  </head>
  <body>
    <div id="root"></div> <!-- контента нет -->
    <script src="/bundle.js"></script>
  </body>
</html>
Критическая ошибка: Генерировать <title>, <meta name="description"> и <h1> только через JS на клиенте. Google получит пустые или дефолтные значения при первичном краулинге и проиндексирует страницу без них.

SSR, SSG и ISR: что выбрать для SEO

Решение проблем CSR — серверная генерация HTML. Существует три основных подхода, и каждый оптимален для своего сценария.

Три подхода к рендерингу JavaScript и их влияние на SEO.

Сравнение подходов рендеринга

ПодходHTML на сервереSEOДинамика
CSRНетПлохоПолная
SSRДа (каждый запрос)ОтличноВысокая
SSGДа (при сборке)ОтличноНет (ребилд)
ISRДа (по таймеру)ОтличноЧерез ревалидацию
Варианты рендеринга и подходящие задачи:
ПодходПодходит для
CSRАвторизованные SPA, dashboards
SSRПерсонализированные страницы, каталоги
SSGБлог, лендинги, документация
ISRКаталоги с ценами, новости

SSR (Server-Side Rendering) — HTML генерируется на сервере при каждом запросе. Максимальная актуальность данных, но нагрузка на сервер выше. Next.js: функция generateMetadata и async компоненты в App Router дают SSR по умолчанию.

SSG (Static Site Generation) — HTML генерируется один раз при сборке и отдаётся как статический файл. Самый быстрый TTFB, идеально для SEO. Минус: при изменении данных нужен ребилд.

ISR (Incremental Static Regeneration) — гибрид: страница строится статически, но по истечении времени ревалидации (revalidate: 3600) сервер перегенерирует её в фоне при следующем запросе. В Next.js это стандартный подход для большинства страниц.

TYPESCRIPT
// Next.js App Router — SSG + ISR
export const revalidate = 3600; // ревалидация каждый час

export async function generateMetadata(): Promise<Metadata> {
  return {
    title: 'Каталог товаров',
    description: 'Весь ассортимент с актуальными ценами',
  };
}

// Весь критичный контент рендерится на сервере:
export default async function CatalogPage() {
  const products = await fetchProducts(); // серверный fetch
  return (
    <main>
      <h1>Каталог товаров</h1>
      {products.map(p => <ProductCard key={p.id} {...p} />)}
    </main>
  );
}

Динамические ссылки и навигация

Googlebot понимает ссылки только в формате <a href="/url">. Ссылки, реализованные через onClick, button, data-href или window.location — для краулера невидимы. Это означает, что страницы, доступные только через такую навигацию, могут не попасть в индекс.

  • Используйте <a href> для всех навигационных ссылок — не button onClick с router.push()
  • Пагинация через URL-параметры (?page=2) — Googlebot обходит такие URL, если они присутствуют в HTML
  • SPA-роутер: Next.js <Link> и React Router <Link> генерируют корректные <a> теги на сервере
  • Хэш-навигация (#anchor) не создаёт отдельных URL в индексе — не используйте для контента, который должен ранжироваться отдельно
  • Динамические меню: если пункты меню рендерятся через API после загрузки — Google может их не обнаружить, пока не отрендерит страницу
Проверка: В Google Search Console → Инспекция URL → «Посмотреть как Googlebot» показывает, какие ссылки краулер нашёл на странице. Если динамической ссылки нет в списке — Googlebot её не видит.

Lazy load и бесконечный скролл

Изображения с loading="lazy" Googlebot загружает — современный краулер поддерживает ленивую загрузку. Но есть условие: изображение должно быть в DOM с корректным атрибутом src при SSR. Если src подставляется JavaScript-ом уже в браузере — краулер видит пустой тег.

Бесконечный скролл — классическая проблема. Google может загружать первый «экран» контента, но последующие порции, подгружаемые через IntersectionObserver, не гарантированно индексируются. Решение: дублируйте контент через пагинацию с реальными URL (/catalog?page=2) или добавляйте ссылки на подгружаемые страницы в HTML.

ПаттернGooglebot видит?Рекомендация
<img src="/img.webp" loading="lazy">ДаИспользовать
<img data-src="/img.webp"> (JS подставляет src)НетИзбегать
Бесконечный скролл без URLТолько первый экранДобавить пагинацию
Контент за кнопкой «Показать ещё»НетПоказывать часть в HTML
Tabbed content (скрытые вкладки)Да, с задержкойДублировать в HTML

Диагностика: как проверить рендеринг JavaScript

Ключевой вопрос диагностики: «Что видит Googlebot на моей странице?». Существует несколько инструментов для ответа на него.

Инструменты диагностики

  • Google Search Console → Инспекция URL: показывает скриншот страницы после рендеринга Googlebot-ом, список ресурсов и обнаруженных ссылок. Самый прямой способ проверки.
  • Просмотр «как Googlebot» в GSC: запускает реальный краулер и возвращает HTML после рендеринга — можно сравнить с исходным HTML.
  • curl или wget: получить сырой HTML без JS. Если ключевые мета-теги и контент есть в curl-выводе — они будут в первой волне индексирования.
  • Отключить JS в Chrome DevTools (F12 → Ctrl+Shift+P → «Disable JavaScript»): быстрый способ увидеть страницу глазами краулера без рендеринга.
  • Fetch as Google в GSC: аналог предыдущего метода, но с реальным UA Googlebot-а и рендерингом в очереди Google.
  • Screaming Frog с рендерингом: настройка «Spider > Configuration > Rendering > Googlebot» позволяет краулить сайт с рендерингом JS — видны ссылки, найденные после рендеринга.
Пример конфигурации Next.js для SSR/SSG:
BASH
# Проверить, что Google видит без рендеринга JS:
curl -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" \
  https://example.com/page

# Найти все title / meta description / h1 в HTML-ответе:
curl -s https://example.com/page | grep -E '<title>|<meta name="description"|<h1'

Чеклист JavaScript SEO

Основные проверки

  • Title и meta description присутствуют в HTML-ответе сервера (SSR/SSG), не генерируются JS
  • H1, H2 и основной текстовый контент доступны без JavaScript
  • Ссылки навигации реализованы через <a href>, а не onClick / button
  • Структурированные данные (JSON-LD) присутствуют в <head> в HTML-ответе
  • Изображения имеют статичный src в HTML, loading="lazy" — только для не-LCP изображений
  • Robots.txt не блокирует JS/CSS файлы, необходимые для рендеринга
  • Нет JS-ошибок, ломающих рендеринг (проверьте Chrome DevTools Console на странице)
  • Core Web Vitals: LCP, INP, CLS соответствуют пороговым значениям Google
  • Пагинация через URL-параметры, не только через JS-состояние
  • Канонические URL (<link rel="[canonical](/glossary/canonical)">) в HTML, не устанавливаются через JS

FAQ

Да. Googlebot использует Evergreen Chromium и может исполнять современный JS. Проблема не в поддержке, а в задержке: рендеринг происходит во второй волне, которая может наступить через дни или недели после краулинга HTML.
Next.js с SSR/SSG — да, при правильной настройке. Чистый React-SPA (Create React App, Vite без SSR) по умолчанию — CSR, что плохо для SEO. Ключевое: серверная генерация meta, h1 и основного контента.
Dynamic rendering (показывать разный HTML Googlebot и пользователям) — устаревший подход. Google его допускает, но не рекомендует. Правильный путь — SSR или SSG, которые отдают один и тот же полный HTML всем.
Да. Code splitting через React.lazy и dynamic import улучшает производительность и не вредит SEO, если критичный контент (H1, основной текст) уже присутствует в начальном HTML. Компоненты, загружаемые по требованию, Googlebot всё равно получит при рендеринге.
Используйте Google Search Console → Инспекция URL. Запросите рендеринг и сравните скриншот с тем, что видит обычный пользователь. Дополнительно: проверьте curl-ом наличие title, h1 и meta description в HTML-ответе без JS.