Когда речь заходит о раздаче статики (картинки, CSS/JS, шрифты, архивы, видеофрагменты), обычно есть два лагеря: «вынесем всё в S3 и прикрутим CDN» и «поставим Nginx на VDS и сделаем нормальный кэш». Оба подхода рабочие — но у каждого свой профиль по цене, сложности, контролю над заголовками, кэшем и рискам на origin.
Ниже разложу выбор максимально практично: как считать egress, где вы проиграете на cache invalidation, какие заголовки критичны, и какие схемы обычно «стреляют» в проде.
Термины: чтобы говорить на одном языке
Мини-словарь, который пригодится дальше:
- S3 — объектное хранилище. Файлы лежат как объекты и выдаются по HTTP(S), но логика — не «файловая система», а ключи/объекты.
- CDN — сеть узлов, которые кэшируют контент ближе к пользователю. CDN обычно забирает данные с origin.
- origin — источник данных для CDN: это может быть S3, ваш Nginx, приложение или их комбинация.
- cache — кэширование на стороне CDN и/или Nginx (
proxy_cache) и/или браузера. - invalidation — «сброс кэша» на CDN: принудительное протухание объектов по пути/шаблону.
- egress — исходящий трафик из точки хранения/раздачи. Важно различать egress из хранилища (до CDN), egress из CDN (до пользователей) и исходящий трафик с VDS.
- headers — заголовки, которые управляют кэшем и поведением клиентов:
Cache-Control,ETag,Last-Modified,Vary,Content-Type,Access-Control-Allow-Originи т.д.
Что вы реально выбираете: управляемость против масштаба «по умолчанию»
Упрощённо выбор выглядит так:
- S3 + CDN — вы покупаете масштабирование, географию и простую долговечность хранения. Взамен платите за egress/запросы и миритесь с тем, что часть поведения задаётся правилами CDN и особенностями origin.
- Nginx на VDS — вы покупаете контроль: любые правила, заголовки, cache key, stale-режимы, кастомные ошибки и полноценные логи. Но масштабирование, отказоустойчивость и ресурсы (CPU, диск под кэш, IOPS, сеть) — уже ваша ответственность.
Часто выигрывает гибрид: хранить исходники в объектном хранилище, а Nginx использовать как «программируемый» слой (origin или защитный/кэширующий прокси). Если вам как раз нужен управляемый серверный слой, проще всего стартовать с VDS и уже от него строить схему раздачи.
Деньги: как считать egress и почему «S3+CDN дешевле» не всегда правда
В обсуждениях стоимости чаще всего путают три разные статьи трафика:
- egress из хранилища до CDN (если тарифицируется);
- egress из CDN до пользователей (почти всегда тарифицируется отдельно);
- исходящий трафик с VDS (обычно это включённый лимит/порт/политика провайдера, но ограничения бывают).
Практический алгоритм оценки:
- Зафиксируйте месячный исходящий трафик на пользователей и пик (ГБ/ТБ в месяц и Мбит/с в пике).
- Оцените ожидаемый hit ratio CDN. Для «правильной» статики (версии в имени + длинный TTL) реальны 95–99%.
- Посчитайте трафик от CDN к origin: примерно (1 − hit ratio) от пользовательского, плюс «мусор» от коротких TTL и частых invalidation.
- Добавьте стоимость запросов/операций (актуально для мелких файлов и некоторых S3-подобных тарифов), а также платные invalidation/правила на CDN (если применимо).
Нюанс: invalidation почти всегда ухудшает экономику. Вы платите и за сам сброс (если он платный), и за «холодный» кэш (CDN чаще ходит на origin, растёт egress и нагрузка).
Подробно про дисциплину версионирования и связь TTL с экономикой кэша можно свериться в материале про версионирование статики для S3/CDN и управление кэшем.
Если вам нужно быстро прикинуть, потянет ли схема Nginx-origin пики по трафику, начните с простой цифры: «сколько запросов/сек и Мбит/с в пике должна выдерживать одна точка origin». Даже с CDN снаружи это важно для моментов «холодного кэша» и релизов.

Кэш и invalidation: где чаще всего ломается прод
Почему invalidation — болезненная кнопка
Если у вас URL вида /app.js и вы выкатываете новую версию «в то же имя», вам приходится одновременно контролировать CDN, браузерные кэши и промежуточные прокси. На практике это выливается в гонки: часть пользователей получает новый HTML, но старые CSS/JS, и интерфейс «разъезжается».
Рабочая стратегия: версионирование вместо purge
Базовый рецепт для статики:
- Файлы с «отпечатком» в имени:
app.9c1f3a2.js,styles.4b10c9.css. - Для таких файлов — долгий TTL:
Cache-Control: public, max-age=31536000, immutable. - HTML (или манифест/entrypoint) — короткий:
Cache-Control: no-cacheили небольшойmax-age+ revalidate.
Тогда invalidation почти не нужен: новый релиз просто публикует новый набор URL, а старый спокойно «доживает» в CDN и браузерах.
Headers: минимальный набор, без которого CDN и браузер будут жить своей жизнью
Ни объектное хранилище, ни Nginx, ни CDN «автоматом» не делают кэш корректным. Всё упирается в заголовки. Наиболее важные:
Cache-Control— главный дирижёр. Для версионированных ассетов — длинныйmax-ageиimmutable. Для HTML —no-cacheили короткий TTL.ETagи/илиLast-Modified— условные запросы (If-None-Match,If-Modified-Since) экономят egress, если объект протухает.Content-Type— иначе ловите неверные MIME-типы, особенно на шрифтах и wasm.Vary— критично, если есть разные варианты поAccept-Encoding(gzip/br) или по другим заголовкам. Ошибка сVaryможет перемешать ответы в кэше.Access-Control-Allow-Origin— нужно для шрифтов, canvas и сценариев, когда домены разные.
Вы выбираете не «S3 или Nginx», а то, где вам проще и надёжнее обеспечить правильные заголовки и предсказуемую политику кэша.
Сценарий 1: S3 как origin + CDN сверху
Один из самых популярных вариантов для «много статики» и небольшой команды.
Плюсы
- Не нужно думать о диске, IOPS, файловой системе, репликации.
- Просто масштабируется по запросам.
- При правильных заголовках hit ratio высокий, origin нагружается мало.
Минусы и подводные камни
- Стоимость egress может стать «налогом на популярность», особенно на тяжёлых файлах.
- Сложнее отлаживать: добавляются правила CDN и нюансы origin, и проблемы приходится «снимать слоями».
- Invalidation при плохом деплое становится регулярной процедурой и обычно обходится дорого.
Кому подходит
Проектам, где статика сильно больше динамики; где важна география; где команда не хочет поддерживать собственный фронт для файлов.
Сценарий 2: Nginx на VDS как origin (и/или как защитный кэширующий слой) + CDN сверху
Вы ставите Nginx на VDS, который раздаёт статику сам или проксирует её из хранилища. CDN при этом кэширует «снаружи».
Плюсы
- Максимальный контроль над заголовками, правилами, редиректами, ошибками и логами.
- Можно сделать «умный кэш» на origin:
proxy_cache,proxy_cache_use_stale, защита от stampede черезproxy_cache_lock. - Проще добавлять защиту: ограничения методов, rate limit, базовую авторизацию для приватных файлов (если нужно).
Минусы
- Вы отвечаете за доступность origin: аптайм VDS, сеть, мониторинг, бэкапы.
- Нужно планировать диск под кэш и следить за его ростом.
- Если CDN настроен плохо (низкий TTL,
no-storeтам, где не надо), нагрузка вернётся на VDS.
Кому подходит
Тем, кому нужны нестандартные требования: сложные правила кэширования, тонкая работа с заголовками, строгая маршрутизация, единые логи, защита origin.
Сценарий 3: только Nginx на VDS (без CDN)
Подход до сих пор хорош, если аудитория в одном регионе, объёмы умеренные, а вы хотите предсказуемую инфраструктуру без внешних зависимостей.
Когда это разумно
- Статика небольшая, а задержка до сервера приемлемая.
- Нужны кастомные правила и минимальная вариативность.
- У вас уже есть серверная площадка, и добавить Nginx проще, чем собирать связку S3+CDN.
Когда будет больно
- Глобальная аудитория и требования по низкой задержке.
- Большие медиафайлы, скачивания, пики по трафику.
Практика Nginx: базовый скелет раздачи статики с правильными headers
Ниже — пример идей, которые чаще всего нужны. Это не универсальный конфиг «скопировал-вставил», а отправная точка для продовой настройки.
server {
listen 80;
server_name static.example;
root /var/www/static;
location / {
try_files $uri =404;
}
location ~* \.(?:css|js|mjs|png|jpg|jpeg|gif|webp|avif|svg|ico|woff2|woff|ttf|eot)$ {
access_log off;
expires 365d;
add_header Cache-Control "public, max-age=31536000, immutable" always;
try_files $uri =404;
}
location = /index.html {
expires -1;
add_header Cache-Control "no-cache" always;
}
}
Что здесь важно:
- Для версионированной статики — длинный TTL и
immutable. - Для HTML —
no-cache, чтобы браузер проверял обновления. - С
alwaysзаголовки применятся более единообразно (включая часть неуспешных ответов, где это поддерживается).

Практика S3: что проверить до подключения CDN
Даже без привязки к конкретной панели есть универсальные пункты:
- Проверьте, что у объектов корректный
Content-Type. Ошибки MIME ломают поведение браузера и кэш. - Убедитесь, что выставляются нужные
Cache-Controlдля разных классов файлов (версионированные ассеты vs HTML/манифест). - Продумайте схему имён: если нет cache busting, вы почти гарантированно попадёте в регулярный invalidation.
- Определите правила приватности: публичные объекты vs выдача по подписи (если требуется).
Origin: как не убить источник при холодном кэше
Когда CDN внезапно перестаёт иметь нужный объект в кэше (после invalidation, после смены TTL, после массового релиза), origin может получить лавину одинаковых запросов.
Для Nginx в роли origin обычно спасают:
- локальный
proxy_cache(если Nginx тянет данные из апстрима); proxy_cache_lockиproxy_cache_use_stale(чтобы не было stampede и чтобы отдавать «старое, но рабочее» при проблемах апстрима);- ограничение параллелизма и rate limiting для особо тяжёлых точек раздачи.
Сводная таблица выбора (кратко)
- S3+CDN: минимум забот и максимум масштаба, но следите за egress и дисциплиной версионирования, иначе invalidation станет «вечной кнопкой».
- Nginx+CDN: максимум контроля над headers/cache/origin, хорошо для сложных правил, но нужны админские привычки: мониторинг, бэкапы, ресурсное планирование.
- Только Nginx на VDS: просто и предсказуемо для умеренных объёмов и локальной аудитории, но без глобальной географии.
Рекомендованные решения под типовые задачи
1) SPA и фронтенд-бандлы
Почти всегда: версионированные ассеты + долгий кэш, HTML — no-cache. Любая схема (S3/CDN или Nginx) будет работать, если заголовки выставлены правильно.
2) Медиа (фото/видео) с большим объёмом
Чаще выигрывает CDN. Вопрос не «нужен ли CDN», а «какой origin»: объектное хранилище проще, Nginx — больше контроля (например, защита от хотлинка, ограничения доступа, собственные правила логирования).
3) Статика с разными политиками доступа и кэша
Здесь обычно удобнее Nginx на VDS как программируемый слой: вы сможете точно управлять заголовками, логированием, правилами и при необходимости проксировать запросы в S3 как в бэкенд-хранилище.
Чек-лист перед запуском: что обязательно измерить и проверить
- Есть ли разделение на классы контента (HTML vs ассеты) и корректные
Cache-Control? - Как вы делаете обновления: invalidation или версионирование URL?
- Какие
Varyреально нужны (минимум — осознанность сAccept-Encoding)? - Понимаете ли вы, где образуется egress (хранилище, CDN, VDS) и что именно оплачивается?
- Что будет при холодном кэше: выдержит ли origin пик?
- Есть ли наблюдаемость: логи, метрики hit/miss, время ответа origin, доля 304?
Если ваш фронтенд на WordPress и вы параллельно оптимизируете скорость загрузки, полезно сопоставить кэш на уровне CDN/статики со стратегиями кеширования сайта в целом: как ускорять WordPress на хостинге и через CDN.
Итог: как принять решение без религии
Выбирайте S3/CDN, когда вам нужен масштаб и простота, и вы готовы дисциплинированно жить с версионированием и учётом egress. Выбирайте Nginx на VDS, когда важны контроль и предсказуемость — заголовки, правила кэша, поведение origin, защита и единая точка диагностики. Если сомневаетесь — начинайте с гибрида: хранение в объектном хранилище, управление выдачей и заголовками через Nginx, а сверху CDN с понятным TTL и минимумом invalidation.
Главное в проде: аккуратная политика кэша, правильные headers и понятная схема обновлений без бесконечных purge.


