ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

Nginx proxy_cache for static: S3 origin, revalidation, cache lock

Пошагово настраиваем Nginx как edge‑кэш для статики с origin в S3: proxy_cache_path и cache key, revalidation через ETag/Last-Modified, cache lock от stampede, stale/background update и диагностика по заголовкам и логам.
Nginx proxy_cache for static: S3 origin, revalidation, cache lock

Зачем кэшировать S3 через Nginx, если S3 и так быстрый

Объектное хранилище по S3-протоколу хорошо подходит для раздачи статических файлов, но в реальном проде часто всплывают нюансы: лишняя задержка из‑за географии, рост стоимости egress при всплесках трафика, лимиты по запросам, а также эффект «стада» (cache stampede), когда после истечения TTL десятки или сотни клиентов одновременно пробивают origin.

nginx proxy_cache позволяет поставить перед S3 локальный edge‑кэш на диске: Nginx отдаёт популярную статику с вашего сервера, а в S3 ходит только при промахе, при revalidation или при прогреве. Это удобно, когда несколько сайтов/приложений используют общий bucket, или вы хотите жёстко контролировать TTL, заголовки и поведение при сбоях.

Ниже соберём практичную конфигурацию: S3 как origin, Nginx как reverse proxy со стабильным cache key, корректной revalidation по ETag/Last-Modified, защитой от stampede через cache lock и предсказуемым stale-поведением.

Архитектура и границы ответственности

Базовая схема: клиент → Nginx → S3. Nginx кэширует ответы (чаще всего 200, иногда редиректы, и в некоторых случаях часть 404), а при истечении TTL может «перепроверять» объект в S3 условными запросами (If-None-Match / If-Modified-Since).

Перед настройкой стоит договориться с самим собой о нескольких вещах:

  • Что именно кэшируем: картинки, CSS/JS, шрифты, архивы, media-сегменты. Для динамики кэш тоже возможен, но это отдельный класс задач.
  • Как инвалидируем: лучший вариант — версионирование в URL (например, app.9f3a1c.js), альтернативный — revalidation по заголовкам.
  • Как различаем варианты: по host, пути, query string, иногда по заголовкам (например, Accept-Encoding).
  • Что делаем при сбоях origin: отдаём stale или честно возвращаем ошибку.

Практическое правило: для статики проще всего жить с «immutable + versioned URLs». Тогда кэш можно держать долго, а revalidation понадобится редко. Если URL не версионирован, ставка на revalidation становится критичной.

Если вы раздаёте статику с отдельного домена, заранее проверьте, что на нём корректно настроены DNS и сертификат. Для собственных доменов удобна регистрация доменов, а для HTTPS — подходящие SSL-сертификаты.

Схема: клиент, Nginx edge-кэш и S3 origin

Базовая конфигурация proxy_cache: зона, путь, ключ

Фундамент начинается в контексте http: объявляем путь кэша, размер метаданных, лимиты и политику очистки.

proxy_cache_path /var/cache/nginx/s3 levels=1:2 keys_zone=s3cache:200m max_size=50g inactive=7d use_temp_path=off;
proxy_temp_path /var/cache/nginx/tmp;

Ключевые параметры:

  • keys_zone=s3cache:200m — память под метаданные (ключи/индекс), а не под сами файлы. Если зона мала, кэш будет чаще «забывать» объекты.
  • max_size=50g — жёсткий потолок по диску под кэш.
  • inactive=7d — если объект не запрашивали 7 дней, его можно удалить при очистке.
  • use_temp_path=off — запись сразу в итоговую структуру кэша (часто меньше лишнего I/O).

Дальше — серверный блок. Ниже пример, где origin — HTTPS endpoint S3 (или S3‑совместимого хранилища). Обратите внимание на Host, keepalive и диагностический заголовок.

server {
  listen 80;
  server_name static.example.com;

  resolver 1.1.1.1 1.0.0.1 valid=300s;
  resolver_timeout 5s;

  location / {
    proxy_http_version 1.1;
    proxy_set_header Connection "";

    proxy_set_header Host bucket-name.s3-region.amazonaws.com;

    proxy_hide_header x-amz-id-2;
    proxy_hide_header x-amz-request-id;

    proxy_cache s3cache;
    proxy_cache_key "$scheme$proxy_host$uri$is_args$args";

    proxy_cache_valid 200 206 1h;
    proxy_cache_valid 301 302 10m;
    proxy_cache_valid 404 5m;

    proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;

    add_header X-Cache-Status $upstream_cache_status always;

    proxy_pass https://bucket-name.s3-region.amazonaws.com;
  }
}

Самый важный момент здесь — proxy_cache_key. В примере ключ включает query string. Это безопасно по корректности, но может резко раздуть кэш, если в URL летят «мусорные» параметры (например, utm_*).

Query string и «размножение» объектов

Для static caching обычно верно одно из двух:

  • query string не используется — тогда убирайте $args из ключа и кэш станет гораздо компактнее;
  • query string — это версия (например, ?v=123) — тогда оставляйте.

Если хотите оставить только «разрешённые» параметры (например, только v), делайте это через map. В отдельной заметке мы разбирали похожую технику на примере картинок и форматов: как нормализовать ключ кэша через map.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Revalidation: проверка свежести по ETag и Last-Modified

Revalidation нужна, когда вы не можете (или не хотите) полностью полагаться на версионирование URL, и вам важно, чтобы Nginx после истечения TTL перепроверял объект в S3 условным запросом. В идеале origin отдаёт ETag и/или Last-Modified, а Nginx использует их для If-None-Match/If-Modified-Since.

Включается это директивой proxy_cache_revalidate. Когда TTL истёк, Nginx попытается получить 304 Not Modified вместо полной перекачки тела.

location / {
  proxy_cache s3cache;
  proxy_cache_valid 200 206 10m;
  proxy_cache_revalidate on;

  proxy_pass https://bucket-name.s3-region.amazonaws.com;
}

Три практических наблюдения:

  • 304 экономит трафик, но это всё равно запрос в S3. При высокой нагрузке доля revalidation-запросов может быть заметна по RPS.
  • ETag в S3 не всегда равен хэшу (особенно при multipart upload). Для revalidation это обычно не важно: вам нужна стабильная «версия», а не криптография.
  • Last-Modified часто достаточно, если вы обновляете объект перезаписью. Но если у вас нестандартный пайплайн (копирование с метаданными, специфические заголовки), проверьте поведение на тестовом bucket.

Cache lock: защита от stampede при истечении TTL

Stampede проявляется в момент, когда популярный объект протух: множество клиентов одновременно получают EXPIRED и начинают пробивать origin. Для S3 это означает всплеск запросов и задержек.

Решение — включить proxy_cache_lock. Тогда один запрос обновляет объект, а остальные ждут результат или получают stale (если вы так настроите).

location / {
  proxy_cache s3cache;
  proxy_cache_valid 200 206 10m;

  proxy_cache_lock on;
  proxy_cache_lock_timeout 10s;
  proxy_cache_lock_age 30s;

  proxy_cache_use_stale updating;

  add_header X-Cache-Status $upstream_cache_status always;

  proxy_pass https://bucket-name.s3-region.amazonaws.com;
}

Как это читать:

  • proxy_cache_lock — включает «замок» на ключ кэша.
  • proxy_cache_lock_timeout — сколько клиент готов ждать lock; после таймаута Nginx может пойти в origin сам (и тем самым частично вернуть stampede), поэтому подбирайте аккуратно.
  • proxy_cache_lock_age — страховка от зависшего lock (например, при проблемах с origin).

Связка proxy_cache_lock + proxy_cache_use_stale updating обычно даёт лучший UX: один запрос обновляет, остальные мгновенно получают старую версию без ожидания.

Stale и background update: как переживать сбои S3 и сеть

Если S3 endpoint на секунду «подвис» или сеть дала потери, превращать это в лавину 502/504 на клиенте чаще всего не хочется. Для этого Nginx умеет отдавать протухший кэш в заданных сценариях.

location / {
  proxy_cache s3cache;
  proxy_cache_valid 200 206 30m;

  proxy_cache_use_stale error timeout invalid_header http_500 http_502 http_503 http_504;
  proxy_cache_background_update on;

  proxy_pass https://bucket-name.s3-region.amazonaws.com;
}

proxy_cache_background_update означает: клиенту можно отдать stale сразу, а обновление выполнить в фоне. Это снижает хвосты latency и делает поведение более «CDN‑похожим».

Для статики отдача stale при ошибках почти всегда лучше, чем «честная» 502. Исключение — когда устаревшая версия критична (например, юридически значимые файлы) и обновления происходят часто без версионирования.

Range/206 и большие файлы: что учитывать

S3 часто используют для видео, архивов и образов, где клиенты активно делают Range‑запросы. Nginx умеет кэшировать ответы 206 Partial Content, но у этого есть цена: один и тот же объект может храниться «кусочками», увеличивая количество файлов в кэше и нагрузку на диск.

Если Range‑трафика много, имеет смысл отдельно протестировать:

  • нужно ли кэшировать 206 вообще или достаточно кэшировать только 200;
  • каковы реальные паттерны клиентов (плееры, докачки, распараллеливание);
  • как быстро растёт кэш по числу объектов и inode.

Тему Range и кэширования больших файлов мы подробно разбирали отдельно: как Nginx ведёт себя с Range и кэшем.

Диагностика proxy_cache: заголовок X-Cache-Status и логи с upstream-таймингами

Диагностика: как быстро понять, что кэш работает

Первое, что стоит добавить — заголовок со статусом кэша:

add_header X-Cache-Status $upstream_cache_status always;

Типовые значения $upstream_cache_status:

  • HIT — отдали из кэша.
  • MISS — сходили в origin и закэшировали.
  • BYPASS — кэш намеренно пропущен (обычно из‑за условий proxy_cache_bypass).
  • EXPIRED — объект протух и пошёл обновляться (часто в паре с revalidation).
  • UPDATING — кто-то обновляет, вы получили stale (при proxy_cache_use_stale updating).

Второй слой — логи. Удобно иметь отдельный log_format для статического прокси, чтобы видеть кэш‑статус, upstream‑статус и тайминги.

log_format s3cache '$remote_addr - $host [$time_local] "$request" $status '
                 'cache=$upstream_cache_status upstream=$upstream_status '
                 'rt=$request_time urt=$upstream_response_time '
                 'uaddr=$upstream_addr';

И применить формат в нужном server:

access_log /var/log/nginx/static-access.log s3cache;

Так вы быстро поймёте, почему вдруг всё стало MISS (ключ «гуляет»), почему слишком много EXPIRED (TTL маловат), и помогает ли revalidation (доля 304 у upstream).

Тонкости S3 origin: Host, SNI и соединения

Для S3‑совместимых endpoint’ов критично корректно задавать Host, потому что маршрутизация часто завязана на виртуальные хосты. Поэтому в примерах есть proxy_set_header Host …. Если провайдер требует path‑style (bucket в пути), меняется только способ формирования URL в proxy_pass, но принцип тот же: запрос должен выглядеть так, как ожидает S3.

Для стабильности соединений полезно:

  • использовать proxy_http_version 1.1 и proxy_set_header Connection "", чтобы keepalive работал предсказуемо;
  • выставить разумные proxy_connect_timeout и proxy_read_timeout (особенно для больших объектов);
  • задать resolver в Nginx, если origin — доменное имя и вы хотите управлять TTL резолва.
FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Частые ошибки и быстрые проверки

Кэш есть, но всегда MISS

Проверьте, не меняется ли ключ: $proxy_host, $uri, $args. Частая причина — мусорные query‑параметры или разные варианты по заголовкам (например, сжатие), которые не учтены одинаково для всех запросов.

Кэш разрастается без контроля

Типовые причины: query string в ключе, кэширование 206 для крупных файлов, слишком большой inactive при большом «ассортименте» объектов. Отдельно следите за inode на файловой системе, а не только за гигабайтами.

Revalidation не даёт экономии

Если объекты часто меняются без версионирования URL, условные запросы будут часто возвращать 200 вместо 304. Смотрите в логах $upstream_status и долю EXPIRED. Иногда проще изменить процесс деплоя статики (versioned filenames), чем пытаться «умно» revalidate’ить всё.

При истечении TTL растёт latency

Почти всегда это лечится включением proxy_cache_lock и proxy_cache_use_stale updating. Это сглаживает нагрузку на origin и убирает «хвосты» задержек.

Сбалансированный пресет для статики с S3

Если нужно быстро получить предсказуемый результат, начните с такого набора (при условии, что статика не зависит от query string):

proxy_cache s3cache;
proxy_cache_key "$scheme$proxy_host$uri";
proxy_cache_valid 200 206 30m;
proxy_cache_valid 404 2m;

proxy_cache_revalidate on;
proxy_cache_lock on;
proxy_cache_lock_timeout 10s;
proxy_cache_lock_age 30s;

proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;

add_header X-Cache-Status $upstream_cache_status always;

Логика простая: не раздуваем кэш $args, включаем revalidation для «мягкого» обновления, добавляем lock+stale updating от stampede и background update для стабильной задержки.

Финальный чек‑лист перед продом

  1. Проверьте права и место на диске под /var/cache/nginx, настройте мониторинг заполнения и inode.
  2. Добавьте X-Cache-Status и отдельный лог‑формат хотя бы на время внедрения.
  3. Решите вопрос с query string: исключить, оставить или нормализовать через map.
  4. Включите proxy_cache_lock и proxy_cache_use_stale updating для горячих объектов.
  5. Если обновления должны быть строгими — внедрите версионирование URL на стороне сборки/деплоя.

При аккуратной настройке связка Nginx + S3 даёт поведение, близкое к CDN: быстрые HIT, мягкие обновления через revalidation и предсказуемую нагрузку на origin за счёт cache lock.

Поделиться статьей

Вам будет интересно

WP-CLI: смена URL в WordPress после миграции, правка двойных слешей, проверка cron и diagnose OpenAI Статья написана AI (GPT 5)

WP-CLI: смена URL в WordPress после миграции, правка двойных слешей, проверка cron и diagnose

После переноса WordPress чаще ломаются не PHP и не веб-сервер, а данные: в базе остаются старые URL, абсолютные ссылки и «кривые» ...
nftables IPv6 firewall: правильный ICMPv6 и Neighbor Discovery без потери сети OpenAI Статья написана AI (GPT 5)

nftables IPv6 firewall: правильный ICMPv6 и Neighbor Discovery без потери сети

IPv6 чаще «падает» не из‑за адресов, а из‑за слишком строгого firewall: блокируют ICMPv6 и ломают Neighbor Discovery, SLAAC и PMTU ...
KVM/QEMU: virtio и tuned — практический тюнинг производительности и latency OpenAI Статья написана AI (GPT 5)

KVM/QEMU: virtio и tuned — практический тюнинг производительности и latency

Пошагово разбираем, как ускорить KVM/QEMU на Linux-хосте: проверить virtio-устройства, включить multiqueue на virtio-net, подобрат ...