Налаштування HTTP-кешування: Cache-Control та ETag
Правильні заголовки кешування дозволяють браузерам та CDN зберігати ресурси без повторних запитів до сервера. Статичні ассети (JS, CSS, шрифти) з правильним кешем завантажуються миттєво при повторному візиті.
Cache-Control директиви
Cache-Control: public, max-age=31536000, immutable
| Директива | Значення |
|---|---|
public |
Кешировать у браузері та на прокси/CDN |
private |
Тільки у браузері (не на CDN) |
no-cache |
Завжди перевіряти актуальність з сервером |
no-store |
Ніколи не кешировать |
max-age=N |
Кешировать N секунд |
s-maxage=N |
Для CDN (переопределяет max-age) |
immutable |
Файл не змінюється — не перевіряти навіть при F5 |
must-revalidate |
Після max-age — обов'язкова перевірка |
Стратегія по типам ресурсів
# Nginx конфігурація
# Статичні ассети з хешем у імені (Vite, Webpack)
# app.a1b2c3d4.js — хеш змінюється при зміні файлу
location ~* \.(js|css)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Vary Accept-Encoding;
}
# Шрифти — також immutable (змінюємо URL при оновленні)
location ~* \.(woff2|woff|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Access-Control-Allow-Origin *;
}
# Зображення — довгий кеш, але без immutable
location ~* \.(webp|avif|jpg|jpeg|png|gif|svg|ico)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000";
}
# HTML — короткий кеш або no-cache
location ~* \.html$ {
expires 5m;
add_header Cache-Control "public, max-age=300, must-revalidate";
}
# API — не кешировать
location /api/ {
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma no-cache;
}
ETag — умовні запити
ETag дозволяє перевірити актуальність кеша без передачі всього файлу:
# Перший запит
GET /images/logo.png
Response: 200 + ETag: "abc123" + Last-Modified: Mon, 15 Mar 2024
# Повторний запит
GET /images/logo.png
If-None-Match: "abc123"
Response: 304 Not Modified (тіло не передається — тільки заголовки)
# Nginx: ETag включений за умовчанням
etag on;
# Last-Modified також включений за умовчанням
Laravel: кеш для API-відповідей
// Кеш на рівні HTTP-відповіді для API
public function products(Request $request): JsonResponse
{
$products = Cache::remember('api:products', 300, fn() =>
Product::with('category')->where('is_active', true)->get()
);
$etag = md5($products->pluck('updated_at')->max());
if ($request->header('If-None-Match') === $etag) {
return response()->noContent(304);
}
return response()->json($products)
->header('Cache-Control', 'public, max-age=300')
->header('ETag', $etag);
}
Інвалідація кеша через версіонування файлів
Vite автоматично додає хеш до імен файлів при сборці:
app.js → app.a1b2c3d4.js
app.css → app.9f8e7d6c.css
Зміна коду → новий хеш → браузер завантажує новий файл, невзирачи на immutable.
Vary: правильне кешування з переговорами контенту
# Різні кеші для gzip/brotli версій
location ~* \.(js|css)$ {
add_header Vary Accept-Encoding;
gzip_static on;
brotli_static on;
}
Без Vary: Accept-Encoding CDN може віддати gzip-версію браузеру, що не підтримує gzip.
Stale-While-Revalidate
# Віддавати кешований ответ, оновлювати у фоні
add_header Cache-Control "public, max-age=300, stale-while-revalidate=3600";
Користувач отримує миттєвий кешований ответ; браузер перевіряє у фоні для наступного візиту.
Час налаштування: кілька годин для повної конфігурації Nginx.







