SSG: Static Site Generation

Static Site Generation (SSG) — стратегия рендеринга, при которой HTML-страницы генерируются один раз во время сборки проекта и затем отдаются пользователям напрямую с CDN без обращения к серверу.

Зачем нужно

SSG обеспечивает максимально возможную скорость доставки контента: готовый HTML отдаётся с ближайшего CDN-узла без вычислений на сервере. TTFB (Time to First Byte) составляет 10-50 мс против 200-500 мс для SSR. Для сайтов с редко меняющимся контентом SSG — оптимальный выбор: блоги, документация, лендинги, маркетинговые сайты.

Где используется

  • Документация (Docusaurus, VitePress, Nextra)
  • Блоги и контентные сайты (Gatsby, Astro)
  • Лендинги и маркетинговые страницы
  • Portfolio-сайты
  • Next.js getStaticProps для страниц с нечастыми обновлениями

Как работает SSG

npm run build
      ↓
Сборщик выполняет getStaticProps/getStaticPaths
      ↓
Получает данные из CMS / API / файлов
      ↓
Рендерит каждую страницу в HTML-файл
      ↓
Результат: /about/index.html, /blog/post-1/index.html, ...
      ↓
Деплой на CDN (Vercel, Netlify, S3 + CloudFront)
      ↓
Пользователь запрашивает страницу → CDN отвечает HTML мгновенно

Next.js: getStaticProps и getStaticPaths

// pages/blog/[slug].jsx

// Какие страницы генерировать при сборке
export async function getStaticPaths() {
  const posts = await fetchAllPosts; // запрос к CMS/API

  return {
    paths: posts.map(post => ({
      params: { slug: post.slug },
    })),
    fallback: false, // 404 для несуществующих slug
    // fallback: 'blocking' — генерирует новые страницы on-demand
    // fallback: true — показывает loading state, генерирует в фоне
  };
}

// Данные для каждой страницы
export async function getStaticProps({ params }) {
  const post = await fetchPost(params.slug);

  if (!post) {
    return { notFound: true };
  }

  return {
    props: { post },
    // revalidate → превращается в ISR
    // revalidate: 3600, // пересобирать не чаще раза в час
  };
}

export default function BlogPost({ post }) {
  return (
    <article>
      <h1>{post.title}</h1>
      <time>{post.publishedAt}</time>
      <div dangerouslySetInnerHTML={{ __html: post.contentHtml }} />
    </article>
  );
}

Astro — SSG by default

---
// src/pages/blog/[slug].astro
// В Astro все страницы SSG по умолчанию

import { getCollection } from 'astro:content';

export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map(post => ({
    params: { slug: post.slug },
    props: { post },
  }));
}

const { post } = Astro.props;
const { Content } = await post.render;
---

<html>
  <head><title>{post.data.title}</title></head>
  <body>
    <h1>{post.data.title}</h1>
    <Content />
  </body>
</html>

Частые ошибки

  • SSG для часто меняющихся данных — цены, остаток товаров, живые данные; используйте ISR или SSR.
  • Огромное количество страниц при сборке — 100 000 страниц = очень долгий build; используйте fallback: 'blocking' и ISR для генерации on-demand.
  • Забывают про fallback — без настройки fallback новые страницы (добавленные после сборки) будут возвращать 404.

Связанные темы

Ресурсы