Кеширование в Nginx: proxy_cache на практике
Разбираем настройку proxy_cache в Nginx 1.25–1.26: какие ответы можно сохранять, как выбрать ключ кеша, не отдавать устаревшие данные после авторизации и найти причины промахов.

Если Nginx проксирует динамический сайт или API, повторная генерация одного и того же ответа может зря занимать CPU и замедлять запросы. В этой практике настроим proxy_cache для публичных GET-запросов, добавим обход кеша для авторизованных пользователей и разберём ошибки, из-за которых сервер продолжает ходить в upstream при каждом обращении. Примеры рассчитаны на open source Nginx 1.25–1.26.
Что именно кешируем
proxy_cache сохраняет ответы upstream на диске и отдаёт их следующим клиентам, пока объект считается свежим. Это полезно для HTML публичных страниц, документации, изображений и GET-запросов API с одинаковым результатом для разных пользователей.
Нельзя без дополнительной логики складывать в общий кеш ответы личного кабинета, корзины, заказов и API, зависящие от cookie или заголовка авторизации. Иначе один пользователь может получить данные другого.
Примечание 1. Ключ кеша — строка, по которой Nginx отличает один сохранённый ответ от другого. Если в ключ не включить важный параметр запроса, разные URL могут ошибочно получить один и тот же объект.
Кеширование в Nginx ускоряет повторные запросы, но не понимает смысл ответа. Без явных правил он не знает, где публичная страница, а где персональные данные.
Три рабочих подхода
На практике встречаются три схемы. Они отличаются не только скоростью, но и риском отдать старые или чужие данные.
| Подход | Что кешируется | Цена решения | Когда выбирать |
|---|---|---|---|
| Общий кеш GET | Ответы с одинаковым содержимым для всех | Нужно исключить cookie, авторизацию и персональные URL | Новости, каталог, документация, публичный API |
| Кеш только по заголовкам ответа | Только ответы, которым приложение разрешило кеширование | Придётся настроить Cache-Control в приложении | Когда правила свежести должны жить в коде |
| Кеш с ручным обходом | Публичные ответы, кроме запросов с cookie или токеном | Нужно поддерживать список исключений | Сайт сочетает публичные и личные разделы |
Для смешанного сайта обычно безопаснее третий вариант: кеш разрешён по умолчанию только для публичных маршрутов, а признаки сессии и авторизации принудительно обходят его.
Базовая настройка proxy_cache
1. Создайте зону кеша
Директива proxy_cache_path задаёт место хранения объектов, имя зоны и ограничения по диску. Имя pages потом используется в proxy_cache pages.
http {
proxy_cache_path /var/cache/nginx/pages
keys_zone=pages:100m
levels=1:2
max_size=5g
inactive=30m
use_temp_path=off;
upstream app_backend {
server 127.0.0.1:8000;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://app_backend;
proxy_cache pages;
proxy_cache_methods GET HEAD;
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
add_header X-Cache-Status $upstream_cache_status always;
}
}
}
В этом примере Nginx хранит кеш в каталоге /var/cache/nginx/pages, использует до 5 ГБ и удаляет объекты, к которым не обращались 30 минут. keys_zone=pages:100m — память под метаданные, а не лимит размера файлов.
Перед перезагрузкой проверьте конфигурацию командой nginx -t. Если проверка успешна, выполните nginx -s reload или используйте штатный способ перезагрузки сервиса в вашей системе. На первом запросе в заголовке X-Cache-Status должно быть MISS, на повторном — HIT.
2. Не смешивайте публичные и приватные запросы
Одного ограничения по методу недостаточно. Запрос GET тоже может возвращать персональные данные. Для сайта с сессиями добавьте обход кеша и запрет сохранения ответа.
map $http_authorization $has_authorization {
default 1;
"" 0;
}
map $http_cookie $has_session_cookie {
default 0;
~*"(?:^|;\s*)(session|sessionid|PHPSESSID)=" 1;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://app_backend;
proxy_cache pages;
proxy_cache_methods GET HEAD;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_bypass $has_authorization $has_session_cookie;
proxy_no_cache $has_authorization $has_session_cookie;
add_header X-Cache-Status $upstream_cache_status always;
}
}
proxy_cache_bypass не позволяет использовать уже сохранённый объект. proxy_no_cache запрещает записывать текущий ответ в кеш. Для защищённого запроса нужны обе директивы: иначе приватный ответ может не попасть в кеш при первом обращении, но уже существующий публичный объект всё равно будет выбран без proxy_cache_bypass.
3. Учитывайте Cache-Control приложения
По умолчанию Nginx анализирует заголовки ответа upstream и может не сохранять ответы с признаками запрета кеширования. Не отключайте это поведение без понимания последствий. Если приложение отправляет Set-Cookie, персональные ответы обычно нужно исключить отдельно, а не заставлять Nginx кешировать их директивой proxy_ignore_headers.
Параметр proxy_cache_valid задаёт запасной срок хранения для кодов ответа. Это не замена корректному Cache-Control: при конфликте правил результат зависит от конкретной конфигурации, поэтому проверяйте реальные заголовки через curl -I.
Когда выбирать каждый подход
Если весь location содержит только публичный контент, базового общего кеша достаточно. Если срок свежести меняется от ответа к ответу, лучше управлять им из приложения через Cache-Control. Если в одном домене смешаны каталог и личный кабинет, выделите приватные маршруты в отдельные location и не полагайтесь только на проверку cookie.
Ключ обычно должен включать схему, метод, хост и полный URI. Не добавляйте в него случайные заголовки: это резко увеличит количество вариантов одного ответа и снизит hit rate. Но если приложение меняет ответ по языку или формату, соответствующий признак должен участвовать в ключе или обрабатываться через корректный Vary.
Что ломается чаще всего
Кеш всегда показывает MISS
Проверьте, что запрос действительно GET или HEAD, ответ не содержит запрета на сохранение, а каталог доступен пользователю, от которого работает Nginx. Значение MISS также нормально для первого запроса после истечения срока или очистки кеша.
Вместо HIT приходит BYPASS
Посмотрите, какие переменные участвуют в proxy_cache_bypass. Частая причина — cookie сессии, которая отправляется браузером на весь домен, включая публичные страницы. Если cookie нужна только личному разделу, ограничьте её область в приложении или вынесите приватные URL в отдельный location.
Кешируется не тот URL
Проверьте значение proxy_cache_key и поведение параметров запроса. Удалять из ключа $args можно только после проверки, что параметры не меняют содержимое. Для страниц с сортировкой, фильтрами и пагинацией полный $request_uri обычно безопаснее.
Изменения на сайте не видны
Старый объект продолжает жить до истечения срока, если приложение не меняет ключ и Nginx не получил команду очистки. Инвалидация кеша — отдельная задача: заранее определите, как публикация новости или изменение товара будет удалять связанные объекты, а не рассчитывайте только на короткий TTL.
Появились ошибки доступа к каталогу
Проверьте владельца и права на путь из proxy_cache_path, наличие свободного места и ошибки в журнале Nginx. Не выдавайте каталогу кеша права записи всем пользователям системы: достаточно доступа процессу Nginx.
Как проверить результат
Проверяйте не только заголовок статуса, но и безопасность содержимого. Выполните два одинаковых запроса без авторизации и сравните MISS и HIT. Затем повторите проверку с cookie сессии и заголовком Authorization: приватный ответ должен получать BYPASS или не сохраняться, а не возвращаться из общего кеша.
curl -I http://example.com/catalog/item-1
curl -I -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' http://example.com/account
curl -I -H 'Cookie: session=YOUR_SESSION_ID' http://example.com/account
Для оценки эффекта снимите до и после хотя бы такие показатели: долю HIT, время ответа клиенту, время ответа upstream, количество запросов к приложению и размер каталога кеша. Единого порога успеха нет: кеш публичной страницы может заметно снизить нагрузку, но не поможет endpoint, который каждый раз получает уникальный URI.
Что делать дальше
Сначала включите кеш на одном публичном маршруте и оставьте X-Cache-Status только для тестового окружения или внутреннего мониторинга. Затем проверьте ответы с cookie, авторизацией, ошибками и query-параметрами. После этого настройте очистку объектов при публикации данных и вынесите метрики hit rate, свободного места и обращений к upstream в мониторинг.
Тест и проверочная: Быстрые ответы с объяснением, почему остальные варианты неверны
Спросить за 10 ₽Источники: NGINX Documentation: ngx_http_proxy_module, NGINX Documentation: proxy_cache_path
Частые вопросы
Какая версия Nginx нужна для этой конфигурации?
Почему первый запрос всегда получает MISS?
Можно ли кешировать ответы API с методом POST?
Что делать, если у меня другая версия Nginx?
Может ли proxy_cache отдать одному пользователю данные другого?
Почему после изменения страницы пользователи видят старую версию?
Что проверить перед включением кеша в проде?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.