Розробка ISR (Incremental Static Regeneration) для сайту
Типова проблема: контент-менеджери оновлюють товари, але відвідувачі бачать старі дані. ISR вирішує це — після публікації в CMS сторінка перегенерується за тегом за секунди. Користувач ніколи не чекає, а пошукові системи індексують свіжі версії. На одному з проектів з 10 000 товарів ми скоротили TTFB з 400 мс до 15 мс, а навантаження на сервер впало на 85 %. Економія на хостингу склала близько $500 на місяць.
Ми впроваджуємо Incremental Static Regeneration (ISR) — підхід, що поєднує швидкість статики зі свіжістю SSR. ISR дозволяє оновлювати окремі сторінки без повної перезбірки сайту: кешований HTML віддається за мілісекунди, а застарілі версії регенеруються у фоні. На відміну від SSR, сервер не навантажується при кожному запиті; TTFB падає в 5–10 разів. Підходить для інтернет-магазинів, блогів і порталів із контентом, що часто змінюється.
Чому розробка ISR краща за SSR для високонавантажених проектів?
Класична модель — Stale-While-Revalidate на рівні сторінок:
- Перший запит до сторінки — рендер на сервері, кешування HTML.
- Повторні запити протягом TTL — віддача з кешу, відповідь за <10ms.
- Запит після закінчення TTL — віддача застарілого кешу (користувач не чекає), запуск фонової регенерації.
- Наступний запит — свіжий HTML з оновленого кешу.
Результат: TTFB як у статики, свіжість контенту як у SSR. ISR віддає кеш за <10ms і знижує навантаження на 70–90%. ISR кращий за SSR: TTFB в 5-10 разів нижчий. Для проектів із тисячами сторінок 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. Детальніше див. у Next.js Documentation on Revalidating.
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 ефективніший за SSG для динамічного контенту, оскільки дозволяє оновлювати сторінки без повної перезбірки.
Стратегії кешування
ISR дозволяє задавати різні TTL для різних типів сторінок. Важливим аспектом є вибір стратегії інвалідації кешу (cache invalidation) та правильне налаштування edge caching на CDN.
| Тип сторінки | TTL | Логіка |
|---|---|---|
| Головна | 60 сек | Часто оновлюється |
| Категорії | 300 сек | Змінюється при додаванні товарів |
| Товари | 3600 сек | Дані стабільні, ціна — окремий запит |
| Статті блогу | 86400 сек | Рідко редагуються |
| Документація | On-demand | Тільки при публікації |
Для розподіленого деплою використовуємо зовнішнє кеш-сховище, наприклад Redis. Для високонавантажених проектів рекомендується використовувати Redis cluster для розподіленого кешування. Приклад кастомного 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 з прогрівом кешу після деплою.
- Моніторинг та оптимізація на основі метрик.
- Документація та навчання команди.
- Підтримка протягом гарантійного періоду.
Процес впровадження розробки ISR та терміни
Етапи роботи:
- Аналіз архітектури та визначення стратегії кешування (1–2 тижні).
- Налаштування ISR на обраному фреймворку (1–3 тижні).
- Інтеграція з CMS через webhook для on-demand ревалідації (1 тиждень).
- Підключення розподіленого кешу (Redis) якщо потрібно (1 тиждень).
- Налаштування CI/CD з прогрівом кешу після деплою (3–5 днів).
- Моніторинг та оптимізація на основі метрик (1–2 тижні).
Терміни: від 2 до 6 тижнів залежно від складності. Вартість розробки ISR починається від $2000 для типових проектів.
Наші інженери працюють з Next.js та Nuxt більше 7 років; ми впровадили ISR на 50+ проектах, включаючи high-load інтернет-магазини. Гарантуємо стабільну продуктивність і прозору підтримку. Отримайте консультацію з вибору стеку та стратегії кешування — зв'яжіться з нами для оцінки вашого проекту.







