Кэширование в Apache — тема с историей, но с приходом Apache 2.4 стек стал гибче: mod_cache выполняет политику кэширования, а провайдеры (cache_disk и cache_socache) хранят объекты на диске или в памяти/внешнем socache. Ключевую роль в производительности играет CacheQuickHandler: этот флаг определяет, будет ли кэш обслуживать запрос ещё до большинства хуков (переписывания, авторизации), или же отдача из кэша произойдёт позже, после стандартной обработки. Разберёмся, как это устроено на практике и какие режимы выбирать под разные нагрузки.
Как устроен mod_cache и откуда берётся выигрыш
Архитектурно mod_cache принимает решение: кэшировать ли ответ и можно ли отдать кэш вместо запроса к бэкенду. Для этого используется ключ кэша (обычно URL со схемой/хостом/портом/путём и строкой запроса плюс вариации по заголовкам из Vary). Факт кэшируемости определяется HTTP-заголовками (Cache-Control, Expires, ETag, Last-Modified) и локальными правилами (CacheIgnore..., CacheStore...). Провайдер хранит объект и метаданные: cache_disk — в файловой системе, cache_socache — в общем кэше (shared object cache: shmcb, memcache, redis и т.п.).
Выгода простая: если вы отдаёте статические файлы, проксируете до приложения или API — кэш способен сократить TTFB и резко уменьшить RPS на бэкенд. В микрокэш-сценарии (1–5 секунд) удаётся погасить всплески и дедуплицировать одинаковые запросы.
CacheQuickHandler: быстрый путь или «по правилам»
CacheQuickHandler определяет, где именно в конвейере Apache будет отдан кэш:
- On — кэш отрабатывает максимально рано, до большинства модулей (переписывание URL, авторизация, директории и т.д.). Это самый быстрый вариант: меньше оверхеда, минимум файловых операций, чаще всего — лучший TTFB и RPS.
- Off — кэш участвует позже. Так вы сохраняете предсказуемость сложных цепочек с
mod_rewrite, авторизацией,mod_security, пользовательскимиSetEnvIfи пр. Цена — небольшое падение производительности.
Правило: включайте
CacheQuickHandler Onв простых ветках (публичный контент, картинки, статические файлы, CDN-ориджин). Отключайте (Off) там, где безопасность и логика критичнее микровыгрышей скорости: авторизация, сложные переписи, индивидуальные куки.
Подводные камни CacheQuickHandler
- Если для ресурса требуется авторизация, ранний кэш теоретически может отдать публичный ответ раньше проверки. Решение —
CacheQuickHandler Offна уровне нужного<Location>или<Directory>. - Сложные правила с
mod_rewriteи условиями на заголовки/переменные могут не сработать при раннем возврате кэша. Если логика переписывания влияет на кэш-ключ, отключайте QuickHandler в этой ветке. - При отладке «хит/миссов» учитывайте, что ранний кэш может обойти часть логов. Настройте отдельный формат логирования для заметок кэша, чтобы видеть реальный статус запроса.
cache_disk vs cache_socache: что выбрать
Роль провайдера кэша — быстро и надёжно хранить закэшированные объекты. В Apache 2.4 основных два:
cache_disk
- Постоянство: переживает перезапуски сервера; можно держать десятки и сотни гигабайт.
- Универсальность: хорошо для больших объектов (изображения, архивы) и долгих TTL.
- Стоимость: IO медленнее RAM, чувствителен к мелким объектам и глубокой структуре каталогов (настроить
CacheDirLevels/CacheDirLength).
cache_socache
- Скорость: хранение в забэкенженном socache (shmcb, memcache, redis). Отличный выбор для микрокэша (1–10 секунд).
- Ограничения размера: разумный потолок для каждого объекта; большие файлы — не сюда.
- Жизненный цикл: в памяти исчезает при рестарте (если shmcb). Memcached/Redis — сохраняют кэш до очистки/TTL, но это внешний сервис.
Итого: диск — для больших и «долго живущих» объектов; socache — для быстрых короткоживущих ответов, сглаживания пиков и микрокэширования динамики.

