Разработка · 7 мин чтения

SSG, SSR и ISR в Next.js: что выбрать для лендинга

У Next.js три режима рендеринга страниц: SSG, SSR и ISR, и для лендинга это не абстрактный технический выбор, а прямое влияние на скорость загрузки, стоимость хостинга и то, насколько быстро на странице появляются актуальные цены или контент из CRM. На практике клиенты редко формулируют задачу в этих терминах, они говорят «сайт должен грузиться быстро» и «цены должны обновляться сами», а дальше уже я решаю, какой режим рендеринга Next.js закрывает оба требования без переплаты за инфраструктуру.

Три модели рендеринга в Next.js: чем SSG, SSR и ISR отличаются друг от друга

В App Router режим рендеринга страницы определяется тем, как вы работаете с данными, а не отдельным флагом в конфиге. Next.js кэширует результат fetch-запроса по умолчанию, и именно от параметров кэширования зависит, получится у вас статика, серверный рендеринг или что-то среднее.

SSG (Static Site Generation) значит, что HTML страницы собирается один раз на этапе сборки проекта. Пользователь получает готовый файл с CDN, без обращения к серверу приложения на каждый запрос.

SSR (Server-Side Rendering) значит, что HTML собирается на сервере при каждом заходе пользователя. Данные всегда свежие, но каждый запрос стоит времени на рендер и нагрузки на сервер.

ISR (Incremental Static Regeneration) - промежуточный вариант: страница отдаётся как статика, но по истечении заданного времени или по внешнему сигналу пересобирается заново в фоне, а пользователи в это время продолжают получать актуальную на тот момент версию.

Для лендинга это редко вопрос «что технически круче», это вопрос того, откуда берутся данные на странице и как часто они меняются. Статичный текст про услуги, отзывы, кейсы - кандидат на SSG. Цены, синхронизированные с 1С или CRM, остатки, персональные предложения по региону - это уже SSR или ISR.

SSG: когда лендингу хватает статической генерации

Для классического одностраничника с описанием услуги, блоком цен, отзывами и формой заявки SSG почти всегда правильный выбор. Страница собирается один раз при деплое, отдаётся с CDN за 50-100 мс в любой точке, куда дотягивается edge-сеть, и не создаёт нагрузки на сервер вообще, потому что после сборки серверу приложения там просто нечего делать.

В App Router это поведение по умолчанию для страниц без динамических сегментов: если внутри компонента нет fetch с явным no-store, Next.js кладёт результат в кэш на этапе сборки.

Форма обратной связи на такой странице не мешает статике: отправка заявки идёт через route handler или внешний вебхук на клиенте, сама HTML-разметка при этом остаётся статичной. Я обычно завожу форму на клиентском компоненте, который дергает API-роут, а тот уже пересылает лид в CRM или в сценарий n8n для дальнейшей маршрутизации по менеджерам.

Минус SSG проявляется, когда контента много и он динамический: если у лендинга 200 страниц под разные города или услуги и цены меняются раз в час, пересборка всего проекта ради одной цифры превращается в лишний CI-прогон на несколько минут при каждом изменении.

SSR: когда лендингу нужны живые данные при каждом заходе

SSR оправдан, если контент страницы завязан на пользователя или на данные, которые нельзя закэшировать заранее. Из практики: лендинг с ценами, которые тянутся напрямую из ERP клиента и обязаны быть точными до рубля в момент показа, гео-зависимый контент с разными предложениями для регионов, определяемыми через middleware по IP, или A/B‑тест, где вариант страницы решается на сервере при каждом запросе.

В коде это выглядит как явный отказ от кэша:

export const dynamic = 'force-dynamic';

export default async function PricingSection() {
  const res = await fetch('https://erp.example.com/api/prices', {
    cache: 'no-store',
  });
  const prices = await res.json();

  return renderPricing(prices);
}

Цена такого подхода в TTFB: у меня на проектах с SSR первый байт отдаётся за 200-800 мс в зависимости от того, сколько времени тратит бэкенд на ответ, против 50-100 мс у статики с CDN. Для лендинга, где решает секунда до отказа посетителя, это ощутимо, поэтому SSR я ставлю только там, где данные реально нельзя предсказать заранее, а не «на всякий случай».

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

ISR: инкрементальная регенерация как компромисс между скоростью и актуальностью

Для большинства лендингов с меняющимся, но не ежесекундным контентом ISR закрывает задачу лучше, чем SSR, и без ручной пересборки, характерной для чистого SSG. Страница отдаётся как статика, а Next.js в фоне пересобирает её заново по таймеру или по внешнему сигналу.

Простой вариант - таймер:

export const revalidate = 3600;

export default async function CasesPage() {
  const res = await fetch('https://cms.example.com/api/cases');
  const cases = await res.json();

  return renderCases(cases);
}

Здесь страница с кейсами или отзывами пересобирается не чаще раза в час, а всё остальное время отдаётся из кэша с той же скоростью, что и у SSG. На практике я ставлю revalidate от 60 секунд для витрины услуг с ценами, которые двигаются раз в день, до нескольких часов для блока с кейсами и статьями.

