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

Зачем кешировать и какие задачи решает Nginx

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

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

Основы: какие типы кешей есть в Nginx

В Nginx несколько механизмов кеширования. Самые распространённые — proxy_cache для обратного прокси и fastcgi_cache для PHP-FPM и других FastCGI-приложений. Есть ещё uwsgi_cache и scgi_cache, но они аналогичны по принципу.

Proxy cache хранит полные HTTP-ответы, вместе с заголовками. FastCGI cache используется, когда Nginx общается с приложением через FastCGI. В конфигурации эти механизмы настраиваются схожим набором директив, но используются в разных контекстах.

Ключевые директивы и их смысл

Для начала стоит понять минимальный набор директив: proxy_cache_path, proxy_cache_key, proxy_cache, proxy_cache_valid, proxy_cache_bypass, proxy_no_cache. Они задают зону хранения, ключ, включение кеша и условия валидности.

proxy_cache_path описывает физическое место хранения и параметры: размер, время жизни, ключи файлов и менеджер. proxy_cache_key определяет, какие части запроса участвуют в формировании ключа кеша. proxy_cache_valid задаёт время хранения ответов по кодам статуса.

Пример базовой конфигурации

Ниже — рабочий пример для случая обратного проксирования, который подойдёт для большинства проектов без сложной логики инвалидации:

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

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_cache one;
        proxy_cache_key "$scheme://$host$request_uri";
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        add_header X-Cache-Status $upstream_cache_status;
    }
}

Эта конфигурация создаёт кеш-зону «one» размером 10 мегабайт для метаинформации и использует до 10 ГБ на диске. Директива add_header поможет визуально понять, с кеша ли отдан ответ.

Как формировать ключ кеша и зачем это важно

Ключ определяет, какие запросы считаются одинаковыми. Чаще всего используют комбинацию схемы, хоста и URI. Но иногда нужны дополнительные части: заголовки, параметры запроса, куки.

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

Примеры ключей

Несколько типичных вариантов ключей:

  • $scheme://$host$request_uri — стандартный для статического контента и простых страниц.

  • $scheme://$host$request_uri|$http_cookie — учитывает куки, подходит для разных версий страницы в зависимости от сессии.

  • $scheme://$host$uri$is_args$args — разделяет параметры запроса, если они важны для ответа.

В продуктиве я часто комбинирую $request_uri и специфичные заголовки, чтобы кешить страницы с A/B-тестами и региональными версиями.

Управление временем жизни кеша и правила валидности

proxy_cache_valid позволяет задавать разные TTL для разных кодов ответа. Это удобно: 200 можно хранить дольше, а 500 или 503 — вообще не кешировать.

Стандартная практика — устанавливать короткий TTL для динамически меняющихся страниц и длинный для неизменяемых ресурсов. Также можно позволять бэкенду управлять временем через Cache-Control заголовки.

Инвалидация и обход кеша

Инвалидация — ключевой момент. В простом сценарии достаточно уменьшать TTL, но это не всегда приемлемо. Есть несколько подходов: отправка PURGE-запросов, использование ключей с версией, bypass по заголовкам.

Direct PURGE удобен для полного удаления записи. Альтернатива — включение в ключ версии, которую меняют при обновлении контента. Это безопасно и просто в автоматизации деплоя.

Пример директив для bypass и purge

Для обхода кеша можно использовать заголовок или GET-параметр. Ниже — пример логики, которая обходит кеш для администратора:

map $http_x_cache_bypass $bypass {
    default 0;
    1       1;
}

server {
    ...
    location / {
        proxy_pass http://backend;
        proxy_cache one;
        proxy_cache_bypass $bypass;
        proxy_no_cache $bypass;
    }

    location /purge {
        allow 127.0.0.1;
        deny all;
        proxy_cache_purge one "$scheme://$host$request_uri";
    }
}

Такую схему я использовал при разработке панели управления сайтом. Админы могли отправлять заголовок X-Cache-Bypass: 1 и немедленно видеть изменения без ожидания TTL.

