← Практика · 31 августа 2026
Почему для большого справочного сайта берут статический генератор, как устроить контент и что именно ломается на десяти тысячах страниц: сборка, поиск, карта сайта и выкладка.
8 минут чтениясайты

Когда страниц десять и когда их десять тысяч — это два разных ремесла. На десяти можно верстать руками. На десяти тысячах любое решение умножается на десять тысяч: лишний запрос к базе, лишняя картинка, лишняя секунда сборки.
Пишу по опыту живого проекта: справочный сайт на Astro, сейчас в нём три с половиной тысячи страниц, растёт дальше. Всё ниже — то, обо что я споткнулся сам.
Для справочного сайта — каталога, словаря, базы кратких содержаний, карточек товаров без корзины — контент меняется редко, а читают его много. Это ровно тот случай, где CMS работает против вас: на каждый заход поднимается PHP, ходит в базу, собирает страницу, которая не менялась полгода. Дальше начинается лечение симптомов: кеш, потом кеш кеша, потом «почему у нас снова слетел кеш».
Статический генератор собирает те же страницы один раз и кладёт готовый HTML. Сервер отдаёт файл. Никакой базы в проде, нечего взламывать, нечему падать под нагрузкой, хостинг — самый дешёвый.
CMS честно выигрывает в двух случаях: контент правят несколько человек без доступа к репозиторию, или страницы персональные (личный кабинет, корзина, цены под пользователя). Если этого нет — статика.
Astro тут удобен тем, что по умолчанию отдаёт ноль килобайт JavaScript, а компоненты можно писать как обычный HTML. На большом справочнике это важнее модных фич: страница должна открываться мгновенно на телефоне в метро.
Главное правило: страница — это данные плюс шаблон, а не файл, который кто-то написал руками. Десять тысяч файлов .astro никто не поддержит.
Данные держите отдельно: JSON, YAML или Markdown с полями. Дальше один динамический маршрут разворачивает их в страницы:
src/
pages/
произведения/[work].astro ← один шаблон на все карточки
авторы/[author].astro
index.astro
data/
работы/*.json ← данные, по файлу на сущность
// src/pages/произведения/[work].astro
export async function getStaticPaths() {
const работы = await Astro.glob('../../data/работы/*.json');
return работы.map((р) => ({
params: { work: р.адрес },
props: { работа: р },
}));
}
const { работа } = Astro.props;
Три вещи, которые стоит решить один раз и не менять:
Адреса. Латиница, транслит, без дат в пути. Адрес страницы — это навсегда: переименование через год означает редиректы, потерянные ссылки и просевший поиск.
Плоская или вложенная структура. Для десяти тысяч страниц лучше плоская с осмысленными разделами (/произведения/имя/, /авторы/имя/), а не пятиуровневая иерархия. Глубина усложняет навигацию, ломает хлебные крошки и делает адреса длинными.
Что считается на сборке, а что лежит в данных. Всё, что можно посчитать заранее — счётчики, связи, «похожие материалы», — считайте один раз скриптом и кладите в данные. Иначе десять тысяч страниц будут пересчитывать одно и то же на каждой сборке.
Сборка растёт линейно, а память — нет. Пока страниц пара сотен, сборка идёт секунды. На тысячах это уже минуты, и главный пожиратель — не HTML, а обработка изображений. Если на каждой странице по картинке через встроенный оптимизатор, вы получите десять тысяч операций с картинками при каждой сборке. Лечится просто: обрабатывайте медиа отдельным скриптом один раз, кладите готовые файлы рядом и подключайте как статику. Правило: в сборке не должно быть ничего, что можно сделать заранее.
Не грузите весь набор данных на каждой странице. Astro.glob по десяти тысячам файлов внутри шаблона страницы — это десять тысяч чтений каталога. Собирайте общие данные один раз в getStaticPaths и передавайте в props только то, что нужно конкретной странице.
Списки нужно резать на страницы. Каталог из десяти тысяч ссылок на одной странице — это мегабайты HTML, которые никто не пролистает. Встроенная пагинация Astro делает это в несколько строк:
export async function getStaticPaths({ paginate }) {
const все = await получитьВсе();
return paginate(все, { pageSize: 50 });
}
Диск кончается раньше терпения. У меня папка сборки на три с половиной тысячи страниц весит 1,2 гигабайта — в основном из-за медиа. На маленьком VPS это половина свободного места. Держите медиа отдельно от сборки и не заливайте их каждый раз заново.
Клиентский поиск по десяти тысячам страниц — отдельная ловушка. Наивный подход «соберём один JSON со всеми текстами» даёт индекс в десятки мегабайт, который браузер честно скачает на телефоне.
Рабочих варианта два.
Резаный индекс. Держите в индексе только заголовки, ключевые слова и адрес — без полных текстов. Для справочника этого обычно достаточно: люди ищут название, а не фразу из середины.
Pagefind. Инструмент, специально сделанный для статических сайтов: после сборки он проходит по готовому HTML и режет индекс на маленькие куски, браузер подтягивает только нужные. Ставится как шаг после сборки:
npx pagefind --site dist
Что бы вы ни выбрали — проверьте вес того, что скачивает браузер. Это единственная метрика, которая тут имеет значение.
Стандарт запрещает больше 50 000 адресов в одном файле. На десяти тысячах вы ещё влезаете, но лучше сразу поставить официальный плагин — он сам разложит карту на части и сделает файл-индекс:
// astro.config.mjs
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://example.ru',
integrations: [sitemap()],
});
Не забудьте site — без него ссылки в карте будут относительными, и поисковик её не примет.
Здесь у большинства и болит. Собранный сайт — это десятки тысяч мелких файлов. Заливать их целиком на обычный хостинг по SFTP — час времени и высокий шанс, что соединение оборвётся на середине.
Заливайте только изменившееся. Если есть SSH — rsync -a --delete dist/ сервер:/путь/. Если хостинг даёт только SFTP, ведите манифест: складывайте контрольные суммы файлов после каждой выкладки и отправляйте лишь те, у которых сумма изменилась. У меня скрипт выкладки принимает список путей и льёт ровно их — обычная правка задевает три-четыре файла вместо десяти тысяч.
Медиа не трогайте. Картинки и аудио заливаются один раз и лежат. Перезаливать их вместе со сборкой — самая частая причина «деплой идёт сорок минут».
Держите в голове, что часть артефактов не пересобирается сама. На большом сайте почти всегда заводятся вещи, которые генерируются отдельно: поисковый индекс, статические листинги, карта. Заведите чек-лист выкладки и проверяйте по нему — иначе через месяц обнаружите, что в поиске нет последних трёхсот страниц.
Если страниц не десять тысяч, а сто тысяч и больше, посмотрите на Hugo: он собирает такие объёмы за секунды, потому что написан на Go, и разница становится решающей. Расплата — менее приятный шаблонизатор.
Если контент меняется ежечасно и ждать сборку нельзя, нужен рендер по запросу с кешем — Next.js и его инкрементальная перегенерация.
Если контент правят не разработчики, добавьте к статике «безголовую» CMS, но оставьте сборку статической: редактор пишет в админке, сборка запускается по кнопке, наружу по-прежнему отдаётся готовый HTML.
Во всех остальных случаях статика скучна, дёшева и работает годами без вашего участия. Для справочника на десять тысяч страниц это ровно то, что нужно.