Второй вариант - on-demand-регенерация по сигналу, когда содержимое обновляется не по расписанию, а по событию: изменили цену в CRM, опубликовали кейс в CMS. Тогда вместо ожидания таймера дергается ручка ревалидации:

import { revalidatePath } from 'next/cache';

export async function POST(request) {
  const { secret, path } = await request.json();

  if (secret !== process.env.REVALIDATE_SECRET) {
    return new Response('Invalid secret', { status: 401 });
  }

  revalidatePath(path);
  return Response.json({ revalidated: true, path });
}

Этот эндпоинт я обычно вызываю не руками, а сценарием в n8n: срабатывает вебхук из CRM при изменении цены, n8n дергает ручку ревалидации, страница пересобирается за секунды, а до этого момента посетители видят прежнюю статику без задержек на рендер.

SSG, SSR и ISR на лендинге: сравнение по практическим параметрам

Критерий SSG SSR ISR
Когда собирается HTML Один раз при сборке При каждом запросе При сборке, затем по таймеру или сигналу
TTFB на практике 50-100 мс с CDN 200-800 мс, зависит от бэкенда 50-100 мс, кроме момента пересборки
Нагрузка на сервер приложения Почти нулевая после сборки Растёт линейно с трафиком Всплеск только во время регенерации
Актуальность данных До следующего деплоя Всегда свежие С задержкой до интервала revalidate или до сигнала
Подходит для Описание услуг, статичные блоки, отзывы Персонализация, гео-контент, live-цены из ERP Цены, обновляемые не ежесекундно, каталог кейсов

Стоимость хостинга от режима рендеринга тоже зависит напрямую: чистая статика уходит на CDN почти бесплатно, а SSR и ISR требуют постоянно работающего сервера приложения или serverless-функций, которые считаются по вызовам, и на трафике в несколько тысяч заходов в сутки эта разница в счёте за хостинг становится заметной уже в первый месяц.

Как я выбираю режим рендеринга для конкретного лендинга

Первый вопрос, который задаю себе на старте проекта: есть ли на странице данные, которые нельзя показать вчерашними без потери смысла для посетителя. Если нет, беру SSG и не усложняю. Если цены или контент обновляются по расписанию заказчика, например раз в сутки после выгрузки из 1С, ставлю ISR с revalidate под этот цикл. SSR оставляю только под персонализацию или гео-логику, потому что там правда нельзя предсказать ответ заранее.

Отдельно смотрю на объём страниц. Лендинг из одной посадочной с формой заявки почти всегда SSG, а вот сайт-каталог услуг на полсотни страниц под разные города логичнее собирать через generateStaticParams с ISR поверх, чтобы не пересобирать весь проект ради правки в одном городе.

Если смежная часть проекта не про рендеринг, а про интеграции - прием оплаты, передача заявок в CRM, синхронизация с СДЭК по доставке - решаю это отдельно от режима рендеринга через route handlers и внешние сценарии в n8n, не смешивая транспортную логику с выбором SSG или SSR. Когда клиенту нужен именно лендинг на Next.js с продуманной архитектурой рендеринга под его данные, я беру такие проекты в разработку и сразу закладываю нужный режим под конкретные источники данных, а не универсальный SSR «на всякий случай».

Частые вопросы

Можно ли сочетать SSG и ISR на одном лендинге?

Да, и на практике это обычная ситуация. Главная страница и текстовые блоки идут как чистая статика без revalidate, а страница с ценами или каталогом кейсов получает свой revalidate отдельно. Next.js не требует единого режима для всего проекта, он назначается на уровне сегмента маршрута.

Что выбрать, если лендинг на Next.js состоит всего из одной формы заявки?

SSG. Форма отправки заявки работает через клиентский компонент и route handler независимо от того, статична остальная страница или нет, так что смысла держать весь лендинг на SSR ради одной формы нет, это только увеличит TTFB без всякой пользы.

Как ISR ведёт себя при деплое на Vercel и на своём сервере?

На Vercel ISR работает из коробки через встроенный слой кэширования и serverless-функции, регенерация происходит прозрачно. При самостоятельном хостинге на Node.js-сервере тот же revalidate тоже работает, но кэш живёт в файловой системе или в настроенном внешнем хранилище, и на это стоит закладывать время при настройке инфраструктуры, особенно если сервер перезапускается или масштабируется на несколько инстансов.

Влияет ли выбор SSG, SSR или ISR на SEO лендинга?

Напрямую поисковики индексируют готовый HTML, и все три режима отдают полностью отрендеренную разметку, так что сама по себе индексация не страдает ни в одном из вариантов. Но SSG и ISR дают более стабильный и быстрый TTFB, а скорость загрузки входит в факторы ранжирования, поэтому на лендинге, где SEO-трафик важен, я стараюсь избегать SSR без необходимости именно из-за скорости, а не из-за индексации как таковой.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно