Обсудить задачу

Поиск

Начните вводить запрос: ищем по статьям, кейсам и услугам.

навигация Esc закрыть

Что реально ускоряет статический сайт - CDN или кэш браузера

CDN и кэш браузера ускоряют сайт по-разному и решают разные задачи: CDN сокращает расстояние, которое проходит запрос до сервера, а кэш браузера убирает сетевой запрос вообще. На статическом сайте на Astro или Next.js оба механизма настраиваются почти без усилий - если знать, какие заголовки ставить для каких файлов. Ниже - конкретные значения Cache-Control для разных типов файлов и разбор того, что делает за вас хостинг, а что приходится настраивать руками.

По данным CDN-главы Web Almanac 2025 от HTTP Archive, через CDN сейчас отдаётся 35% HTML-контента в вебе - против 33% годом ранее, и рост объясняется в первую очередь бесплатными или встроенными CDN-опциями, которые идут в комплекте с современным хостингом. Формально CDN у сайта уже есть почти всегда, если он развёрнут на Vercel, Netlify или Cloudflare Pages, - вопрос в том, настроено ли кэширование за этим CDN так, чтобы оно реально ускоряло повторные визиты.

В аудитах сайтов на Astro и Next.js мы регулярно видим одну и ту же ошибку: команда переехала на современный хостинг с CDN из коробки, PageSpeed вырос один раз при запуске - и на этом внимание к кэшированию заканчивается. HTML отдаётся с теми же заголовками, что и хэшированные JS-бандлы, или наоборот - хэшированные файлы кэшируются на пять минут, как будто это динамический контент. Дальше разберём, что в этой конфигурации реально экономит секунды, а что - просто копия одной и той же ошибки на разных доменах.

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

CDN (Content Delivery Network): сеть серверов, распределённых географически, которая хранит копии статических файлов сайта ближе к пользователю и отдаёт их без обращения к исходному серверу.

Кэш браузера (HTTP-кэш): локальное хранилище файлов на устройстве пользователя, которое браузер использует вместо повторного сетевого запроса, если заголовки ответа это разрешают.

Чем кэш CDN отличается от кэша браузера

CDN и кэш браузера кэшируют один и тот же файл, но на разных этапах пути запроса, и путать их - причина половины ошибок в настройке.

Edge-кэширование - это копия файла на сервере CDN, физически расположенном ближе к пользователю, чем исходный сервер. Первый запрос из региона идёт до источника и заполняет edge-кэш, все следующие запросы из этого региона получают файл с ближайшего сервера - экономия на времени в пути (RTT), а не на самом запросе: браузер пользователя всё равно делает сетевой запрос, просто более короткий.

Кэш браузера устраняет сетевой запрос полностью. Заголовки ответа - Cache-Control, ETag, Last-Modified - говорят браузеру, можно ли использовать локальную копию файла без обращения к сети вообще, и на сколько долго. Управляют этим поведением именно заголовки ответа: CDN отдаёт файл быстрее, но именно заголовки решают, нужно ли браузеру вообще снова его запрашивать.

stale-while-revalidate: директива Cache-Control, которая разрешает браузеру или CDN отдать устаревшую копию файла мгновенно и в фоне обновить её для следующего запроса - пользователь никогда не ждёт валидации кэша, но и не застревает на старой версии надолго. Подробно механику описывает статья Джейка Арчибальда на web.dev.

Что на статическом сайте можно кэшировать надолго, а что нельзя

Статическая генерация (SSG) даёт два принципиально разных типа файлов, и правило кэширования для них противоположное.

Сборка Astro или Next.js добавляет к имени каждого JS- и CSS-файла хэш содержимого - app.4f2c9f1.js, styles.7890ab.css. Пока содержимое файла не меняется, хэш и, соответственно, URL остаются теми же; при любом изменении кода сборка генерирует новый хэш и новый URL. Это значит, что конкретный URL физически не может измениться - и его можно кэшировать максимально долго, вообще не проверяя актуальность.

HTML-документы работают иначе. У страницы /blog/statya/ всегда один и тот же URL, но после каждого деплоя её содержимое меняется - новый текст, новые ссылки на пересобранные JS-бандлы с новыми хэшами. Если закэшировать HTML так же агрессивно, как хэшированные файлы, пользователь после деплоя продолжит получать старую HTML-страницу, которая ссылается на уже удалённые из CDN файлы сборки, - и сайт в лучшем случае покажет старый контент, в худшем - отдаст 404 на несуществующий более бандл.

Изображения занимают промежуточное положение: если пайплайн сборки хэширует и их имена, к ним применимо то же правило, что и к JS/CSS. Если изображения лежат в public/ без хэша - как обычно бывает с загружаемыми через CMS хero-картинками, - нужен умеренный кэш с ревалидацией, а не бессрочный immutable. Как выбирать формат и размер самих изображений, чтобы они не были узким местом ещё до вопроса о кэше, разобрано в статье про оптимизацию изображений под LCP.

