HTTP-кешування без застарілих ресурсів: Cache-Control, ETag і версії файлів

Як налаштувати кешування HTML, CSS і JavaScript, щоб повторні відкриття були швидкими, а нові релізи не ламали сторінки. Практична схема заголовків, умовних запитів і публікації файлів.

HTTP-кешування: Cache-Control, ETag і версії ресурсів
On this page
  1. Що саме вирішує HTTP-кеш
  2. Політика для HTML і персоналізованих відповідей
  3. ETag і ціна відповіді 304
  4. Незмінні ресурси з версією в адресі
  5. Публікація без конфлікту версій
  6. Як перевірити поведінку кешу

Щоб HTTP-кеш прискорював повторні відкриття й не заважав оновленням, HTML варто перевіряти на актуальність, а статичні ресурси з версією в URL — кешувати надовго та ніколи не змінювати за тією самою адресою. Ці дві політики працюють разом: документ визначає потрібну версію застосунку, а браузер повторно використовує вже отримані файли.

Проблеми починаються, коли одна політика поширюється на все. Довгий строк свіжості для HTML затримує появу релізу. Обов’язкова перевірка кожного незмінного CSS-файлу додає мережеві звернення. А заміна JavaScript за старою адресою створює ситуацію, коли різні відвідувачі отримують різний код для однакового документа. Правильне налаштування потребує не стільки великої кількості заголовків, скільки чіткої домовленості про те, які адреси можуть змінювати вміст.

Що саме вирішує HTTP-кеш

Під час повторного звернення до ресурсу кеш має відповісти на кілька окремих запитань. Чи є придатна збережена відповідь? Чи дозволено використати її для цього запиту? Чи залишається вона свіжою? Якщо строк свіжості минув, чи можна перевірити її актуальність замість повторного завантаження всього тіла?

Свіжість не означає, що серверний файл гарантовано не змінився. Вона означає, що сервер дозволив використовувати збережену відповідь протягом певного часу без перевірки. Якщо файл замінили раніше, браузер може продовжувати використовувати попередню копію цілком відповідно до отриманих заголовків.

Директива max-age задає строк свіжості в секундах. Для простої відповіді без проміжних кешів його зручно уявляти як час, протягом якого браузер може обійтися без звернення до сервера. У ланцюжку з проксі або CDN також враховується вже накопичений вік відповіді: отримання файлу браузером не обов’язково запускає весь строк заново.

ДирективаЩо встановлюєЧого не гарантує
max-ageСтрок свіжості відповідіАвтоматичне отримання змін до завершення цього строку
no-cacheПеревірку перед повторним використаннямЗаборону зберігати відповідь
no-storeЗаборону зберігати відповідьВидалення всіх раніше збережених копій
privateЗаборону зберігання у спільному кешіЗаборону кешування в браузері користувача

Ці відмінності важливі для діагностики. Якщо відповідь прийшла з кешу, це ще не помилка. Спочатку потрібно з’ясувати, яку поведінку дозволив сервер і чи відповідає вона життєвому циклу ресурсу.

Політика для HTML і персоналізованих відповідей

HTML часто є точкою вибору версії: саме він містить посилання на CSS, JavaScript і початкові дані. Для документа, який має підхоплювати зміни під час наступного звичайного мережевого відкриття, придатною базовою політикою є перевірка перед повторним використанням:

Cache-Control: no-cache
ETag: "document-revision-a"

Значення ETag тут умовне: у реальній конфігурації його формує сервер. Браузер може зберегти документ, але перед повторним використанням має перевірити його. Якщо представлення не змінилося, сервер може відповісти без повторного передавання HTML. Якщо змінилося — надіслати новий документ із новими посиланнями на ресурси.

Для персоналізованого документа, який дозволено зберігати на пристрої користувача, можна явно обмежити кешування приватним кешем:

Cache-Control: private, no-cache

Директива private сама по собі не робить відповідь конфіденційною і не замінює автентифікацію. Вона визначає поведінку кешів. Якщо відповідь узагалі не повинна зберігатися в HTTP-кеші, потрібна інша політика:

Cache-Control: no-store

Таке розмежування особливо корисне для API. Публічний довідник і відповідь із даними поточного облікового запису можуть повертатися тим самим сервером, але потребують різних правил. Не слід призначати їм однаковий довгий max-age лише тому, що обидві відповіді мають формат JSON.

