SSR, SSG и SPA: полный разбор


Если вы хоть раз участвовали в споре «SSR vs SPA», вы знаете как это бывает: каждый говорит о своём опыте, апеллирует к своему проекту, и в итоге никто никого не убедил. Этот пост — моя попытка разобраться как всё устроено на самом деле. Сам я почти всегда пишу SPA, и в последнее время немного SSG, поэтому могу быть предвзят.

TL;DR: серебряной пули нет. Правильный ответ всегда «зависит от задачи» — и в конце этого поста вы будете точно знать, от чего именно.


Коротко о терминах

Прежде чем сравнивать, разберёмся в понятиях.

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

SSR (Server-Side Rendering) — каждый запрос к серверу возвращает готовый HTML, сгенерированный на лету. Пользователь сразу видит контент, но браузеру всё равно нужно скачать JS и «оживить» страницу — этот процесс называется гидратацией. Next.js, Nuxt, Remix — типичные SSR-фреймворки.

SSG (Static Site Generation) — HTML генерируется один раз на этапе сборки (npm run build), а на сервере лежат готовые файлы. Нет никакого рендеринга в рантайме — только раздача статики. Astro, Jekyll, Hugo, 11ty — это SSG. Этот блог, кстати, написан на Astro.

ISR (Incremental Static Regeneration) — гибрид SSG и SSR. Страницы генерируются статически, но могут перегенерироваться в фоне через заданный интервал или по запросу. Один из ключевых режимов работы Next.js.


Сравнительная таблица

КритерийSPA / CSRSSRSSG
SEO (Google)⚠️ Работает, но с задержками✅ Отлично✅ Отлично
SEO (Yandex, Bing)❌ Плохо✅ Отлично✅ Отлично
Время до первого байта (TTFB)✅ Мгновенный⚠️ Выше из-за серверного рендеринга✅ Минимальный
Время до интерактивности (TTI)❌ Долго (всё зависит от JS)⚠️ После гидратации✅ Быстро
og-превью в соцсетях❌ Нет без хаков✅ Да✅ Да
Динамический контент✅ Нативно✅ Да⚠️ Только через клиентские запросы или ISR
Стоимость инфраструктуры✅ Дёшево (статика)❌ Нужен сервер/serverless✅ Дёшево (статика)
Масштабирование✅ Тривиально (CDN)❌ Нужно думать✅ Тривиально (CDN)
Сложность инфраструктуры✅ Низкая❌ Высокая✅ Низкая

SPA / CSR

Как это работает

Сервер (например, nginx:alpine — весь контейнер с кодом занимает ~40–60 МБ) отдаёт один и тот же index.html на любой URL. Браузер скачивает JS-бандл и React/Vue начинает рендерить интерфейс. Данные подтягиваются через API-запросы уже после этого.

Плюсы

  • Инфраструктура копеечная или вовсе бесплатная. nginx раздаёт статику и держит десятки тысяч одновременных соединений на самом дешёвом сервере. Горизонтальное масштабирование — просто поставить CDN перед сервером.
  • Мгновенные переходы между страницами. После первой загрузки навигация не делает запросов к серверу за HTML — только за данными.
  • Простота деплоя. npm run build → закинул на S3/GitHub Pages/Cloudflare Pages → готово.
  • Идеально для приложений за авторизацией. Поисковики туда не ходят, SEO не нужно — зачем переплачивать за SSR?

Минусы

  • SEO — основная проблема. Google научился рендерить JS, но отправляет страницы в «очередь на рендеринг» (задержка от секунд до недель), что замедляет индексацию. Yandex и Bing справляются значительно хуже. Отдельная проблема — AI-краулеры (Google AI Overviews, ChatGPT и другие LLM-боты) вообще не выполняют JS и видят только исходный HTML. Для публичных сайтов, где важен органический трафик, это реальная проблема.
  • og-превью не работают. Когда кто-то кидает ссылку в Telegram или Twitter, парсер этих сервисов не запускает JS. Он видит пустой <head>. Красивой превьюшки не будет.
  • Долгий Time to Interactive (TTI). Пользователь сначала видит белый экран, потом загружается весь бандл. На медленном интернете или слабом устройстве это может быть неприятно.

