Googlebot не читает JS в тот же момент, когда забирает страницу. Сначала он парсит сырой HTML, потом ставит URL в отдельную очередь на рендер — и до этой очереди может дойти через дни, а на слабом домене через недели. SSR и SSG снимают риск почти полностью, чистый CSR — это всегда лотерея с отложенным результатом. Разбираю механику двух проходов, разницу поведения Google и Яндекса и свою ошибку с SPA без SSR на сайте услуг, которая полгода стоила органического трафика.
«У нас React, Google и так всё индексирует, он умный» — слышу это на каждом втором аудите сайта на SPA. Отчасти правда: Googlebot действительно умеет выполнять JavaScript, в отличие от большинства других ботов. Но «умеет» и «делает это сразу, бесплатно и для каждой страницы» — разные вещи. Между «умеет» и «реально проиндексировал» лежит механика, которую не разжёвывают в маркетинговых блогах фреймворков.
Два прохода, а не один
Когда Googlebot впервые забирает URL, он получает сырой HTML — ровно то, что прислал сервер до выполнения единой строчки JavaScript. Если это чистый CSR-рендер (client-side rendering, типичный Create React App или Vite-сборка без SSR), сырой HTML выглядит как <div id="root"></div> и тег скрипта. Пусто. Контента, ссылок, текста — нет вообще.
Дальше у Google два этапа. Первый — обработка сырого HTML: заголовки, canonical, meta robots, ссылки, которые физически есть в разметке. Второй — рендеринг через headless Chromium (внутри Google это называют Web Rendering Service, WRS) — отдельный процесс, который реально выполняет JS, строит DOM и только потом видит то, что видит пользователь в браузере. Между первым и вторым проходом — очередь. Google прямо пишет в своей документации про понимание JavaScript, что рендеринг не мгновенный и зависит от доступных ресурсов рендеринга на конкретный момент.
На практике это означает: страница может успешно проползти первый проход (сервер ответил 200, canonical ОК), зависнуть в рендер-очереди на непонятный срок, и только потом — если домен достаточно авторитетен, чтобы получить рендер-бюджет быстро — контент реально увидят и начнут оценивать для индекса. На новом домене или домене с низким crawl budget это может быть недели, не дни.
Что это значит для ссылок и внутренней перелинковки
Самое больное место — не текст, а ссылки. Если внутренняя навигация реализована через onClick с программным роутингом (react-router без реальных <a href="..."> в DOM до рендера), первый проход просто не видит путей дальше. А значит и crawl budget на эти страницы не тратится до того, как рендер-очередь дойдёт до родительской страницы и откроет их. Цепная задержка: не одна страница ждёт рендера, а вся ветка сайта, которая на неё ссылается.
Боты, которые не умеют выполнять JS вообще, тут в ещё худшем положении — тот же GPTBot и ClaudeBot (разбирал в отдельном посте про robots.txt под ИИ-краулеры) не рендерят JS в принципе. Для них сайт на чистом CSR без SSR — это пустая страница с тегом скрипта, и точка.
CSR, SSR, SSG, ISR — что выбирать и какой риск у каждого
| Подход | Что получает краулер в сыром HTML | Риск для SEO | Когда годится |
|---|---|---|---|
| CSR (чистый React/Vue SPA) | Пустой контейнер, без контента и ссылок | Высокий — всё зависит от рендер-очереди и бюджета | Личный кабинет, админка, всё, что не нужно в поиске |
| SSR (Next.js/Nuxt с server-side рендерингом) | Полный HTML с контентом и ссылками сразу, на первом проходе | Низкий — краулер видит то же, что пользователь, без ожидания | Любая страница, которая должна попасть в выдачу |
| SSG (статическая генерация на сборке) | Готовый HTML, как у обычного статичного сайта | Минимальный — рендер-очередь вообще не нужна | Блог, лендинги, страницы услуг, каталог без real-time данных |
| Dynamic rendering (отдельный рендер для ботов) | Предрендеренный HTML только для юзер-агентов краулеров | Средний — Google официально не поощряет как основной метод | Временная заплатка на существующем SPA, пока не перевели на SSR |
Яндекс — отдельная история, и она хуже
С Google ещё можно жить на CSR, если домен авторитетный и терпение есть. С Яндексом ставки выше. Поддержка JS-рендеринга у Яндекса ограниченнее, рендер-бюджет скромнее, и на практике для .by/.ru-сайтов на чистом SPA без SSR органика из Яндекса может не появиться вообще — не «появится позже», а просто не появится, потому что страница так и не попадает в тот процент URL, до которого у Яндекса хватает ресурса на рендер. Если у тебя трафик из двух поисковиков одновременно (а для .by это норма), SSR или SSG — не опция для рассмотрения, а обязательное условие на старте.
Моя ошибка: полгода думал, что дело в рендер-бюджете
Вёл сайт услуг на чистом React без SSR — решение приняли до меня, я зашёл на правки семантики и технички уже на готовом SPA. Первые три месяца трафика почти нет, хотя контент нормальный, ключи собраны по делу. Грешил на рендер-бюджет — думал, подождём, Google новые домены раскачивает медленно, это нормально.
Полез в Google Search Console → Покрытие (Coverage), а там половина страниц сайта в статусе «Обнаружено, не проиндексировано» (Discovered — currently not indexed) — месяцами, без движения. Залез через URL Inspection → «Протестировать активный URL» → скриншот отрендеренной страницы — а там реально пусто, рендер происходил, но без контента в разделах, куда вели внутренние ссылки через onClick. Проблема была не в том, что Google «не успевал» рендерить — рендер происходил. Проблема была в том, что внутренние ссылки физически не существовали в DOM до клиентского роутинга, и Googlebot банально не находил, куда идти дальше, пока не дорендерит родителя. Переделали на честные <a href> с клиентским роутингом через preventDefault (фреймворк это позволяет без переписывания всей логики) — через месяц Покрытие начало расчищаться.
Ты вообще смотрел скриншот из «Протестировать активный URL» в Search Console хоть раз для своего сайта на SPA, или просто веришь, что раз в консоли нет красных ошибок — значит всё рендерится как надо?
По теме техничного SEO смотри ещё: мобильная индексация Googlebot, оптимизация Core Web Vitals и разбор статуса «просканировано, не проиндексировано».