Типовые сценарии
1) Публичные статические файлы
Большие TTL, предсказуемые ключи, нет авторизации. Идеален cache_disk с CacheQuickHandler On. На небольших проектах на виртуальном хостинге часто достаточно правильно выставить TTL и заголовки (Cache-Control: public, max-age=...).
2) Микрокэш динамики
PHP/FCGI/прокси-апп отдаёт одинаковые ответы для большинства неавторизованных пользователей. Кэшируем на 1–5 секунд в cache_socache, включаем блокировку (CacheLock) против «стаи» одновременно идущих запросов.
3) API с Vary
Ответ зависит от Accept или Accept-Encoding — убедитесь, что бэкенд выставляет Vary. Это критично для правильного ключа кэша и отсутствия «склейки» ответов.
4) Авторизованные области
Обычно не кэшируем. Если требуется кэширование части ответов, изолируйте контекст, используйте CacheQuickHandler Off, внимательно работайте с Set-Cookie, Cache-Control: private/no-store и тестируйте, что персонализация не попадёт в общий кэш.
Базовая подготовка: модули и глобальные параметры
Убедитесь, что задействованы нужные модули: mod_cache, mod_cache_disk или mod_cache_socache, а также mod_headers, mod_expires при необходимости. На Debian/Ubuntu:
a2enmod cache cache_disk headers expires
systemctl reload apache2
Либо для cache_socache и выбранного провайдера (shmcb, memcache, redis):
a2enmod cache cache_socache socache_shmcb headers
# или с провайдером memcache/redis, если установлен соответствующий модуль
# a2enmod socache_memcache
# a2enmod socache_redis
systemctl reload apache2
Настройка cache_disk: надёжный дисковый кэш
Пример VirtualHost с публичным статическим контентом. Включим быстрый обработчик, кэширование по префиксу и разумные лимиты размеров файлов.
<VirtualHost *:80>
ServerName static.example
CacheQuickHandler On
CacheEnable disk /
# Корень кэша, глубина каталогов, длина сегмента
CacheRoot /var/cache/apache2/mod_cache_disk
CacheDirLevels 2
CacheDirLength 2
# Размеры объектов
CacheMinFileSize 1
CacheMaxFileSize 104857600
# Блокировка от «стаи»
CacheLock On
CacheLockPath /var/lock/apache2/mod_cache_lock
CacheLockMaxAge 5
# При ошибке бэкенда можно отдать устаревшее
CacheStaleOnError On
# Игнорируем отсутствие Last-Modified, если контент всё равно публичен
CacheIgnoreNoLastMod On
# Не кэшировать ответы, запрещённые политикой клиента
CacheIgnoreCacheControl Off
# Заголовки клиентского кэширования (если приложение не задаёт)
<Location /assets/>
ExpiresActive On
ExpiresDefault "access plus 30 days"
Header set Cache-Control "public, max-age=2592000, immutable"
</Location>
DocumentRoot /var/www/static
</VirtualHost>
Ключевые моменты:
CacheRootвынесите на быстрый диск и мониторьте размер.CacheDirLevels/CacheDirLengthподбираются эмпирически: слишком мелко — перегрузка каталогов, слишком глубоко — много inode.CacheStaleOnErrorпригоден для фронтов поверх нестабильных бэкендов: лучше старый ответ, чем 5xx.
Настройка cache_socache: микрокэш на памяти/внешнем k/v
Для короткого TTL и высоких RPS подходит cache_socache. Выберите провайдера: shmcb (общая память ОС), memcache или redis при наличии соответствующих модулей. Если поднимаете memcached/Redis на отдельном узле, это удобно делать на VDS — так проще масштабировать фронтенды горизонтально.
<VirtualHost *:80>
ServerName app.example
# В микрокэше часто хотим быстрый путь
CacheQuickHandler On
# Включаем кэш в socache
CacheEnable socache /
# Выбор провайдера socache (один из вариантов)
# shmcb: сегмент общей памяти (указать файл и размер сегмента в байтах)
CacheSocache shmcb:/var/cache/apache2/socache_shmcb(33554432)
# memcache: внешний memcached кластер
# CacheSocache memcache:127.0.0.1:11211
# redis: внешний Redis (при наличии модуля)
# CacheSocache redis:127.0.0.1:6379
# Лимиты на размер объекта (микрокэш маленьких ответов)
CacheSocacheMinObjectSize 1
CacheSocacheMaxObjectSize 262144
# Блокировка от «стаи» и устаревшее при ошибке
CacheLock On
CacheLockMaxAge 5
CacheStaleOnError On
# Секундные TTL применяем со стороны приложения или заголовками ниже
<Location /api/public/>
# Только публичные ответы без персонализации
Header unset Set-Cookie
Header set Cache-Control "public, max-age=3, s-maxage=3, stale-if-error=30"
</Location>
# Проксирование до приложения (пример)
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>
Примечания:
shmcbживёт только в рамках одного процесса/узла и очищается при рестарте. Это честный RAM-кэш, быстрый и простой.- При memcache/redis кэш переживёт рестарт Apache и может быть общим для фронтендов.
- Лимиты на объект защищают от попытки хранить большие ответы в RAM.
- Строго избегайте кэширования персонализированных ответов с
Set-CookieиCache-Control: private. В примере мы удаляемSet-Cookieтолько для зоны, где гарантированно нет персонализации.

