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

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

Как Google рендерит JavaScript: как это работает

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

Google использует собственный браузерный движок на базе Chromium, который регулярно обновляют. Поэтому современные возможности JavaScript (ES2020+, динамический 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, meta 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: what Google sees in the first wave -->
<html>
  <head>
    <title></title> <!-- empty -->
  </head>
  <body>
    <div id="root"></div> <!-- no content -->
    <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, дашборды
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; // revalidate every hour

export async function generateMetadata(): Promise<Metadata> {
  return {
    title: 'Product Catalogue',
    description: 'Full range with up-to-date prices',
  };
}

// All critical content is server-rendered:
export default async function CatalogPage() {
  const products = await fetchProducts(); // server fetch
  return (
    <main>
      <h1>Product Catalogue</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: <Link> в Next.js и <Link> в React Router на сервере генерируют корректные теги <a>
  • Навигация по хешу (#anchor) не создаёт отдельных URL в индексе, не используйте её для контента, который должен ранжироваться сам по себе
  • Динамические меню: если пункты меню подгружаются из API после загрузки страницы, Google может не найти их до рендеринга
Проверка: в Google Search Console → «Проверка URL» → «Просмотр как Googlebot» видно, какие ссылки сканер нашёл на странице. Если динамической ссылки нет в списке, Googlebot её не видит.

Ленивая загрузка и бесконечная прокрутка

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

Бесконечная прокрутка нуждается в сканируемом пути к каждому значимому элементу. Сделайте последовательные URL и обычные ссылки, которые работают без нажатия кнопки, затем проверьте через «Проверку URL» или сканирование, что HTML после рендеринга содержит нужный контент.

ПаттернВидит ли Googlebot?Рекомендация
<img src="/img.webp" loading="lazy">ДаИспользуйте
<img data-src="/img.webp"> (JS задаёт src)НетИзбегайте
Бесконечная прокрутка без URLТолько первый экранДобавьте пагинацию
Контент за кнопкой «Показать ещё»НетВынесите часть в HTML
Табы со скрытым контентомДа, с задержкойПродублируйте в 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»): быстрый способ увидеть страницу так, как её видит сканер без рендеринга.
  • Screaming Frog с рендерингом: настройка «Spider > Configuration > Rendering > Googlebot» позволяет сканировать сайт с рендерингом JS и видеть ссылки, найденные после рендеринга.
Пример конфигурации Next.js для SSR/SSG:
bash
# Check what Google sees without JS rendering:
curl -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" \
  https://example.com/page

# Find all title / meta description / h1 in HTML response:
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 на странице)
  • Core Web Vitals: LCP, INP и CLS соответствуют пороговым значениям Google
  • Пагинация через параметры URL, а не только через состояние JS
  • Канонические URL (<link rel="[canonical](/glossary/canonical)">) прописаны в HTML, а не заданы через JS

Частые вопросы

Может ли Google вообще индексировать JavaScript?
Да. Googlebot использует Evergreen Chromium и может выполнять современный JS. Проблема не в поддержке, а в задержке: рендеринг происходит во второй волне, которая может наступить через дни или недели после сканирования HTML.
Хорошо ли индексируется сайт на React/Next.js автоматически?
Next.js с SSR/SSG — да, если настроен правильно. Обычный React SPA (Create React App, Vite без SSR) по умолчанию работает как CSR, а для SEO это плохо. Ключ — серверная генерация meta-тегов, h1 и основного контента.
Нужен ли пререндеринг или динамический рендеринг?
Динамический рендеринг (показ Googlebot одного HTML, а пользователям другого) — устаревший подход. Google его допускает, но не рекомендует. Правильный путь — SSR или SSG, которые отдают всем один и тот же полный HTML.
Можно ли использовать React.lazy и разбиение кода?
Да. Разбиение кода через React.lazy и динамический import ускоряет загрузку и не вредит SEO, если критичный контент (H1, основной текст) уже есть в исходном HTML. Компоненты, которые загружаются по запросу, всё равно будут получены Googlebot во время рендеринга.
Как проверить, что сайт индексируется правильно?
Используйте Google Search Console → «Проверка URL». Запросите рендеринг и сравните скриншот с тем, что видит обычный пользователь. Также проверьте через curl, есть ли title, h1 и meta description в HTML-ответе без JS.

Check it with SpiderHead

Free and local: run the audit on your own machine.

Try it