Не кожному HTML потрібна перевірка при кожному відкритті. Для публічної сторінки, де допустима контрольована затримка оновлення, можливий короткий строк свіжості. Важливо зафіксувати допустиму застарілість як вимогу продукту, а не отримати її випадково через глобальну конфігурацію вебсервера.

ETag і ціна відповіді 304

ETag — це непрозорий ідентифікатор представлення ресурсу. Клієнт не повинен виводити з нього дату, номер релізу чи алгоритм хешування. Для кешування важливо, щоб сервер коректно визначав, чи відповідає наданий клієнтом валідатор поточному представленню.

Після отримання відповіді з ETag браузер може надіслати умовний запит. У спрощеному вигляді обмін має такий вигляд:

GET /catalog HTTP/1.1
Host: example.test
If-None-Match: "document-revision-a"

Якщо відповідне представлення не змінилося, відповідь може бути такою:

HTTP/1.1 304 Not Modified
Cache-Control: no-cache
ETag: "document-revision-a"

Відповідь 304 не містить тіла документа: браузер використовує збережене тіло й оновлює відповідні метадані кешу. Коли валідатор не відповідає поточному представленню, сервер зазвичай повертає 200 із новим тілом та актуальним ETag.

304 економить байти, але не усуває очікування мережі. Запит усе одно має дістатися сервера або проміжного кешу, який здатний його обробити. Крім того, сервер іноді спочатку формує відповідь і лише потім порівнює валідатор. За такої реалізації обсяг передавання зменшується, а витрати на генерацію можуть залишитися.

Інший поширений валідатор — Last-Modified, який використовують з If-Modified-Since. Він спирається на час зміни, тоді як ETag дає змогу серверу визначати відповідність представлень без прив’язки до часової позначки. Не потрібно вручну додавати обидва механізми, якщо сервер уже коректно підтримує умовні запити.

Окремо перевіряйте варіанти відповіді. Заголовок Vary повідомляє кешу, які заголовки запиту впливають на вибір представлення. Якщо сервер віддає різний вміст залежно від них, валідатор і правила вибору кешованої відповіді мають бути узгоджені. Один незмінний ETag для всіх відповідей динамічного маршруту не забезпечує перевірки актуальності.

Незмінні ресурси з версією в адресі

Для статичних ресурсів найпростіша модель інша: новий вміст отримує новий URL. Замість постійного /assets/app.js збірка створює файл на зразок /assets/app.7c91e2.js. Суфікс у цьому прикладі умовний; суттєво те, що зміна вмісту змінює адресу.

Для такого ресурсу можна призначити тривалий строк свіжості:

Cache-Control: public, max-age=31536000, immutable

Тут строк становить один рік. Директива immutable повідомляє, що представлення не змінюватиметься протягом строку свіжості, тож додаткові перевірки свіжої відповіді не потрібні. Вона не робить ресурс вічним і не заважає браузеру видалити його з кешу через нестачу місця.

Основна гарантія походить із процесу публікації: одна адреса завжди відповідає одному вмісту. Якщо після виправлення помилки перезаписати app.7c91e2.js, кеш не зобов’язаний помітити заміну. Потрібно створити новий файл і оновити посилання в HTML або маніфесті збірки.

Правило поширюється на залежності. Якщо CSS посилається на змінений шрифт або зображення, відповідні URL теж мають оновитися. Коли через це змінюється сам CSS, він також отримує нову адресу. Система збірки має забезпечувати цілісність цього ланцюжка.

Хеш у назві не зменшує розмір першого завантаження. Він створює умови для повторного використання без перевірки актуальності. Скорочення ваги ресурсів залишається окремою роботою: наприклад, оптимізація форматів і розмірів зображень допомагає саме тоді, коли потрібної копії в кеші ще немає.

Публікація без конфлікту версій

Навіть правильні заголовки не врятують реліз, якщо новий HTML з’явиться раніше за файли, на які він посилається. У проміжку між цими подіями відвідувач може отримати документ із недоступним JavaScript. Тому порядок публікації є частиною стратегії кешування.

  1. Створіть нову збірку з новими адресами змінених ресурсів.
  2. Завантажте ресурси та перевірте їхню доступність через шлях, яким користується браузер.
  3. Опублікуйте HTML або маніфест, що посилається на нову збірку.
  4. Збережіть попередні ресурси для старих документів, відкритих вкладок і відкату.

Особливо важливий останній крок для застосунків із відкладеним завантаженням модулів. Вкладка може залишатися відкритою після релізу й запросити додатковий модуль лише після певної дії користувача. Якщо файли попередньої збірки вже видалено, цей запит завершиться помилкою, хоча початкове відкриття сторінки було успішним.

Універсального строку зберігання старих збірок немає. Він залежить від тривалості користувацьких сесій, строку кешування документів, механізму оновлення застосунку та вимог до відкату. Річний max-age не гарантує, що всі залежності вже лежать у кожному браузері: частину з них користувач міг ще не запитувати.

Очищення CDN також не видаляє свіжі копії з браузерів. Якщо публічна адреса помилково отримала тривалий строк свіжості, зміна заголовків на сервері не змусить усіх клієнтів негайно звернутися до неї знову. Для статичного ресурсу новий URL дає документу спосіб однозначно запросити виправлений вміст.

Під час відкату повернення попереднього HTML працює лише тоді, коли його залежності доступні. Це ще одна причина зберігати збірки як цілісні набори, а не замінювати окремі файли вручну.

Як перевірити поведінку кешу

Починайте перевірку в мережевій панелі браузера з увімкненим кешем. Режим вимкнення кешу корисний для інших завдань, але не показує звичайної поведінки повторних відвідувань. Примусове перезавантаження також може змінювати правила перевірки, тому його слід розглядати як окремий сценарій.

Спочатку відкрийте сторінку без придатних збережених відповідей. Перевірте фактичні Cache-Control, ETag і Vary для документа та ресурсів. Важливі саме заголовки, які отримав браузер: проксі, CDN або конфігурація конкретного маршруту можуть змінити налаштування застосунку.

Потім виконайте повторну навігацію до сторінки й з’ясуйте джерело кожної відповіді. Свіжий статичний ресурс може використовуватися без мережевого передавання. Документ із no-cache може спричинити умовний запит і відповідь 304. Сам статус 200 у панелі ще не доводить повторного завантаження: інструмент може показувати статус збереженої відповіді, тож дивіться також джерело й переданий обсяг.

Окремо перевірте оновлення між двома збірками:

  • Змінений ресурс отримує іншу адресу, а незмінний зберігає попередню.
  • Новий документ посилається на файли, які вже доступні.
  • Стара вкладка може завантажити свої відкладені залежності після релізу.
  • Повернення попереднього документа відновлює працездатну версію.
  • Персоналізовані відповіді не отримують політику публічних статичних ресурсів.

Якщо результати не відповідають заголовкам, перевірте, чи запит узагалі проходить через HTTP-кеш. Service Worker може повертати відповіді з Cache Storage за власною логікою. Повернення назад також може відновлювати документ із кешу переходів, не виконуючи звичайного завантаження HTML. Ці механізми потрібно відрізняти від умовного HTTP-запиту.

Підсумком перевірки має бути конкретна відповідність: документ отримує актуальну версію за обраною політикою, незмінні залежності повторно використовуються, а старі сторінки зберігають доступ до своїх файлів. Саме ця поведінка робить кешування передбачуваним під час щоденних релізів.

Поширені запитання

Чи означає no-cache, що браузер щоразу завантажує весь файл?

Ні. Браузер може зберегти відповідь і перевірити її актуальність умовним запитом. Якщо сервер підтвердить відповідність через 304, збережене тіло використовується повторно.

Чи потрібен ETag ресурсам із хешем у назві?

Для використання свіжої відповіді без перевірки ETag не потрібен. Він може знадобитися після втрати свіжості, якщо копія ще зберігається, але основою цієї схеми є незмінний вміст за версіонованою адресою.

Чи можна версіонувати файл параметром запиту?

Так, різні параметри утворюють різні URL. Однак потрібно перевірити, що конфігурація проміжних кешів враховує цей параметр, а сервер зберігає правильний вміст для кожної версії. Хеш у назві зазвичай спрощує перевірку відповідності адреси конкретному файлу.

Чому після очищення CDN користувач бачить старий ресурс?

Браузер може мати власну свіжу копію й не звертатися до CDN. Очищення спільного кешу не скасовує строк свіжості вже отриманої відповіді. Посилання на нову адресу ресурсу дозволяє запросити іншу версію.

Article changelog (1)
  • — Content revised