Какие заголовки Cache-Control реально ставить

Конкретные значения важнее общих формулировок вроде “кэшировать статику подольше” - вот таблица, от которой можно отталкиваться на реальном проекте.

Тип файлаЗаголовокПочему
Хэшированные JS/CSS из сборкиCache-Control: public, max-age=31536000, immutableURL меняется при каждом изменении содержимого, поэтому годовой кэш безопасен, а immutable убирает лишние проверочные запросы
HTML-документыCache-Control: public, max-age=0, must-revalidate или max-age=60, stale-while-revalidate=86400Содержимое меняется с каждым деплоем; короткий TTL или SWR не дают показать устаревшую страницу надолго
Изображения без хэша в имениCache-Control: public, max-age=2592000, stale-while-revalidate=604800Меняются редко, но не никогда - 30 дней с фоновым обновлением балансируют скорость и актуальность
ШрифтыCache-Control: public, max-age=31536000, immutableФайл шрифта физически не редактируется постфактум, обновление всегда даёт новый файл
JSON/API-ответы для клиентского фетчаCache-Control: no-store или короткий max-age под конкретный кейсДинамические данные требуют отдельной стратегии, единого правила нет
Как проверить текущие заголовки кэша на своём сайте
curl -I https://example.com/assets/app.4f2c9f1.js
curl -I https://example.com/blog/statya/

В ответе смотрите на строки cache-control, etag и age - последняя показывает, сколько секунд файл уже отдаётся из кэша CDN, не доходя до источника.

ETag и Last-Modified работают как резервный механизм для файлов с более коротким max-age: когда локальная копия устарела, браузер отправляет условный запрос с этим значением, и сервер отвечает 304 Not Modified без пересылки самого файла, если содержимое не изменилось - экономия трафика даже при частой ревалидации.

Что современные платформы делают за вас, а что нет

Deploy-платформы для Astro и Next.js закрывают большую часть этой настройки автоматически, но не всю.

Vercel, Netlify и Cloudflare Pages по умолчанию раздают файлы из папки сборки через собственный CDN и автоматически ставят immutable-кэш на хэшированные JS/CSS/шрифты - эту часть таблицы выше настраивать вручную почти никогда не нужно. Сравнение того, как каждая из трёх платформ устроена на уровне edge-инфраструктуры и деплоя, разобрано в статье Vercel vs Netlify vs Cloudflare Pages.

Что платформы не решают за вас: кэш для изображений, загружаемых динамически через CMS или API, а не собранных статически, - на них нужно вручную выставлять заголовки через конфигурацию хостинга (vercel.json, netlify.toml, Cloudflare Page Rules) или через image-оптимизатор платформы. Инвалидация кэша при частичных обновлениях контента - тоже ручная зона: если сайт использует инкрементальную регенерацию страниц (ISR) или динамические API-роуты поверх статической сборки, нужно явно решать, что происходит со старыми edge-копиями при обновлении данных, - платформа не может угадать это за вас.

Типичные ошибки кэширования, которые заметно снижают скорость

Большинство проблем с кэшем на статических сайтах сводится к нескольким повторяющимся конфигурациям.

Самая частая - слишком долгий кэш на HTML. Команда один раз ставит max-age=31536000 для всего сайта через общее правило в конфигурации хостинга, не разделяя типы файлов, - и после следующего деплоя часть пользователей неделями видит старую версию страницы, потому что и браузер, и CDN считают её ещё актуальной.

Обратная ошибка - не использовать immutable для хэшированных файлов, оставляя дефолтный короткий кэш в пару часов. Формально это не ломает сайт, но каждый повторный визит заставляет браузер заново скачивать файлы, чьё содержимое физически не могло измениться, - чистая потеря скорости без единой причины.

Отдельная категория - отсутствие сжатия. Кэш ускоряет доставку уже переданного файла, но не уменьшает сам файл: без Brotli или Gzip на исходном сервере CDN будет кэшировать и раздавать несжатую версию, и выигрыш от edge-кэширования частично съедается лишним весом каждого ответа. Большинство современных deploy-платформ включают сжатие по умолчанию, но при самостоятельном хостинге за отдельным CDN это нужно проверять явно.

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

Реальный кейс - что меняется после настройки

В типовом проекте миграции маркетингового сайта B2B-компании с WordPress на Astro хостинг был изначально настроен на дефолтные значения Cloudflare Pages без ручной донастройки заголовков для контентных изображений, загружаемых через headless CMS.

После аудита кэша поменяли три вещи: для хэшированных JS/CSS явно прописали immutable вместо дефолтного часового TTL, для HTML выставили stale-while-revalidate=86400 вместо бессрочного кэша, унаследованного от старого правила времён WordPress, а для CMS-изображений без хэша в имени добавили max-age=2592000 с ревалидацией вместо no-cache по умолчанию.

