Пошук уроків, статей та іншого контенту
Налаштуєте Cache-Control, ETag, Last-Modified та інші HTTP-механізми для повторного використання відповідей.
Коли браузер запитує ресурс, наприклад HTML-документ, CSS-файл або відповідь API, сервер повертає відповідь через мережу. Повторний запит може бути непотрібним, якщо ресурс не змінився.
Кешування допомагає:
зменшити кількість HTTP-запитів;
пришвидшити завантаження сторінок;
зменшити навантаження на сервер;
економити трафік користувача.
Браузер може зберігати відповідь у своєму кеші. Під час наступного запиту він перевіряє правила кешування та вирішує:
використати збережену відповідь без звернення до сервера;
перевірити на сервері, чи змінилася відповідь;
отримати повністю нову відповідь.
Ці правила передаються за допомогою HTTP-заголовків.
Cache-ControlОсновний заголовок для керування кешуванням — Cache-Control.
max-ageДиректива max-age визначає, скільки секунд відповідь вважається свіжою.
Cache-Control: max-age=3600У цьому прикладі браузер може використовувати збережену відповідь протягом однієї години без нового запиту до сервера.
Час рахується від моменту отримання відповіді. Якщо минула година, відповідь стає застарілою, і браузер може повторно перевірити її.
Наприклад:
Cache-Control: max-age=31536000Це означає, що відповідь можна вважати свіжою протягом одного року.
no-storeno-store забороняє зберігати відповідь у кеші:
Cache-Control: no-storeЦю директиву використовують для даних, які не повинні залишатися в кеші, наприклад:
персональних відповідей;
одноразових токенів;
конфіденційної інформації;
даних, які щоразу повинні надходити із сервера.
no-cacheНазва no-cache може бути оманливою. Вона не забороняє зберігати відповідь. Вона означає: перед повторним використанням збереженої відповіді потрібно перевірити її на сервері.
Cache-Control: no-cacheБраузер може зберегти відповідь, але під час наступного запиту використає умовний запит з ETag або Last-Modified.
private і publicCache-Control: private, max-age=600private означає, що відповідь призначена для кешу конкретного користувача, зазвичай браузера. Спільні кеші між користувачами не повинні її зберігати.
Це корисно для відповідей, які залежать від авторизованого користувача.
Cache-Control: public, max-age=3600public дозволяє кешування також у спільних кешах, наприклад у CDN або проксі-серверах.
Для загальнодоступних незмінних ресурсів часто використовують:
Cache-Control: public, max-age=31536000, immutableimmutable повідомляє, що ресурс не зміниться протягом терміну кешування. Такий підхід зручний для файлів із версією в імені, наприклад app.4f82c.js.
Після завершення терміну max-age браузеру не обов’язково завантажувати весь ресурс повторно. Він може запитати сервер, чи змінилася відповідь.
Для цього використовують умовні заголовки:
If-None-Match разом із ETag;
If-Modified-Since разом із Last-Modified.
Якщо ресурс не змінився, сервер повертає:
HTTP/1.1 304 Not ModifiedВідповідь 304 не містить тіла ресурсу. Браузер використовує копію, яка вже є в його кеші.
Це все одно HTTP-запит, але він значно менший за повторне передавання всього файлу.
ETagETag — це ідентифікатор конкретної версії ресурсу.
ETag: "profile-v7"Замість текстової версії сервер може використовувати хеш вмісту:
ETag: "a3f5c91d"Під час наступного запиту браузер надсилає:
If-None-Match: "a3f5c91d"Сервер порівнює це значення з поточним ETag:
якщо значення однакові — повертає 304 Not Modified;
якщо значення відрізняються — повертає нову відповідь із кодом 200 та новим ETag.
ETag точно визначає версію вмісту. Навіть невелика зміна файлу може призвести до зміни його ETag.
Last-ModifiedLast-Modified містить дату й час останньої зміни ресурсу:
Last-Modified: Wed, 02 Sep 2026 10:00:00 GMTБраузер може надіслати цю дату в наступному запиті:
If-Modified-Since: Wed, 02 Sep 2026 10:00:00 GMTЯкщо файл не змінювався після вказаної дати, сервер повертає 304 Not Modified.
Last-Modified простіший у реалізації, але має обмеження:
час зміни може мати недостатню точність;
деякі системи можуть неправильно оновлювати час;
два різні варіанти ресурсу можуть мати однаковий час зміни.
Тому для важливих або часто змінюваних ресурсів зазвичай надійнішим є ETag. Сервер може надсилати обидва заголовки.
ETag і Last-ModifiedЯкщо відповідь містить і ETag, і Last-Modified, браузер може надіслати обидва умовні заголовки.
На сервері зазвичай спочатку перевіряють If-None-Match, оскільки ETag точніше описує версію ресурсу. Якщо ETag відсутній, можна використовувати If-Modified-Since.
Важливо, що 304 Not Modified означає не «сервер нічого не знає про ресурс», а «збережена у вас копія все ще актуальна».
ExpiresДо появи Cache-Control часто використовували Expires:
Expires: Wed, 02 Sep 2026 11:00:00 GMTВін визначає конкретний момент, до якого відповідь вважається свіжою.
Сьогодні для нових проєктів переважно використовують Cache-Control, оскільки він задає правила зрозуміліше. Expires може залишатися для сумісності зі старими клієнтами.
Якщо присутні обидва заголовки, сучасні клієнти зазвичай надають перевагу Cache-Control.
VaryВідповідь іноді залежить від заголовків запиту. Наприклад, сервер може повертати різне стиснення залежно від Accept-Encoding.
У такому випадку відповідь може містити:
Vary: Accept-EncodingЦе означає, що кеш повинен зберігати окремі варіанти відповіді для різних значень Accept-Encoding.
Інший приклад:
Vary: Accept-LanguageТоді українська та англійська версії сторінки не змішуватимуться в одному записі кешу.
Без правильного Vary кеш може повернути користувачу відповідь, підготовлену для іншого варіанта запиту.
Нижче наведено повністю робочий приклад сервера на Node.js без додаткових бібліотек.
Він:
повертає ресурс із Cache-Control;
додає ETag і Last-Modified;
обробляє If-None-Match;
обробляє If-Modified-Since;
повертає 304, якщо ресурс не змінився.
const http = require('node:http');
const port = 3000;
const body = JSON.stringify({
message: 'Ці дані можна кешувати'
});
// Значення ETag має змінитися, коли змінюється вміст ресурсу.
const etag = '"data-v1"';
// HTTP-дата останньої зміни ресурсу.
const lastModified = new Date('2026-09-02T10:00:00Z').toUTCString();
const server = http.createServer((request, response) => {
if (request.url !== '/data') {
response.writeHead(404, {
'Content-Type': 'text/plain; charset=utf-8'
});
response.end('Не знайдено');
return;
}
const requestEtag = request.headers['if-none-match'];
const requestDate = request.headers['if-modified-since'];
// Спочатку перевіряємо ETag, бо він точніше визначає версію.
if (requestEtag === etag) {
response.writeHead(304, {
ETag: etag,
'Last-Modified': lastModified,
'Cache-Control': 'public, max-age=60'
});
response.end();
return;
}
// Якщо ETag не збігся або його немає, перевіряємо дату зміни.
if (requestDate && requestDate === lastModified) {
response.writeHead(304, {
ETag: etag,
'Last-Modified': lastModified,
'Cache-Control': 'public, max-age=60'
});
response.end();
return;
}
response.writeHead(200, {
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': Buffer.byteLength(body),
ETag: etag,
'Last-Modified': lastModified,
'Cache-Control': 'public, max-age=60'
});
response.end(body);
});
server.listen(port, () => {
console.log(`Сервер запущено: http://localhost:${port}/data`);
});Збережіть код у файлі server.js і запустіть:
node server.jsПерший запит поверне повну відповідь 200. У ній будуть заголовки приблизно такого вигляду:
HTTP/1.1 200 OK
ETag: "data-v1"
Last-Modified: Wed, 02 Sep 2026 10:00:00 GMT
Cache-Control: public, max-age=60Умовний запит із тим самим ETag можна виконати так:
curl -i http://localhost:3000/data \
-H 'If-None-Match: "data-v1"'Результатом буде відповідь 304 Not Modified без JSON у тілі.
Для різних типів ресурсів потрібні різні правила.
Якщо ім’я файлу змінюється після кожної зміни, можна довго кешувати ресурс:
Cache-Control: public, max-age=31536000, immutableПриклади імен:
styles.a81f2.css;
app.4f82c.js;
logo.v3.svg.
Коли вміст змінюється, застосунок посилається на нове ім’я файлу.
HTML часто містить посилання на актуальні версії скриптів і стилів, тому його зазвичай не кешують надовго. Один із варіантів:
Cache-Control: no-cache
ETag: "page-v12"Браузер зможе зберігати HTML, але перевірятиме його актуальність перед повторним використанням.
Якщо відповідь API може бути трохи застарілою:
Cache-Control: private, max-age=60Якщо відповідь залежить від авторизованого користувача, private допомагає не зберігати її у спільному кеші.
Для даних, які не можна зберігати:
Cache-Control: no-storeno-cache і no-storeno-cache дозволяє зберігати відповідь, але вимагає перевірки;
no-store забороняє зберігати відповідь.
Якщо сервер повертає:
Cache-Control: max-age=31536000для файлу app.js, а ім’я файлу не змінюється, користувач може бачити старий код протягом року.
Для довгого кешування використовуйте версію або хеш у назві файлу.
ETag після зміни вмістуЯкщо вміст ресурсу змінився, його ETag також повинен змінитися. Інакше сервер може повідомити браузеру, що стара копія все ще актуальна.
304Відповідь 304 Not Modified не повинна містити тіло ресурсу. Браузер уже має потрібну копію в кеші.
Якщо сервер формує різні відповіді залежно від Accept-Encoding, Accept-Language або іншого заголовка, потрібно вказати це через Vary.
Під час розробки кеш можна перевірити в інструментах розробника браузера:
відкрийте вкладку мережевих запитів;
перезавантажте сторінку;
перегляньте заголовки відповіді;
знайдіть Cache-Control, ETag і Last-Modified;
зверніть увагу на статус 304 або ознаку використання кешу.
Також корисно перевіряти заголовки через curl. Перший запит і наступний умовний запит мають різні цілі:
перший отримує ресурс і його метадані;
наступний перевіряє, чи потрібно передавати ресурс повторно.
Браузер зберігає HTTP-відповіді та може повторно їх використовувати.
Cache-Control визначає основні правила кешування.
max-age задає час, протягом якого відповідь вважається свіжою.
no-store забороняє зберігання, а no-cache вимагає повторної перевірки.
ETag ідентифікує версію ресурсу.
Last-Modified повідомляє час останньої зміни.
If-None-Match та If-Modified-Since використовуються для умовних запитів.
304 Not Modified дозволяє не передавати тіло ресурсу повторно.
Vary потрібен, коли відповідь залежить від заголовків запиту.
Для незмінних файлів добре підходять довгий кеш і версія в імені файлу.