Когда выбирать SPA

  • Приложения за авторизацией: дашборды, CRM, ERP, личные кабинеты.
  • Корпоративные инструменты, которыми пользуются 10–1000 человек внутри компании.
  • PWA с оффлайн-режимом.
  • Прототипы и MVP, где скорость разработки важнее SEO.

Практика

Я работал над приватным интернет-магазином, где за авторизацией скрывались все «вкусности». Для мимокрокодилов — только главная и страница «как вступить в клуб». Это классический кейс для SPA.

Проблему с SEO решили банально: прописали все нужные og-теги и мета-теги прямо в index.html. Получилось 50+ строк в <head>, и один <div id="root"> в <body>. Этого хватило и для поисковиков, и для красивых превьюшек в соцсетях — без SSR.


SSG

Как это работает

При сборке фреймворк проходит по всем страницам, рендерит каждую в HTML-файл и сохраняет на диск. На сервере нет никакой логики — только раздача готовых файлов. nginx или CDN справятся с любым трафиком.

Плюсы

  • Максимальная производительность. HTML готов заранее, CDN отдаёт его из ближайшей точки за 20–50 мс. Никакой вычислительной работы на запрос.
  • Дешевизна и надёжность. GitHub Pages, Cloudflare Pages, Netlify дают это бесплатно. Нет серверного кода, который может упасть.
  • Отличный SEO и og-превью — по тем же причинам, что и SSR.
  • Простота. npm run build → папка с готовыми файлами → закинул на S3/GitHub Pages/Cloudflare Pages → готово.

Минусы

  • Динамический контент требует хаков. Хотите показать актуальную цену товара? Придётся либо делать запрос с клиента после загрузки, либо перегенерировать сайт при каждом изменении данных.
  • Долгий билд при большом количестве страниц. Сайт с миллионом статей будет собираться очень долго. ISR решает эту проблему частично.
  • Не подходит для персонализации. Одна страница = один файл = одинаковый контент для всех.

Когда выбирать SSG

  • Блоги, документация, лендинги, портфолио.
  • Маркетинговые сайты, где контент меняется редко.
  • Любой сайт, где контент одинаковый для всех посетителей и меняется редко.

SSR

Как это работает

На каждый запрос сервер генерирует HTML на лету. В мире React это делается через renderToPipeableStream (React 18+) или renderToString (старый способ). Готовый HTML летит к пользователю, браузер его отображает — пользователь видит контент. Затем скачивается JS и происходит гидратация: React «оживляет» уже отрендеренный HTML, навешивает обработчики событий. После этого страница становится интерактивной.

Плюсы

  • Лучший SEO из возможных. Поисковый робот получает готовый HTML без необходимости запускать JS.
  • og-превью работают из коробки. Каждая страница имеет свой уникальный <title>, og:image, og:description.
  • Быстрое отображение контента (FCP). Пользователь видит готовый контент сразу, не дожидаясь загрузки и выполнения всего JS-бандла. TTFB при этом чуть выше, чем у статики (серверу нужно время на рендеринг), но зато First Contentful Paint наступает раньше, чем у SPA.
  • Серверные компоненты (React Server Components) — революция в подходе. Их код исполняется только на сервере и не отправляется клиенту вообще. Никакой гидратации, никакого лишнего JS в браузере. Серверные компоненты могут ходить напрямую в базу данных, к бекенд-сервисам, читать файловую систему.

Минусы

  • Инфраструктура дороже. Нужен постоянно работающий сервер или serverless-функции. На Vercel это может неожиданно влететь в копеечку при скачке трафика.
  • Масштабирование нетривиально. При реализации в лоб (один Node.js процесс, renderToString, без кеширования) 50 мс на рендер × 6 одновременных запросов = вылет из 300 мс таймаута. В реальности задача решается кластеризацией Node.js, кешированием на CDN, ISR, streaming — но это всё требует архитектурной работы.
  • Гидратация — источник боли. Сервер отрендерил HTML → пользователь видит страницу → скачивается JS → React строит виртуальное дерево → сравнивает с HTML → оживляет. В этом промежутке кнопки видны, но не кликаются. На слабых устройствах это ощутимо и бесит. React 18 частично решает это через selective hydration и streaming SSR с Suspense.
  • Сложность разработки. Нужно думать о том, что код запускается и на сервере, и на клиенте. window, document, localStorage — всё это на сервере не существует.

Streaming SSR и прогрессивная гидратация

Современный SSR — не монолитный renderToString, который блокирует поток до готовности всей страницы. React 18 с Suspense и renderToPipeableStream умеет отправлять HTML кусками: сначала шапку и оболочку, потом — по мере готовности — каждый блок контента. Пользователь видит страницу прогрессивно, как при потоковой загрузке. TTFB существенно снижается, потому что серверу не нужно ждать готовности всей страницы — он начинает отправлять данные, как только первый кусок HTML готов.

ISR: лучшее из двух миров

Я сам это пока не пробовал, но звучит очень круто.

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

Когда выбирать SSR

  • Контентные сайты, которые зависят от поискового трафика: e-commerce каталоги, новостные порталы, маркетплейсы.
  • Сайты, где каждая страница имеет уникальный динамический контент + нужны og-превью.

Современные гибридные подходы

Индустрия давно вышла за рамки «выбери одно из трёх». Сегодня лучшие фреймворки комбинируют все три подхода и добавляют новые приёмы.

Islands Architecture (Astro, Fresh)

Представьте обычную HTML-страницу — она полностью статична и весит копейки. Но в ней есть, например, слайдер с фотографиями и форма обратной связи. Вместо того чтобы загружать JavaScript для всей страницы, фреймворк «оживляет» только эти два элемента — «острова» (islands) интерактивности. Остальная страница остаётся чистым HTML без единого байта JS.

Как это работает на практике: Astro (самый популярный представитель) по умолчанию отправляет браузеру ноль JavaScript. Разработчик явно указывает, какой компонент нужно сделать интерактивным, с помощью так называемых «клиентских директив»: client:load (загрузить сразу), client:visible (загрузить когда пользователь доскроллит до элемента), client:idle (загрузить когда браузер будет свободен), client:media (загрузить на определённых размерах экрана). В Astro можно использовать компоненты из React, Vue, Svelte, SolidJS и Preact на одной странице одновременно — каждый «остров» может быть написан на своём фреймворке.

Edge Rendering (Cloudflare Workers, Vercel Edge Functions, Deno Deploy)

Традиционный SSR работает на одном сервере (или нескольких, но обычно в одном дата-центре). Если ваш сервер стоит в Германии, а пользователь сидит во Владивостоке — запрос летит через полмира и обратно, что добавляет сотни миллисекунд только на передачу данных.

Edge Rendering решает это просто: ваш серверный код запускается не в одном месте, а на сотнях CDN-узлов по всему миру. Пользователь из Владивостока получает ответ от ближайшего узла в Азии, пользователь из Парижа — от европейского узла.

Конкретные платформы:

  • Vercel Edge Functions — оптимизированы для Next.js. Удобно, если вы на Vercel.
  • Cloudflare Workers — самая большая сеть из 300+ дата-центров по всему миру. Код выполняется на движке V8 (том же, что и в Chrome). Недавно команда Cloudflare преставила vinext — переписанный Next.js без зависимости от фич Vercel.
  • Deno Deploy — платформа от создателей Deno, ориентированная на TypeScript. Используется фреймворком Fresh.

Важный нюанс: edge-платформы имеют ограничения. Cloudflare Workers работают с ограничением по CPU и памяти (128 МБ), поэтому тяжёлые вычисления (например, рендеринг большой React-страницы с обращениями к БД) могут быть медленнее, чем на выделенном сервере. Edge Rendering лучше всего работает для задач, где не нужно много считать, а нужно быстро отдать ответ.

Resumability (Qwik)

Все обычные фреймворки (React, Vue, Angular) работают по одной схеме: сервер рендерит HTML → браузер скачивает весь JS → фреймворк заново строит дерево компонентов в памяти → навешивает обработчики событий. Этот процесс называется гидратация, и он повторяет на клиенте работу, уже проделанную на сервере.

Qwik полностью выбрасывает этап гидратации. Вместо этого он:

  1. При SSR сериализует всё нужное состояние, обработчики событий и структуру компонентов прямо в HTML (в виде специальных атрибутов).
  2. Отправляет клиенту HTML и крошечный загрузчик (Qwikloader, ~1 КБ) — и всё. Страница готова к работе.
  3. Когда пользователь кликает на кнопку — только тогда Qwik скачивает и выполняет JS-код именно этого обработчика. Ни раньше, ни больше, чем нужно.

Результат: страница становится интерактивной мгновенно, независимо от её сложности. Чем больше приложение — тем заметнее разница с традиционной гидратацией.

React Server Components (Next.js App Router, Remix, Waku)

RSC — это не просто серверный рендеринг компонента. Это принципиально новая модель, в которой серверные и клиентские компоненты живут рядом в одном дереве.

Как это работает:

  1. Компоненты, не помеченные как клиентские, выполняются только на сервере. Они могут обращаться к базе данных, читать файлы, вызывать внутренние API — всё прямо из компонента.
  2. Их JavaScript-код не отправляется клиенту вообще.
  3. При первом открытии страницы клиент получает и обычный HTML, и специальный RSC Payload — сериализованное представление компонентного дерева в формате JSON-подобных строк. Это не просто JSON.stringify: формат умеет сериализовать промисы, даты, Set, Map от React.
  4. При навигации между страницами (клиентские переходы) сервер отправляет только RSC Payload, без полной HTML-страницы. React на клиенте восстанавливает дерево из этого формата и обновляет DOM — без полной перезагрузки.

Границу между серверным и клиентским кодом определяет директива 'use client'. Компонент с этой директивой и все его дочерние зависимости отправляются клиенту и гидратируются как обычно.

Partial Prerendering (Next.js 15+)

Самый новый подход, объединяющий статику и динамику на уровне одной страницы. Идея простая: всё, что можно отрендерить заранее (шапка, навигация, шаблоны) — рендерится при сборке как SSG. Динамические куски (данные пользователя, персонализация) заворачиваются в Suspense и рендерятся на сервере при запросе, подставляясь в готовый статический «каркас» через стриминг.

Фактически это SSG + SSR на одной странице: статическая оболочка отдаётся из CDN мгновенно, а динамические части подгружаются через streaming SSR.


Как выбирать

Три вопроса, которые дадут ответ:

  1. Нужен ли вам SEO? Если нет (авторизованная зона, внутренний инструмент) → SPA.
  2. Часто ли меняется контент и нужна ли персонализация? Если нет → SSG. Если да, но можно терпеть задержку в минуты → ISR. Если нужно мгновенно → SSR.
  3. Какой у вас бюджет на инфраструктуру? Минимальный → SSG/SPA. Есть ресурсы на поддержку → SSR.

Итог

SPA / CSRSSRSSG
Лучший дляПриложения за авторизацией, PWA, дашбордыКонтентные сайты с динамикой, SEO + уникальные превьюБлоги, лендинги, документация
Главная больSEO, TTI, og-превьюСтоимость, гидратация, сложностьДинамический контент, долгий билд
ИнфраструктураСтатический хостингСервер / serverlessСтатический хостинг
Современный ответ на болиПре-рендеринг мета-тегов, SPA с lazy-loadingStreaming SSR, RSC, Edge renderingISR, Island Architecture