Повторные визиты по CrUX-данным за месяц ускорились заметно: TTFB для вернувшихся пользователей упал с 380 до 40 миллисекунд за счёт кэша браузера на статике, а LCP при повторном визите - с 1,8 до 0,9 секунды. Первый визит новых пользователей почти не изменился: кэш браузера по определению не помогает тем, кто на сайте впервые, - весь эффект получили именно повторные посещения и переходы между страницами. Подробности о том, что теряется и что нужно перепроверить при самом переезде с WordPress, - в статье про миграцию с WordPress на Astro.

Для кого это критично

Правильная настройка кэша особенно заметна на сайтах с высокой долей повторных визитов - блогах, документации, маркетинговых сайтах SaaS-продуктов, куда пользователи возвращаются за новым контентом или для сравнения тарифов перед покупкой. На одностраничных лендингах под холодный платный трафик эффект от кэша меньше, потому что каждый визит - первый, и там важнее оптимизация самого первого запроса. Если помимо кэша нужно разобраться, что ещё тормозит LCP, CLS и INP, этим занимается аудит скорости сайта и Core Web Vitals.

Это также критично для команд, которые недавно мигрировали с WordPress или конструктора на статический стек: старые правила кэширования, унаследованные от прежнего хостинга, часто переносятся как есть и не соответствуют логике хэшированных сборок Astro или Next.js.

Часто задаваемые вопросы

Нужен ли отдельный CDN, если сайт уже хостится на Vercel или Netlify

Нет, отдельно подключать CDN не нужно - обе платформы уже раздают статические файлы сборки через собственную edge-сеть. Отдельный внешний CDN имеет смысл только для специфичных задач: своей edge-логики вне экосистемы платформы или мультиоблачного резервирования, что для большинства маркетинговых сайтов избыточно.

Что произойдёт, если поставить immutable на файл, который может измениться

Браузер и CDN будут отдавать закэшированную версию весь срок max-age, даже если на сервере уже лежит новый файл по тому же URL - пользователь не увидит изменений, пока кэш не истечёт естественным путём. Именно поэтому immutable безопасен только для файлов с хэшем в имени, где смена содержимого гарантированно даёт новый URL.

Как избежать того, что пользователи видят старую версию сайта после деплоя

HTML не должен кэшироваться на длительный срок - короткий max-age или stale-while-revalidate с окном в несколько часов гарантируют, что даже закэшированная версия страницы обновится в течение разумного времени после деплоя. Хэшированные JS/CSS этой проблемы не создают в принципе, потому что старые версии продолжают существовать по старым URL до следующей сборки.

Ускоряет ли CDN сайт с небольшим количеством посетителей

Да, хотя эффект от географического распределения будет меньше заметен, если аудитория сосредоточена в одном регионе. Но у современных deploy-платформ CDN идёт бесплатно в комплекте с хостингом, поэтому вопрос обычно не в том, включать ли его, а только в том, правильно ли настроены заголовки кэша поверх него.

Чем ETag отличается от Cache-Control по назначению

Cache-Control определяет, нужно ли браузеру вообще обращаться к серверу перед использованием локальной копии. ETag работает уже после того, как обращение произошло: это идентификатор версии файла, который сервер сравнивает с версией у клиента и отвечает 304 Not Modified без пересылки содержимого, если файл не изменился. Для файлов с длинным max-age и immutable ETag практически не задействуется - до сервера просто не доходит запрос, который мог бы его проверить.

Что стоит сделать в первую очередь:

  • Проверить заголовки Cache-Control через curl -I для трёх типов файлов - хэшированного JS, HTML-страницы и изображения без хэша
  • Убедиться, что хэшированные файлы сборки отдаются с immutable, а не с дефолтным коротким кэшем хостинга
  • Сократить кэш HTML до stale-while-revalidate вместо бессрочного значения, если оно стояло по недосмотру
  • Проверить, что сжатие Brotli или Gzip включено на уровне сервера, а не только теоретически доступно

Если на вашем сайте CDN уже подключён, а повторные визиты всё равно ощущаются медленными - опишите задачу команде Exceltic.dev. Проверим текущие заголовки кэша по каждому типу файлов и настроим конфигурацию под конкретный стек. Это часть разработки и оптимизации сайтов, которой занимается Exceltic.dev.

Ещё статьи

Все
Связаться удобным способом
WhatsApp Telegram

Прежде чем уйти: оценка задачи бесплатно

Опишите задачу в двух словах, и мы предложим решение, стек и смету со сроками.

Три направления одной командой: разработка программ, веб-разработка и внедрение CRM. Больше 100 проектов, проект ведёт напрямую инженер, на русском и английском.