Разработка ISR (Incremental Static Regeneration) для сайта
Мы внедряем Incremental Static Regeneration (ISR) — подход, сочетающий скорость статики со свежестью SSR. ISR позволяет обновлять отдельные страницы без полной пересборки сайта: кэшированный HTML отдаётся за миллисекунды, а устаревшие версии регенерируются в фоне. В отличие от SSR, сервер не нагружается при каждом запросе; TTFB падает в 5–10 раз. Подходит для интернет-магазинов, блогов и порталов с часто меняющимся контентом.
Типичная проблема: контент-менеджеры обновляют товары, но посетители видят старые данные. ISR решает это — после публикации в CMS страница перегенерируется по тегу за секунды. Пользователь никогда не ждёт, а поисковые системы индексируют свежие версии. На одном из проектов с 10 000 товаров мы сократили TTFB с 400 мс до 15 мс, а нагрузка на сервер упала на 85 %.
Почему ISR лучше SSR для высоконагруженных проектов?
Классическая модель — Stale-While-Revalidate на уровне страниц:
- Первый запрос к странице — рендер на сервере, кэширование HTML.
- Повторные запросы в течение TTL — отдача из кэша, ответ за <10ms.
- Запрос после истечения TTL — отдача устаревшего кэша (пользователь не ждёт), запуск фоновой регенерации.
- Следующий запрос — свежий HTML из обновлённого кэша.
Результат: TTFB как у статики, свежесть контента как у SSR. ISR отдаёт кэш за <10ms и снижает нагрузку на 70–90%. При этом контент остаётся свежим в пределах TTL. Для проектов с тысячами страниц ISR практически не требует серверных ресурсов во время пиков.
Как настроить on-demand revalidation?
TTL-кэш не подходит, когда нужно обновить страницу сразу после изменения в CMS. Для этого используется on-demand revalidation через API:
// app/api/revalidate/route.ts import { revalidateTag, revalidatePath } from 'next/cache'; import { NextRequest, NextResponse } from 'next/server'; export async function POST(request: NextRequest) { const secret = request.headers.get('x-revalidate-secret'); if (secret !== process.env.REVALIDATE_SECRET) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } const { tag, path } = await request.json(); if (tag) revalidateTag(tag); if (path) revalidatePath(path); return NextResponse.json({ revalidated: true }); } Webhook из CMS вызывает этот endpoint при публикации. Мы интегрируем с Contentful, Strapi, WordPress и другими системами.
Реализация в Next.js App Router и Nuxt 3
Next.js
// app/products/[id]/page.tsx interface Props { params: { id: string }; } async function getProduct(id: string) { const res = await fetch(`https://api.example.com/products/${id}`, { next: { revalidate: 300, tags: [`product-${id}`] }, }); if (!res.ok) return null; return res.json(); } export default async function ProductPage({ params }: Props) { const product = await getProduct(params.id); if (!product) notFound(); return <ProductView product={product} />; } export async function generateStaticParams() { const popularProducts = await fetch('https://api.example.com/products?popular=true&limit=100') .then(r => r.json()); return popularProducts.map(({ id }) => ({ id })); } Страницы из generateStaticParams генерируются при сборке. Остальные — при первом запросе и затем регенерируются по TTL.
Nuxt 3 — страница использует useFetch с ключом, а серверный обработчик оборачивается в cachedEventHandler:
// server/api/products/[id].ts export default cachedEventHandler( async (event) => { const id = getRouterParam(event, 'id'); return await $fetch(`https://api.example.com/products/${id}`); }, { maxAge: 300, staleMaxAge: 3600, name: 'product', getKey: (event) => `product-${event.context.params.id}`, } ); Сравнение SSG, SSR и ISR
| Параметр | SSG | SSR | ISR |
|---|---|---|---|
| TTFB | <10ms | 200–500ms | <10ms |
| Свежесть контента | Только при сборке | Всегда свежий | В пределах TTL |
| Нагрузка на сервер | Минимальная | Высокая | Низкая |
| Регенерация | Полная сборка | Нет | Фоновая, постранично |
Стратегии кэширования
ISR позволяет задавать разные TTL для разных типов страниц:
| Тип страницы | TTL | Логика |
|---|---|---|
| Главная | 60 сек | Часто обновляется |
| Категории | 300 сек | Меняется при добавлении товаров |
| Товары | 3600 сек | Данные стабильные, цена — отдельный запрос |
| Статьи блога | 86400 сек | Редко редактируются |
| Документация | On-demand | Только при публикации |
Для распределённого деплоя используем внешнее кэш-хранилище, например Redis. Пример кастомного cache-handler для Next.js
// next.config.ts import type { NextConfig } from 'next'; const nextConfig: NextConfig = { cacheHandler: process.env.NODE_ENV === 'production' ? require.resolve('./cache-handler.js') : undefined, cacheMaxMemorySize: 0, }; // cache-handler.js const redis = require('ioredis'); const client = new redis(process.env.REDIS_URL); module.exports = class CacheHandler { async get(key) { const data = await client.get(key); return data ? JSON.parse(data) : null; } async set(key, data, ctx) { const ttl = ctx.revalidate || 3600; await client.setex(key, ttl, JSON.stringify({ value: data, lastModified: Date.now() })); } async revalidateTag(tag) { const keys = await client.smembers(`tag:${tag}`); if (keys.length) await client.del(...keys); await client.del(`tag:${tag}`); } }; Мониторинг и отладка
Отслеживайте в production: cache hit rate, revalidation duration, stale responses. Мы настраиваем метрики в Grafana. Пример: добавление заголовка x-cache-time через middleware помогает анализировать актуальность кэша.
Что входит в работу
- Аудит текущей архитектуры и производительности.
- Проектирование стратегии кэширования с подбором TTL.
- Реализация ISR на выбранном фреймворке (Next.js, Nuxt 3 или кастомное решение).
- Интеграция on-demand revalidation через webhook CMS.
- Подключение распределённого кэша (Redis) при необходимости.
- Настройка CI/CD с прогревом кэша после деплоя.
- Мониторинг и оптимизация на основе метрик.
- Документация и обучение команды.
- Поддержка в течение гарантийного периода.
Процесс внедрения и сроки
Этапы работы:
- Анализ архитектуры и определение стратегии кэширования (1–2 недели).
- Настройка ISR на выбранном фреймворке (1–3 недели).
- Интеграция с CMS через webhook для on-demand ревалидации (1 неделя).
- Подключение распределённого кэша (Redis) если требуется (1 неделя).
- Настройка CI/CD с прогревом кэша после деплоя (3–5 дней).
- Мониторинг и оптимизация на основе метрик (1–2 недели).
Сроки: от 2 до 6 недель в зависимости от сложности. Стоимость рассчитывается индивидуально.
Наши инженеры работают с Next.js и Nuxt более 7 лет; мы внедрили ISR на 50+ проектах, включая high-load интернет-магазины. Гарантируем стабильную производительность и прозрачную поддержку. Получите консультацию по выбору стека и стратегии кэширования — свяжитесь с нами для оценки вашего проекта.







