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 / CSR | SSR | SSG |
|---|---|---|---|
| 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 полностью выбрасывает этап гидратации. Вместо этого он:
- При SSR сериализует всё нужное состояние, обработчики событий и структуру компонентов прямо в HTML (в виде специальных атрибутов).
- Отправляет клиенту HTML и крошечный загрузчик (Qwikloader, ~1 КБ) — и всё. Страница готова к работе.
- Когда пользователь кликает на кнопку — только тогда Qwik скачивает и выполняет JS-код именно этого обработчика. Ни раньше, ни больше, чем нужно.
Результат: страница становится интерактивной мгновенно, независимо от её сложности. Чем больше приложение — тем заметнее разница с традиционной гидратацией.
React Server Components (Next.js App Router, Remix, Waku)
RSC — это не просто серверный рендеринг компонента. Это принципиально новая модель, в которой серверные и клиентские компоненты живут рядом в одном дереве.
Как это работает:
- Компоненты, не помеченные как клиентские, выполняются только на сервере. Они могут обращаться к базе данных, читать файлы, вызывать внутренние API — всё прямо из компонента.
- Их JavaScript-код не отправляется клиенту вообще.
- При первом открытии страницы клиент получает и обычный HTML, и специальный RSC Payload — сериализованное представление компонентного дерева в формате JSON-подобных строк. Это не просто
JSON.stringify: формат умеет сериализовать промисы, даты, Set, Map от React. - При навигации между страницами (клиентские переходы) сервер отправляет только RSC Payload, без полной HTML-страницы. React на клиенте восстанавливает дерево из этого формата и обновляет DOM — без полной перезагрузки.
Границу между серверным и клиентским кодом определяет директива 'use client'. Компонент с этой директивой и все его дочерние зависимости отправляются клиенту и гидратируются как обычно.
Partial Prerendering (Next.js 15+)
Самый новый подход, объединяющий статику и динамику на уровне одной страницы. Идея простая: всё, что можно отрендерить заранее (шапка, навигация, шаблоны) — рендерится при сборке как SSG. Динамические куски (данные пользователя, персонализация) заворачиваются в Suspense и рендерятся на сервере при запросе, подставляясь в готовый статический «каркас» через стриминг.
Фактически это SSG + SSR на одной странице: статическая оболочка отдаётся из CDN мгновенно, а динамические части подгружаются через streaming SSR.
Как выбирать
Три вопроса, которые дадут ответ:
- Нужен ли вам SEO? Если нет (авторизованная зона, внутренний инструмент) → SPA.
- Часто ли меняется контент и нужна ли персонализация? Если нет → SSG. Если да, но можно терпеть задержку в минуты → ISR. Если нужно мгновенно → SSR.
- Какой у вас бюджет на инфраструктуру? Минимальный → SSG/SPA. Есть ресурсы на поддержку → SSR.
Итог
| SPA / CSR | SSR | SSG | |
|---|---|---|---|
| Лучший для | Приложения за авторизацией, PWA, дашборды | Контентные сайты с динамикой, SEO + уникальные превью | Блоги, лендинги, документация |
| Главная боль | SEO, TTI, og-превью | Стоимость, гидратация, сложность | Динамический контент, долгий билд |
| Инфраструктура | Статический хостинг | Сервер / serverless | Статический хостинг |
| Современный ответ на боли | Пре-рендеринг мета-тегов, SPA с lazy-loading | Streaming SSR, RSC, Edge rendering | ISR, Island Architecture |