Single Page Application (SPA) и SEO: как продвигать

Single Page Application (SPA) и SEO: как продвигать

Single Page Application (SPA) — веб-приложение, которое после первой загрузки подменяет контент в браузере через JavaScript, не запрашивая у сервера новые HTML-страницы. Архитектура удобна для пользователя, но конфликтует с тем, как поисковые роботы видят сайт: в исходном HTML часто нет ни текста, ни ссылок, ни мета-тегов. Этот материал — практическое руководство о том, как сделать SPA индексируемым в Яндексе и Google.

В чём суть проблемы SPA для поиска

Классический сайт отдаёт роботу готовый HTML — он сразу видит весь контент. SPA на React, Vue или Angular отдаёт почти пустой <div id="root"></div> и бандл JavaScript; контент появляется лишь после того, как браузер выполнит скрипты и отрисует DOM. Это и есть client-side rendering (CSR). Краулер же не работает как браузер: он скачивает HTML по коду 200, ставит страницу в очередь на рендеринг и только потом выполняет JavaScript. Если контент не отрисовался или скрипт упал, в индекс попадёт пустая страница.

Различие двух систем принципиально. Google умеет рендерить JavaScript на движке Chromium, но рендеринг — отложенная фаза индексации: тратит crawl budget и не гарантирует полноты. Яндекс рендерит JS хуже и менее предсказуемо: для коммерческого проекта ставка на «робот сам всё отрендерит» — риск выпадения из индекса. Вывод: контент, важный для ранжирования, должен приходить роботу уже в HTML.

Способы рендеринга: что выбрать

Есть четыре базовых подхода — выбор зависит от динамичности контента и ресурсов на бэкенд.

Подход Что отдаётся роботу Кому подходит Минусы
CSR (клиентский) Пустой HTML + JS Закрытые админки, кабинеты Плохая индексация, не для SEO
SSR (серверный) Готовый HTML с сервера Каталоги, маркетплейсы, динамика Нужен Node-сервер, нагрузка
SSG (статика) Заранее собранный HTML Блог, лендинги, докумен­тация Не подходит для частых обновлений
Prerendering Снимок HTML для ботов Готовый SPA без переписывания Контент «замораживается» на момент снимка

Для коммерческого SPA выбирают SSR (Next.js, Nuxt, Angular Universal) на динамичных каталогах и SSG (с гибридом ISR в Next.js) на редко меняющемся контенте. Prerendering (Prerender.io, Rendertron, Puppeteer) спасает без переписывания приложения: роботам по User-Agent отдаётся заранее снятый HTML, пользователям — обычный SPA.

⚠️ РИСК: предрендеринг по User-Agent — пограничная техника. Если боту и человеку отдаётся разный по смыслу контент, это клоакинг, за который Яндекс и Google накладывают санкции. Безопасно — когда HTML для бота и DOM для человека идентичны по содержанию.

Маршрутизация и URL: каждое состояние — отдельный адрес

Главная SEO-ошибка ранних SPA — навигация через хэш: site.ru/#/catalog/divany. Всё, что после #, поиск игнорирует, и весь сайт схлопывается в один URL. Современные фреймворки используют History API (pushState) и дают настоящие ЧПУ-адреса site.ru/catalog/divany. Требования для индексации:

  • Каждая значимая «страница» SPA = уникальный URL с кодом 200; прямой заход на /catalog/divany (без перехода с главной) отдаёт корректный HTML, а не 404.
  • Несуществующий маршрут возвращает честный код 404 или 410, а не «мягкую» 404 со статусом 200.
  • Канонический адрес каждой страницы зафиксирован в rel=canonical.

Подробно о требованиях к адресам — в материале про структуру URL и SEO-friendly адреса.

Мета-теги, которые меняются вместе с маршрутом