Параметры хранения и производительность

proxy_cache_path содержит параметры, которые влияют на IO и расход дискового пространства: levels, max_size, inactive, use_temp_path. levels управляет структурой директорий и влияет на число файлов в одной папке.

Для высоких нагрузок рекомендую use_temp_path=off. Это уменьшает количество операций записи в разных местах и ускоряет отдачу кеша. Также стоит выделить отдельный диск для кеша, чтобы избежать конкуренции с логами и базой данных.

Взаимодействие с заголовками Cache-Control и Expires

По умолчанию Nginx уважает заголовки Cache-Control от бэкенда. Это удобно, когда приложение контролирует кэширование. Но иногда нужно переопределять поведение на уровне прокси.

Директивы proxy_ignore_headers и proxy_hide_header позволяют скрыть или игнорировать определённые заголовки. Используйте их аккуратно, чтобы не нарушить логику обновления и не отдать пользователю устаревшие данные.

Мониторинг, логирование и диагностика

Важно отслеживать эффективность кеша. Отладочный заголовок X-Cache-Status показывает, откуда пришёл ответ: HIT, MISS, BYPASS, EXPIRED и пр. Логи можно настроить для записи статуса кеша в access_log.

Также полезно смотреть размер кеша, количество объектов и частоту попаданий. Инструменты вроде Grafana и Prometheus помогут визуализировать метрики, если экспортировать данные из систем мониторинга или самостоятельно парсить логи.

Пример формата логов с информацией о кешировании

Добавьте в конфигурацию формат лога, который включает $upstream_cache_status и $upstream_response_time. Это даст быстрый набор данных для анализа.

log_format cache '$remote_addr - $remote_user [$time_local] '
                  '"$request" $status $body_bytes_sent '
                  '"$http_referer" "$http_user_agent" '
                  'upstream_status=$upstream_status cache=$upstream_cache_status rt=$upstream_response_time';

access_log /var/log/nginx/access_cache.log cache;

Типичные ошибки и как их избежать

Ошибки при настройке кеша чаще всего связаны с неправильным ключом, незнанием того, какие заголовки передаёт бэкенд, и неверной политикой инвалидации. Ещё одна частая проблема — переполнение диска.

Решения просты: сначала тестируйте на staging, проверяйте X-Cache-Status, контролируйте размеры кеша и задавайте лимиты. Настройте ротацию логов и мониторинг свободного места на диске.

Небольшая таблица: важные директивы и их назначение

Директива Назначение
proxy_cache_path Определяет путь, размер и параметры кеш-зоны
proxy_cache Включает кеширование для location
proxy_cache_key Шаблон формирования ключа кеша
proxy_cache_valid TTL для ответов по кодам состояния
proxy_cache_bypass / proxy_no_cache Условия обхода и запрета записи в кеш

Личный опыт: один практический кейс

Однажды на проекте с интенсивным трафиком мы столкнулись с частыми пиками запросов к поиску. Без кеша база падала. Я настроил proxy_cache для поисковых результатов с коротким TTL и добавил версионирование ключа для результатов фильтрации.

В результате среднее время ответа упало в 3 раза, а нагрузка на базу — более чем на 60%. Самое важное было подобрать ключ так, чтобы не кэшировать персонализированные ответы и одновременно агрессивно кэшировать общие запросы.

Практические советы для внедрения

Начните с малого: включите кеш для статических и часто читаемых страниц. Собирайте метрики и смотрите на процент попаданий в кеш. На основе этих данных расширяйте области кеширования и настраивайте инвалидацию.

Документируйте политику кеширования, чтобы команда знала, какие маршруты кешируются и как их обновлять. Автоматизируйте purge при деплое и тестируйте все сценарии на staging перед выходом в прод.

Настройка кеша в Nginx даёт ощутимый эффект, но требует внимания к деталям: ключи, TTL, взаимодействие с заголовками и стратегии инвалидации. Сбалансированный подход позволит ускорить сайт и уменьшить нагрузку, при этом сохранив актуальность данных для пользователей.