Vary, ключ кэша и канонизация
Ключ кэша зависит от URL и набора заголовков из Vary. Практические советы:
- Всегда выставляйте
Vary: Accept-Encodingдля компрессии, если она включена. Иначе возможна подмена gzip/identity. - Если ваш API действительно зависит от
Acceptили других заголовков — явно добавляйте их вVary. - Стабилизируйте URL: избегайте «мусорных» параметров в строке запроса. При необходимости канонизируйте ключ с помощью
CacheKeyBaseURLтам, где фронтенд и бэкенд видят разные хосты/схемы.
Подробно о выборе TTL, директивах Cache-Control и ETag мы писали в материале как правильно выставлять Cache-Control и ETag. Для больших файлов пригодится разбор кэширования Range-запросов.
Контроль кэшируемости: заголовки и директивы
- Приложение должно говорить, можно ли кэшировать:
Cache-Control,Expires,ETag,Last-Modified. - Apache может помочь, когда приложение «молчит»: используйте
mod_expiresиmod_headersдля установкиCache-Control/Expires. - Нельзя кэшировать то, что помечено
no-storeили персонализированоSet-Cookieбез строгой изоляции. В крайнем случае —CacheIgnoreHeaders Set-Cookieтолько в заведомо публичных локациях. - Директивы
CacheIgnoreNoLastModиCacheIgnoreCacheControlаккуратно: не ломайте политику клиентов и кэш-семантику ответа.
Защита от «стаи»: CacheLock
Когда много клиентов одновременно запрашивают один и тот же несуществующий в кэше ключ, бэкенд получает «лавину». Включайте CacheLock On. Первый запрос «запирает» ключ, остальные ждут. Это особенно важно в микрокэше.
Диагностика и наблюдение
- Смотрите на заголовок
Age: ненулевой — ответ из кэша или с реконфирмацией поIf-None-Match/If-Modified-Since. - Уточняйте, срабатывает ли кэширование по пути: временно задайте короткие TTL, проверяйте разницу в TTFB/CPU на бэкенде.
- Логируйте статусы кэша: добавьте заметки в формат access log и анализируйте HIT/MISS/REVALIDATE. В тестовой среде полезно временно включить подробный уровень логирования модулей кэша.
Рецепт: смешанный режим — диск для статики, socache для микрокэша
Нередко идеальна комбинация: статические файлы и крупные объекты на диске; короткие ответы динамики — в socache на несколько секунд.
<VirtualHost *:80>
ServerName mixed.example
# Статика: диск
CacheQuickHandler On
CacheEnable disk /assets/
CacheRoot /var/cache/apache2/mod_cache_disk
CacheDirLevels 2
CacheDirLength 2
CacheMaxFileSize 104857600
# Динамика: микрокэш в памяти (шаред-мемори)
CacheEnable socache /api/public/
CacheSocache shmcb:/var/cache/apache2/socache_api(67108864)
CacheSocacheMaxObjectSize 131072
# Общие опции
CacheLock On
CacheLockMaxAge 5
CacheStaleOnError On
# Политики для каждой зоны
<Location /assets/>
ExpiresActive On
ExpiresDefault "access plus 30 days"
Header set Cache-Control "public, max-age=2592000, immutable"
</Location>
<Location /api/public/>
Header unset Set-Cookie
Header set Cache-Control "public, max-age=5, s-maxage=5, stale-if-error=60"
Header append Vary "Accept,Accept-Encoding"
</Location>
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>
Когда отключать CacheQuickHandler
- Внутри зон с авторизацией:
<Location /private/> CacheQuickHandler Off </Location>, чтобы аутентификация и ACL сработали до кэша. - Где
mod_rewriteменяет путь/параметры и это влияет на ключ кэша. - При тонком контроле заголовков через
SetEnvIf/RequestHeader, которые должны выполниться до решения о кэше.
Тонкая настройка для высокой нагрузки
- Выберите адекватный storage: быстрые SSD для
cache_disk; достаточно RAM и размер сегмента дляshmcb; отдельный кластер memcached/Redis для горизонтального масштабирования. - Правильно задайте лимиты размера объектов. Большие бинарники держите на диске, API-ответы — в памяти.
- Следите за тем, что попадает в кэш: включите отчётность, раз в сутки анализируйте топ-ключи, TTL, соотношение HIT/MISS.
- Используйте короткие TTL и
stale-if-error/CacheStaleOnErrorвместо «вечного» кэша. Это повышает устойчивость при релизах и временных деградациях бэкенда.
Частые ошибки и как их избежать
- Кэшируются персональные страницы. Симптом: пользователи видят чужие данные. Решение: исключить такие зоны из кэша, отключить QuickHandler, не игнорировать
Set-Cookie, не сниматьprivate/no-store. - Смешение gzip и не-gzip. Добавьте
Vary: Accept-Encoding, проверьте, что сжатие включено последовательно. - Лавина запросов при истечении TTL. Включите
CacheLock, используйте короткие TTL и «скользящее» обновление (revalidate), где возможно. - Медленный кэш на диске. Проверьте IO, размер каталога, параметры
CacheDirLevels/CacheDirLength, вынесите кэш на отдельный SSD.
Итоги
mod_cache в Apache — хороший встроенный инструмент без внешних зависимостей. Для публичной статики и крупных файлов выбирайте cache_disk, для микрокэша динамики — cache_socache. CacheQuickHandler даёт ощутимый выигрыш, но его стоит отключать в чувствительных ветках с авторизацией и сложным переписыванием. Несколько точных директив (CacheLock, лимиты размера, корректные Vary и Cache-Control) — и вы получите предсказуемый рост производительности без риска кэшировать лишнее.


