Главная/Статьи/SSR-кэширование в NextJS: ускоряем рендеринг в 10 раз

SSR-кэширование в NextJS: ускоряем рендеринг в 10 раз

Разбираем стратегии кэширования Server Components в NextJS 14+. Revalidation, ISR, on-demand revalidation и edge caching.

ДМ
Дмитрий Мещеряков
📅 27 июля 2026 г.📖 4 мин чтения

Кэширование в Next.js — тема, где чаще всего теряют время не из-за сложности механизмов, а из-за непонимания, сколько их. Слоёв несколько: кэш запросов данных, кэш отрендеренных маршрутов, кэш клиентского роутера. Симптом «изменил данные, страница прежняя» может относиться к любому из них, и ищут обычно не в том.

⚠️ Важно

Поведение кэширования по умолчанию менялось между мажорными версиями Next.js — в частности, кэшируется ли fetch без явных параметров и как ведут себя маршруты без динамических функций. Прежде чем полагаться на умолчания, проверьте их в документации своей версии: приёмы из статьи остаются рабочими, а вот значения по умолчанию стоит уточнить.

Практический вывод из этого простой и полезный сам по себе: задавайте стратегию кэширования явно, а не полагайтесь на умолчания. Явный revalidate или явное указание не кэшировать переживут обновление фреймворка; поведение по умолчанию — не обязательно.

Почему кэширование критично для SSR

Server-Side Rendering — это мощно, но дорого. Каждый запрос выполняет рендеринг на сервере: запросы к БД, API, сборка HTML. Без кэширования время ответа растёт линейно с нагрузкой.

NextJS 14+ предлагает несколько уровней кэширования:

  • Request Memoization — дедупликация запросов в рамках одного рендера
  • Data Cache — кэш fetch-запросов между рендерами
  • Full Route Cache — кэш всего HTML-ответа
  • Router Cache — клиентский кэш для навигации
💡 Совет

Правильная стратегия кэширования может ускорить ваш сайт с 2–3 секунд до 50–100 миллисекунд. Это не оптимизация — это необходимость.

Базовый fetch с кэшированием

В NextJS fetch автоматически кэшируется. Настройки через опции:

typescript
// Кэш навсегда (по умолчанию в Server Components)
const data = await fetch('https://api.example.com/data');
// Кэш на 1 час
const data = await fetch('https://api.example.com/data', {
  next: { revalidate: 3600 }
});
// Без кэша
const data = await fetch('https://api.example.com/data', {
  cache: 'no-store'
});

ISR — Incremental Static Regeneration

ISR позволяет обновлять статические страницы без полной пересборки:

typescript
// app/blog/[slug]/page.tsx
export const revalidate = 3600; // Ревалидация каждый час
export async function generateStaticParams() {
  const posts = await getPosts();
  return posts.map((post) => ({ slug: post.slug }));
}
export default async function PostPage({ params }) {
  const post = await getPost(params.slug);
  return <Article post={post} />;
}

Страница генерируется при первом запросе и кэшируется. Через час следующий запрос триггерит фоновую регенерацию.

On-Demand Revalidation

Когда данные меняются, можно инвалидировать кэш вручную:

typescript
// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache';
export async function POST(request: Request) {
  const { secret, path, tag } = await request.json();
  // Проверка секрета
  if (secret !== process.env.REVALIDATION_SECRET) {
    return Response.json({ error: 'Invalid secret' }, { status: 401 });
  }
  // Инвалидация по пути
  if (path) {
    revalidatePath(path);
  }
  // Инвалидация по тегу
  if (tag) {
    revalidateTag(tag);
  }
  return Response.json({ revalidated: true, now: Date.now() });
}
⚠️ Важно

Храните REVALIDATION_SECRET в переменных окружения. Никогда не коммитьте секреты в репозиторий!

Тегирование запросов

Теги позволяют группировать связанные запросы для инвалидации:

typescript
// Помечаем запрос тегом
const posts = await fetch('https://api.example.com/posts', {
  next: { tags: ['posts'] }
});
// Помечаем несколькими тегами
const post = await fetch(`https://api.example.com/posts/${id}`, {
  next: { tags: ['posts', `post-${id}`] }
});
// Инвалидируем все посты
revalidateTag('posts');
// Инвалидируем один пост
revalidateTag('post-123');

Кэширование на Edge

Для максимальной скорости используйте Edge Runtime:

typescript
// app/api/data/route.ts
export const runtime = 'edge';
export async function GET() {
  const data = await fetch('https://api.example.com/data', {
    next: { revalidate: 60 }
  });
  return Response.json(await data.json(), {
    headers: {
      'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=300'
    }
  });
}

Edge-функции работают ближе к пользователю (CDN), что даёт:

  • Меньшую задержку (latency)
  • Быстрый cold start
  • Глобальное распределение

Мониторинг кэша

Добавьте логирование для отладки:

typescript
// lib/cache-logger.ts
export function logCacheStatus(key: string, hit: boolean) {
  if (process.env.NODE_ENV === 'development') {
    console.log(`[Cache] ${key}: ${hit ? 'HIT' : 'MISS'}`);
  }
}

Итоги

  1. По умолчанию — fetch кэшируется навсегда
  2. revalidate — устанавливайте TTL для динамических данных
  3. Теги — группируйте запросы для точечной инвалидации
  4. On-demand — вызывайте из webhooks при изменении данных
  5. Edge — используйте для глобальной CDN-раздачи

Правильное кэширование — это баланс между свежестью данных и производительностью.

Совет «начните с агрессивного кэширования и ослабляйте» стоит уточнить: он верен для контента и неверен для персональных данных. Ошибка в сторону излишнего кэширования на публичной странице означает устаревшую информацию — неприятно, но исправимо. Та же ошибка на странице с данными пользователя означает, что один посетитель увидит данные другого, и это уже инцидент.

Отсюда рабочее правило: начинайте с агрессивного кэширования публичного контента и с полного отказа от кэша для всего, что зависит от пользователя. Ослаблять первое и усиливать второе — по мере того, как разберётесь.

И последнее, что экономит больше всего времени при отладке: определите, какой именно слой отдаёт вам старые данные, прежде чем что-то менять. Кэш данных, кэш маршрута и кэш клиентского роутера дают одинаковый симптом, но лечатся по-разному, а перебор наугад в этой теме особенно затратен.

🚀

Хотите такое же решение?

Настрою окружение под ваш проект, учту специфику инфраструктуры и обучу команду.

Обсудить проект →
Бесплатная консультация · Ответ в течение дня

Комментарии

Система комментариев скоро будет подключена. А пока вы можете написать мне в Telegram или на email.