Запросы проходят через кеш Nginx к серверам приложения
DevOps

Как настроить proxy cache в Nginx и не закешировать личные данные

Настраиваем кеш ответов приложения в Nginx: cache zone, ключ, bypass для авторизации и cookies, TTL, cache lock, проверка HIT/MISS и безопасный откат.

Содержание

Приложение формирует одну и ту же публичную страницу сотни раз в минуту. Nginx может сохранить готовый ответ и вернуть его следующему посетителю без обращения к программе. Это снижает задержку и нагрузку, но ошибка в правилах способна показать одному пользователю данные другого.

Поэтому proxy cache начинают не с размера каталога, а с классификации ответов. Кешировать можно публичные GET/HEAD-страницы, содержание которых одинаково для всех посетителей. Авторизация, сессия, корзина, личный кабинет и административная часть должны обходить общий кеш.

Определите безопасный маршрут

Для примера возьмём публичный каталог /articles/, который приложение отдаёт без входа. До настройки проверьте три вещи:

  • страница не меняется в зависимости от cookie или заголовка Authorization;
  • приложение возвращает правильные Cache-Control, Set-Cookie и коды;
  • устаревший ответ в течение выбранного срока допустим для пользователя.

Если хотя бы одно условие не выполняется, сначала исправьте контракт приложения либо не используйте общий кеш. Nginx не знает смысл данных и не отличит имя пользователя от публичного текста.

Создайте зону и каталог кеша

Директива proxy_cache_path располагается в контексте http, за пределами server. Она задаёт каталог на диске, структуру подпапок, имя зоны метаданных, максимальный размер и срок удаления неиспользуемых записей:

proxy_cache_path /var/cache/nginx/myapp
    levels=1:2
    keys_zone=myapp_cache:10m
    max_size=1g
    inactive=60m
    use_temp_path=off;

keys_zone=myapp_cache:10m выделяет память под ключи и метаданные, а не под тела ответов. Сами файлы лежат в /var/cache/nginx/myapp. inactive=60m удаляет объект, к которому не обращались час, даже если его срок свежести больше. Значения 10 МБ и 1 ГБ — пример, их выбирают по числу URL, размеру ответов и свободному диску.

Проверьте, какой пользователь запускает worker-процессы и может ли пакет Nginx писать в каталог. Не выдавайте 777; создавайте каталог и владельца по правилам вашего дистрибутива.

Отделите запросы с учётными данными

В контексте http создайте переменную, которая равна нулю только при отсутствии Authorization и сессионной cookie. Имя session замените фактическим именем cookie приложения:

map "$http_authorization:$cookie_session" $skip_myapp_cache {
    default 1;
    ":"     0;
}

map вычисляется для каждого запроса. Строка : получается, когда обе части пусты. Любое непустое значение включает обход кеша. Этот пример нельзя переносить на приложение с несколькими cookies доступа, персонализацией по заголовкам или географии без отдельной модели.

Включите кеш только на публичном location

Внутри HTTPS-блока server добавьте настройки к точному публичному маршруту. Две директивы со значением $skip_myapp_cache запрещают соответственно чтение из кеша и сохранение нового ответа:

location /articles/ {
    proxy_pass http://127.0.0.1:3000;

    proxy_cache myapp_cache;
    proxy_cache_methods GET HEAD;
    proxy_cache_bypass $skip_myapp_cache;
    proxy_no_cache $skip_myapp_cache $upstream_http_set_cookie;

    proxy_cache_valid 200 10m;
    proxy_cache_valid 404 1m;
    proxy_cache_lock on;

    add_header X-Cache-Status $upstream_cache_status always;
}

Успешные ответы хранятся десять минут, а 404 — одну минуту. Кеширование 404 снижает повторную нагрузку от ошибочного URL, но задерживает появление только что опубликованной страницы; если это неприемлемо, уберите правило. Значение $upstream_http_set_cookie не позволяет сохранять ответ, в котором приложение установило cookie.

proxy_cache_lock on разрешает одному запросу заполнить отсутствующую запись, пока другие ждут. Это защищает приложение от всплеска при одновременном истечении популярной страницы.

Диагностический заголовок удобен при внедрении. Если он не нужен посетителям, после проверки оставьте $upstream_cache_status только в access log.

Поймите ключ до изменения

