CSR (client-side rendering) — документ URL собирается в браузере после загрузки и исполнения JavaScript. Первый HTTP-ответ часто оболочка: пустой контейнер, лоадер, «включите JS». Робот без второго прохода видит оболочку, не оффер. Это не SSR (HTML уже в теле ответа) и не очередь Chromium как отдельный кабинет. Контракт origin — соседний JavaScript SEO. Очередь WRS — JavaScript SEO в Google. Общий рендеринг для поисковиков — следующий H1, сюда карту режимов не тащим.
CSR уместен в кабинете за логином, в мастере, который не должен быть в индексе, в виджете после действия пользователя. На денежном публичном URL это ставка: Google когда-нибудь отрисует страницу через WRS, Яндекс — если включит исполнение JS. Хаб — техническое SEO.
Что Google называет app shell
В справке «Understand JavaScript SEO basics» классический сайт и SSR — HTML ответа уже содержит контент. CSR-приложение часто живёт в модели app shell: исходный HTML не содержит документа, Google видит его только после исполнения скриптов. Все URL с кодом 200 ставятся в очередь рендера (если нет noindex в исходнике). Рендер может занять секунды или ждать ресурсов — публичной нормы «через N часов» нет. Пока страница в очереди, граф ссылок строится из первого HTML: <a href>, не onclick по div.
| Слой | Типичный CSR | Что ломается в поиске |
|---|---|---|
| Первый HTML | Оболочка, бандл, пустой root | Индекс и сниппет могут зафиксировать лоадер |
| Ссылки | Клиентский роутер, часто #/path |
Hash Google отдельным URL не считает; схема AJAX-crawling со #! снята с 2015-го |
| Статус | Сервер всегда 200, «не найдено» рисует JS | Soft 404, см. 404 |
| Состояние | Контент из Local Storage / cookie сессии | WRS чистит storage и cookie между загрузками URL |
| Транспорт | WebSocket / WebRTC как единственный канал данных | Googlebot ходит HTTP-запросами, другого канала нет |
WRS кэширует бандлы агрессивно и может игнорировать Cache-Control: смена логики без смены имени файла — риск отдать роботу старый клиент. Отпечаток в имени — гигиена, не буст. Ссылки, которые JS вставляет в DOM, Google заберёт после рендера, и только если это настоящие <a href>. History API вместо фрагментов — требование справки, не вкус дизайнера: сервер на каждый путь должен отдать хотя бы оболочку того URL, а не 404 «потому что файла нет».
Яндекс без SSR: тумблер, не «оно само»
Если CSR — единственный режим и пререндера нет, справка Вебмастера как раз про этот случай: управление в «Индексирование → Рендеринг страниц JavaScript β». По умолчанию «На усмотрение робота»: он сам решает, исполнять ли код, сравнивая полноту с JS и без. На публичной витрине «на усмотрение» — не архитектура. Пока документа нет в первом HTML, либо включаете исполнение, либо уходите в SSR. Исполнение JS роботом дополнительно грузит ваш origin — это в карточке, не байка.
Отложенная подгрузка каталога: объект window.YandexRotorSettings до DOMContentLoaded, ждать конкретный селектор, не «пять секунд». Разбор ключей — на странице JavaScript SEO, не дублируем таблицу. history.back() без NoJsRedirectsToMain Яндекс воспринимает как переход на главную: в сниппет главной утекает внутренний CSR-экран. Робот — робот Яндекса.
Dynamic rendering (отдельный HTML боту по User-Agent) Google назвал workaround. Он не делает CSR «правильным»: это второй документ для детектора, легко разъехаться с человеком. Долгий путь для индекса — SSR, статика или гидратация одной страницы, не вечный CSR плюс маска бота.
CSR в индексе — это зависимость от второго прохода. Если оффер живёт только после
fetchв браузере, в поиске может остаться оболочка с 200.
Когда оставлять CSR и когда это дыра
Оставляйте клиентский рендер там, где URL не должен сравниваться в выдаче: аккаунт, корзина после логина, внутренний конструктор. Для таких путей честный noindex в исходном HTML (не после JS: Google, встретив noindex в исходнике, может не рендерить страницу — и снять тег скриптом уже поздно).
На денежном URL CSR допустим только как временный долг, если одновременно: History API и сервер отвечает 200 на этот путь; бандлы не закрыты в robots.txt; в Вебмастере включено исполнение JS; в GSC инспектор показывает оффер в отрисованном HTML; несуществующий SKU уходит на URL, который сервер отдаёт 404, либо ставит noindex. Иначе это не «современный фронт», а дыра доступа. Лимит обхода — crawl budget: каждый CSR-URL жрёт crawl и render, не один заход.
Проверка без театра. curl -sS денежного пути: если нет H1 и оффера — вы в CSR. Chrome под вашим профилем не считается. Инспектор URL в Search Console: скачанный vs отрисованный. Вебмастер: «Проверка страницы» с JS и без. Пустая оболочка на проде — постановка SEO-аудита сайта, не задача «подождать индексацию» (индексация Google хранит то, что уже получила).
Ограничение. Нет доли «какой процент CSR в РФ индексируется только после WRS» — не выдумываем. Не обещаем, что «просто включить рендер в Яндексе» равно SSR. CSR не равен плохому LCP сам по себе: скорость для человека — отдельные H1 (LCP/TTFB), этот URL про то, видит ли поиск документ без серверного HTML.
Частые вопросы
CSR и SPA — это одно и то же?
Нет. SPA — способ организации приложения (один шелл, клиентский роутер). CSR — где собирается HTML: в браузере. SPA может отдавать SSR на каждом пути и гидратироваться. Именно клиентский рендер оставляет в первом ответе оболочку.
Если Google умеет JavaScript, достаточно ли CSR для выдачи?
Для Google — ставка на очередь WRS и на то, что бандлы не закрыты. Для Яндекса — ставка на тумблер β. Не все боты исполняют JS. На денежных URL надёжнее документ в первом HTML, то есть SSR или пререндер, не «оно доедет».
Почему в Chrome каталог есть, а в индексе пустая страница?
Робот забрал оболочку, не дождался рендера, упёрся в robots.txt на бандл, получил 200 на пустышке или контент, который живёт только в sessionStorage. Сверяйте отрисованный HTML в инспекторе и curl без JS, не вкладку под своим логином.
Нужен ли CSR на карточке товара?
Как единственный режим — нет: цена, наличие и внутренние ссылки должны быть в ответе сервера. Клиентский слой после SSR (фильтр, галерея) поиску не мешает, если документ уже в HTML. Чистый CSR карточки — долг, который чинят SSR, а не «ещё один fetch».