Skip to content
← Все статьи

Cache-Control заголовки: полный гид по кэшированию

Заголовок Cache-Control — главный инструмент управления кэшированием в HTTP. Правильная настройка сокращает загрузку сервера, ускоряет сайт для пользователей и улучшает PageSpeed анализ. Неправильная — приводит к «залипшему» контенту, пропаже обновлений и SEO-проблемам.

В этом гайде разберём все директивы Cache-Control, отношения с ETag, Last-Modified и Vary, стратегии для разных типов контента (статика, HTML, API документацию), примеры настройки в nginx и Apache.

Что такое Cache-Control

По RFC 9111, Cache-Control управляет поведением кэшей (браузер, CDN, прокси). Это стандарт с 1999 года (HTTP/1.1), заменивший устаревшие заголовки Expires и Pragma.

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

max-age=N

Срок жизни кэша в секундах. max-age=3600 = кэш валиден 1 час.

s-maxage=N

Как max-age, но только для shared cache (CDN, reverse proxy), игнорируется браузером.

public

Разрешено кэширование и shared (CDN), и private (браузер).

private

Только браузер. Запрещает CDN кэшировать (для авторизованного контента).

no-cache

НЕ означает «не кэшировать»! Означает «перед использованием кэша всегда проверь у origin через ETag/If-Modified-Since». Вернулся 304 — используй кэш.

no-store

Вот это «совсем не кэшировать». Ни браузер, ни CDN, ни прокси не должны сохранять ответ.

must-revalidate

Использование кэша после истечения запрещено без revalidation. Полезно для финансовых данных.

immutable

Обещание: контент никогда не изменится. Браузер даже не ревалидирует при reload.

stale-while-revalidate=N

Разрешает отдавать устаревший кэш N секунд, пока в фоне идёт ревалидация. Мгновенный response + свежий контент следующим запросом.

stale-if-error=N

Если origin упал, отдавай устаревший кэш N секунд. Защита от downtime.

Типичные паттерны

Статичные ассеты с хэшем в имени

Cache-Control: public, max-age=31536000, immutable
# 1 год, immutable — webpack bundle app.a3f2b.js не изменится

HTML-страницы

Cache-Control: public, max-age=0, s-maxage=3600, stale-while-revalidate=86400
# Браузер: всегда проверяет
# CDN: кэш 1 час
# При истечении — stale-while-revalidate на день

API endpoints (динамика)

Cache-Control: private, max-age=60
# Личный кэш 60 секунд, CDN не кэширует

Авторизованный контент

Cache-Control: private, no-cache
Vary: Authorization, Cookie

Чувствительные данные (банк, пароли)

Cache-Control: no-store
Pragma: no-cache

Настройка в nginx

server {
    # Статика с хэшем — длинный immutable кэш
    location ~* \.(css|js|woff2|png|jpg|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # Изображения без хэша — умеренный кэш
    location ~* \.(png|jpg|gif)$ {
        expires 7d;
        add_header Cache-Control "public, max-age=604800";
    }

    # HTML — минимум кэша, stale-while-revalidate
    location ~* \.html$ {
        expires 0;
        add_header Cache-Control "public, max-age=0, s-maxage=3600, stale-while-revalidate=86400";
    }

    # API — приватно, коротко
    location /api/ {
        add_header Cache-Control "private, max-age=60";
        proxy_pass http://backend;
    }

    # Чувствительное
    location /api/account {
        add_header Cache-Control "no-store";
    }
}

Настройка в Apache (.htaccess)

<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/jpeg "access plus 7 days"
    ExpiresByType text/css "access plus 1 year"
    ExpiresByType application/javascript "access plus 1 year"
</IfModule>

<IfModule mod_headers.c>
    <FilesMatch "\.(css|js|woff2)$">
        Header set Cache-Control "public, max-age=31536000, immutable"
    </FilesMatch>

    <FilesMatch "\.html$">
        Header set Cache-Control "public, max-age=0, s-maxage=3600, stale-while-revalidate=86400"
    </FilesMatch>
</IfModule>

ETag и Last-Modified: ревалидация

Когда кэш истекает, браузер делает conditional request:

GET /page.html
If-None-Match: "a3f2b"
If-Modified-Since: Wed, 01 Jan 2026 12:00:00 GMT

Сервер отвечает 304 Not Modified (пустое тело) или 200 с новым контентом. 304 — дешевле, чем полная передача.

# nginx автоматически отдаёт ETag для статики
etag on;

# PHP
$etag = md5_file($path);
header("ETag: \"{$etag}\"");
if (($_SERVER['HTTP_IF_NONE_MATCH'] ?? '') === "\"{$etag}\"") {
    http_response_code(304);
    exit;
}

Vary — учёт различных представлений

Vary: Accept-Encoding
# Разные кэши для gzip/br/identity

Vary: Accept-Language
# Разные кэши для ru/en версий

Vary: Cookie
# Ломает shared cache — используйте только когда необходимо

Cache-Busting стратегии

  1. Fingerprinting (хэш в имени файла)app.a3f2b.js. Самый надёжный.
  2. Query stringapp.js?v=2. Работает, но некоторые CDN игнорируют query.
  3. Versioning path/v2/app.js. Полная инвалидация при релизе.
  4. ETag-based — только для HTML, не для статики.

CDN-специфика

Проверка кэширования

Используйте HTTP Header Checker — он покажет Cache-Control, ETag, Last-Modified, Age и CF-Cache-Status в одном месте. Также полный гид по кэшированию содержит продвинутые примеры.

# Проверка из терминала
curl -I https://example.com/app.js

# С ревалидацией
curl -H 'If-None-Match: "a3f2b"' https://example.com/app.js
# ждёте HTTP/2 304

Антипаттерны

Часто задаваемые вопросы (FAQ)

В: Почему no-cache всё равно кэширует?
О: no-cache ≠ no-store. no-cache разрешает хранить, но требует ревалидации. Для полного отключения — no-store.

В: max-age или Expires — что лучше?
О: max-age. Expires — устаревший HTTP/1.0 header, требует правильное время сервера. Cache-Control приоритетнее.

В: Кэш пропадает после обновления сайта?
О: Используйте fingerprinting — новый хэш в имени файла даст новый URL, и браузер загрузит свежую версию.

В: Влияет ли кэширование на SEO?
О: Косвенно через Core Web Vitals. Правильный кэш снижает TTFB и улучшает LCP.

Заключение

Cache-Control — мощный инструмент, но требует понимания директив. Статика с хэшем = immutable на год. HTML = короткий max-age + stale-while-revalidate. Авторизованное = private или no-store. Всегда проверяйте реальные заголовки через Enterno.io HTTP Checker после изменений.

Проверьте ваш сайт прямо сейчас

Проверить →
Другие статьи: HTTP
HTTP
HTTP коды ответов: полный справочник с примерами
10.03.2025 · 64 просм.
HTTP
Ошибка 503 Service Unavailable: причины и решения
15.04.2026 · 5 просм.
HTTP
HTTP-методы: GET, POST, PUT, DELETE и другие
16.03.2026 · 38 просм.
HTTP
504 Gateway Timeout: причины и решения для админов
15.04.2026 · 18 просм.