В SPA <title>, <meta name="description"> и Open Graph должны переписываться при каждой смене маршрута — иначе все страницы унаследуют мета-теги главной и получат одинаковые сниппеты, что ломает релевантность. В React это решает react-helmet или Metadata API в Next.js, в Vue — useHead. Чек-лист on-page для каждого маршрута:

  • Уникальный Title (50–65 символов, главный запрос в начале).
  • Уникальный Description (140–160 символов с призывом к действию).
  • Один H1 на страницу с главным запросом.
  • Канонический URL и Open Graph для соцсетей.
  • Schema.org через JSON-LD (Product, Article, BreadcrumbList) — обязательно в серверном HTML или предрендере, иначе робот её не увидит. Подробнее — в руководстве по structured data и Schema.org.

Скорость, Core Web Vitals и crawl budget

SPA грузит крупный JS-бандл, и без оптимизации страдают метрики ранжирования. Целевые значения Core Web Vitals: LCP < 2,5 с, INP < 200 мс, CLS < 0,1. JS-нагрузка бьёт по INP и LCP сильнее всего. Помогают code splitting, tree shaking, отказ от тяжёлых библиотек, ленивая загрузка изображений вне первого экрана. SSR/SSG сами по себе улучшают LCP, потому что контент виден до выполнения JS. Глубже — в материале про Core Web Vitals.

Отдельная тема — crawl budget: чем больше страниц робот получает готовым HTML, тем эффективнее тратится бюджет сканирования. Это критично для каталогов на десятки тысяч URL.

Мини-пример: каталог мебели на React

Интернет-магазин на чистом CSR-React: в коде только <div id="root">, весь каталог рисуется в браузере. В Яндекс.Вебмастере карточки висели как «малоценные/пустые», в индекс попадала только главная. Что сделали:

  1. Перевели проект на Next.js с SSR для карточек и SSG для статических разделов.
  2. Заменили хэш-роутинг на History API — каждая категория и карточка получили ЧПУ с кодом 200.
  3. Подключили динамические мета-теги и JSON-LD (Product, BreadcrumbList) в серверный HTML.
  4. Сгенерировали sitemap.xml и отправили в Яндекс.Вебмастер и Google Search Console.
  5. Проверили рендеринг инструментами «Проверка страницы» в Вебмастере и «Проверка URL» в GSC.

После проверки страницы каталога стали отдаваться роботу с полным контентом, статус «пустая страница» ушёл. Цифры роста трафика не приводим — они зависят от семантики и конкуренции ниши, а не только от внедрения SSR.

Типичные ошибки

  • Хэш-роутинг (/#/page) — поиск не видит маршруты после #. Нужен History API.
  • Soft 404 — несуществующий маршрут отдаёт код 200 вместо 404/410.
  • Один комплект мета-тегов на всё приложение — дубли Title и Description.
  • JSON-LD только на клиенте — разметка не попадает в HTML, рич-сниппеты не работают.
  • Блокировка JS и CSS в robots.txt — робот не отрендерит страницу даже там, где мог бы.

О настройке доступа роботов — в материале про robots.txt.

FAQ

Индексирует ли Яндекс JavaScript-сайты?
Частично и непредсказуемо — хуже, чем Google. Для коммерческих SPA нужен серверный рендеринг или предрендеринг, а не расчёт на робота.

SSR или SSG — что выбрать?
SSG — для редко меняющегося контента (блог, лендинги, документация). SSR — для динамики (каталоги, маркетплейсы). Гибрид ISR в Next.js закрывает промежуточные случаи.

Является ли предрендеринг для ботов клоакингом?
Нет, если боту и пользователю отдаётся одинаковый по смыслу контент. Клоакинг — это подмена смысла ради манипуляции выдачей, за это следуют санкции.

Как проверить, что робот видит контент SPA?
«Проверка страницы» в Яндекс.Вебмастере и «Проверка URL» в Google Search Console показывают отрендеренный HTML глазами робота. Дополнительно — просмотр исходного кода (Ctrl+U): если текста там нет, его не видит и краулер без рендеринга.

Материал подготовлен экспертами Chrome Media — агентства SEO-продвижения и технической оптимизации сайтов в Яндексе и Google.

Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *