Кеш прокси в Nginx: рабочие приёмы без ловушек
Прокси-кеш Nginx ускоряет повторные запросы к приложению, но легко начинает отдавать устаревшие страницы или сохранять персональные данные. Разбираем настройки для Nginx 1.27.x, проверку результата и типичные ошибки.

Если Nginx проксирует динамическое приложение, повторные GET-запросы к одним и тем же URL можно обслуживать без обращения к upstream. На практике кеш ломают не сами файлы на диске, а неправильный ключ, cookies, авторизация и отсутствие проверки заголовка Age или собственного статуса. Ниже — рабочие приёмы для Nginx 1.27.x: как включить proxy cache, не сохранить приватный ответ, пережить краткий сбой приложения и понять по заголовкам, что происходит.
Базовая схема proxy cache
Кеш Nginx состоит из зоны метаданных в памяти и файлов с ответами на диске. В proxy_cache_path задаётся зона, каталог и ограничения, а в location — правила, по которым ответы можно сохранять и отдавать повторно.
Фрагмент ниже рассчитан на конфигурацию Nginx 1.27.x. Директива proxy_cache_path находится в контексте http, остальные настройки — внутри нужного location.
http {
proxy_cache_path /var/cache/nginx/app
levels=1:2
keys_zone=app_cache:20m
max_size=2g
inactive=30m
use_temp_path=off;
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://app_backend;
proxy_cache app_cache;
proxy_cache_methods GET HEAD;
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
proxy_cache_valid any 0;
add_header X-Cache-Status $upstream_cache_status always;
}
}
}
Перед запуском проверьте, что пользователь Nginx может создавать и изменять файлы в каталоге кеша. Затем выполните nginx -t и перезагрузите конфигурацию штатной командой вашей системы. Проверка результата простая: первый GET обычно получает X-Cache-Status: MISS, повторный — HIT.
Термин: upstream — сервер или группа серверов, куда Nginx передаёт исходный запрос. Это может быть приложение на Gunicorn, PHP-FPM через FastCGI или отдельный HTTP-сервис.
Ключ кеша: URL — не всегда вся история
По умолчанию Nginx формирует ключ из схемы, метода запроса и полного URI. Для публичного сайта этого часто достаточно, но любой параметр в query string создаёт отдельный объект кеша. URL /catalog?utm_source=a и /catalog?utm_source=b будут расходовать разные записи, даже если приложение отдаёт одну страницу.
Не добавляйте в ключ cookies, Authorization и другие персональные значения без необходимости. Иначе кеш перестанет объединять запросы, а при ошибке в логике можно получить неправильный ответ для другого пользователя. Если вы явно задаёте ключ, безопасный минимальный вариант для публичных GET-страниц выглядит так:
location / {
proxy_pass http://app_backend;
proxy_cache app_cache;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_methods GET HEAD;
add_header X-Cache-Status $upstream_cache_status always;
}
Если в ключ входит $request_method, проверьте совместимость с преобразованием HEAD в GET. Для большинства публичных страниц проще не переопределять ключ без причины и не включать в него метод вручную. Нормализация маркетинговых параметров через map требует отдельной проверки: удалённый параметр не должен менять логику приложения.
Кеш безопасен не тогда, когда в нём есть записи, а тогда, когда вы точно знаете, какие ответы в него никогда не попадут.
Не кешируйте приватные ответы
Самая опасная ошибка — кешировать страницу пользователя только потому, что она пришла с кодом 200. Cookie сессии, заголовок Authorization и методы, меняющие данные, должны исключать ответ из кеша. Для запроса можно запретить использование уже существующей записи через proxy_cache_bypass, а сохранение нового ответа — через proxy_no_cache. Обычно нужны обе директивы.
location / {
proxy_pass http://app_backend;
proxy_cache app_cache;
proxy_cache_methods GET HEAD;
# Не используем кешированный ответ для авторизованных запросов.
proxy_cache_bypass $http_authorization $cookie_session;
# Не сохраняем ответ, если запрос содержит авторизацию или сессию.
proxy_no_cache $http_authorization $cookie_session;
# Ошибки приложения не должны становиться обычными кеш-записями.
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
add_header X-Cache-Status $upstream_cache_status always;
}
Имена cookies зависят от приложения. У WordPress это может быть один набор, у интернет-магазина — другой. Сначала посмотрите реальные заголовки через DevTools или curl -I, затем добавляйте исключения. Наличие любой cookie не всегда означает приватный ответ: некоторые системы используют cookies для аналитики, но решение должно опираться на поведение конкретного приложения.
Три режима обновления и их цена
У кеша нет универсального TTL. Десять минут для каталога могут быть приемлемы, а для страницы с остатком товара — уже слишком долго. Выбирайте режим по цене устаревших данных, а не по удобному круглому числу.
| Приём | Что получает пользователь | Цена и риск | Когда применять |
|---|---|---|---|
| Фиксированный TTL | Кешированный ответ до истечения срока | Простая схема, но данные устаревают до TTL | Статика и страницы, меняющиеся редко |
proxy_cache_lock | Один запрос обновляет запись, остальные ждут | Снижает лавину запросов, но увеличивает ожидание при медленном upstream | Популярные URL после истечения TTL |
| Фоновое обновление | Пользователь получает старую запись, пока Nginx обновляет её | Нужны аккуратные правила устаревания и контроль свежести | Каталог, новости и другие публичные страницы |
| Stale при сбое | Старая запись вместо ошибки upstream | Пользователь видит не самые свежие данные | Когда доступность важнее мгновенной актуальности |
Для популярных публичных URL можно сочетать блокировку и выдачу устаревшего ответа при временной проблеме:
location / {
proxy_pass http://app_backend;
proxy_cache app_cache;
proxy_cache_methods GET HEAD;
proxy_cache_valid 200 5m;
proxy_cache_lock on;
proxy_cache_lock_timeout 5s;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cache-Status $upstream_cache_status always;
}
proxy_cache_lock не ускоряет сам upstream. Он ограничивает количество одновременных запросов, которые пытаются заполнить одну и ту же кеш-запись. proxy_cache_use_stale нужно включать только там, где устаревшая страница лучше ответа 502 или 504.
Почему статус не всегда HIT
Заголовок X-Cache-Status — диагностический, а не гарантия качества кеша. На первом запросе ожидайте MISS, после истечения TTL — EXPIRED, при запрете по условию — BYPASS, при выдаче устаревшей записи — STALE. Статус UPDATING возможен, когда запись обновляется в фоне.
Проверяйте несколько вариантов одного URL:
- анонимный запрос без cookies;
- запрос с cookie сессии;
- запрос с заголовком
Authorization; - URL с разными query-параметрами;
- ответы 200, 404 и ошибка upstream.
Сравнивайте не только статус кеша, но и тело ответа, заголовки Cache-Control, Set-Cookie, время ответа и логи Nginx. Диагностический заголовок не стоит открывать наружу навсегда: после проверки ограничьте его внутренними запросами или удалите.
Что ломается чаще всего
Кеш не растёт
Причина обычно в запрете через Set-Cookie, Authorization, Cache-Control: private или в том, что запрос не входит в список кешируемых методов. Смотрите статус BYPASS и заголовки upstream, а не только размер каталога на диске.
Каждый URL получает MISS
Проверьте, не меняются ли host, схема и query string. Отдельные UTM-параметры, случайные идентификаторы и разные варианты завершающего слеша быстро дробят кеш на мелкие записи.
Пользователь видит старую страницу
Это ожидаемое поведение при положительном TTL. Инвалидация кеша — отдельная тема; здесь важно заранее определить допустимое время устаревания и не использовать длинный TTL для данных, которые меняются после каждого заказа.
После изменения конфигурации сайт отдаёт 502
Сначала выполните nginx -t. Если синтаксис корректен, проверьте доступность upstream, права на каталог кеша и ошибки в error log. Кеш не исправляет недоступное приложение: при неправильном upstream он лишь меняет форму сбоя.
Когда какой приём выбирать
Для публичного каталога начните с фиксированного TTL и исключений для сессий. Для нескольких очень популярных страниц добавьте proxy_cache_lock, чтобы истечение записи не создавало всплеск запросов к приложению. Если краткий простой недопустим, используйте stale-ответ, но отдельно договоритесь с владельцем продукта, сколько минут старая информация приемлема.
Не пытайтесь одним location / кешировать весь сайт. Админка, личный кабинет, корзина, оформление заказа и API с изменяющими данные методами должны иметь отдельные правила или явный запрет кеширования.
Что делать дальше
После включения кеша соберите базовую линию: долю HIT, долю BYPASS, среднее время ответа и количество запросов к upstream. Затем проверьте эти показатели на реальном трафике после истечения TTL. Если доля попаданий низкая, сначала ищите дробление ключа и приватные заголовки, а не увеличивайте размер дискового кеша. Для следующего шага изучите официальную документацию по proxy_cache_purge или механизм обновления данных приложения, если сайту нужна точная инвалидация после публикации.
Проверка рекламы на риски: Что в макете вызовет вопросы ФАС: превосходные степени, сравнения, обещания результата, маркировка.
Спросить за 40 ₽Источники: NGINX documentation: ngx_http_proxy_module, NGINX documentation: ngx_http_map_module
Частые вопросы
Можно ли кешировать все GET-запросы без исключений?
Почему первый запрос всегда получает MISS?
Нужно ли добавлять $request_method в proxy_cache_key?
Что делать, если после включения кеша пользователь видит чужие данные?
Сработает ли эта конфигурация на другой версии Nginx?
Как понять, что кеширование не сработало?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.