Ключ определяет, какие запросы считаются одинаковыми. Стандартный ключ Nginx учитывает схему, адрес upstream и исходный URI вместе с аргументами. Не удаляйте query string ради роста HIT: параметры могут менять язык, фильтр или содержание страницы.

Если приложение использует заголовок Accept-Language или другой параметр представления, общий кеш либо должен включать его нормализованное значение в ключ, либо маршрут не должен зависеть от него. Случайное добавление всех заголовков тоже вредно: число вариантов растёт, а попадания исчезают.

Проверьте MISS, HIT и обход

После успешного nginx -t выполните reload. Затем дважды запросите один публичный URL без cookie и посмотрите диагностический заголовок:

sudo nginx -t
sudo systemctl reload nginx

curl -sS -D - -o /dev/null https://example.com/articles/test
curl -sS -D - -o /dev/null https://example.com/articles/test

Первый ответ обычно содержит X-Cache-Status: MISS, второй — HIT. Значение BYPASS ожидается при условии обхода. EXPIRED или STALE описывает состояние ранее сохранённого объекта. Отсутствующий заголовок означает, что запрос мог попасть в другой location или не использует proxy cache.

Теперь повторите запрос с тестовой cookie. Он не должен получить HIT и не должен повлиять на следующий публичный ответ:

curl -sS -D - -o /dev/null \
  -H 'Cookie: session=test-value' \
  https://example.com/articles/test

Не используйте настоящую сессионную cookie в общей истории терминала. Для полной проверки применяйте тестовую учётную запись и смотрите журнал приложения: запрос с сессией должен дойти до upstream.

Не скрывайте ошибку приложения старым ответом

proxy_cache_use_stale умеет вернуть устаревшую копию при некоторых ошибках upstream. Это повышает доступность публичных страниц, но может скрыть длительный отказ и показать уже неверные данные. Включайте только конкретные условия, определите максимальную допустимую старость и сохраняйте мониторинг самого upstream.

Кеш не заменяет оптимизацию базы и не должен превращать неустойчивое приложение в невидимую проблему. Измеряйте долю HIT/MISS/BYPASS, размер каталога, свободный диск и время ответа upstream.

Обновление и очистка

Бесплатная сборка Nginx не предоставляет универсальную HTTP-директиву purge для любого кеша. Для большинства публичных страниц проще выбрать короткий TTL или менять URL вместе с содержимым. Ручное удаление всего каталога создаёт всплеск запросов к приложению и требует точного пути.

Перед релизом определите, какие страницы могут ждать истечения срока. Если нужен немедленный сброс одного объекта, проектируйте управляемый механизм и проверяйте соответствие его ключа фактическому cache key.

Как вернуть прежнее поведение

Сначала удалите или отключите proxy_cache в нужном location, проверьте конфигурацию и выполните reload. Наличие старых файлов на диске не влияет на ответы, если зона больше не используется:

sudo nginx -t
sudo systemctl reload nginx
curl -sS -D - -o /dev/null https://example.com/articles/test

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

Базовую передачу запроса настройте по статье о reverse proxy Nginx, а клиентский кеш для статических файлов — по руководству о Cache-Control.

Первоисточники

Рекламное местоВаша компания здесьРазместить рекламу

Самопроверка

Проверьте, что материал усвоен

Ответьте на все вопросы. Результат сохранится только в этом браузере и будет учтён в статистике прочитанных материалов.

01Что должно произойти с запросом, содержащим Authorization?
02Для чего нужен proxy_cache_lock?
03Почему cache key должен учитывать query string?

Разбираем коротко

Частые вопросы

Какие ответы нельзя помещать в общий proxy cache?

Не кешируйте персональные страницы, ответы с авторизацией, сессионными cookies, корзину, административные разделы и данные, доступ к которым зависит от пользователя. Для них задайте явный bypass и no_cache.

Чем отличаются proxy_cache_bypass и proxy_no_cache?

proxy_cache_bypass запрещает брать готовый ответ из кеша для текущего запроса, а proxy_no_cache запрещает сохранять полученный ответ. Для авторизованного запроса обычно нужны обе директивы.

Как понять, что ответ пришёл из кеша Nginx?

Временно верните значение upstream_cache_status в диагностическом заголовке и журнале. Первый запрос обычно имеет MISS, а повторный идентичный запрос — HIT, если ответ разрешено кешировать.