Пошук уроків, статей та іншого контенту
Зрозумієте, як CDN кешує контент на edge-вузлах, і налаштуєте правила кешування та очищення ресурсів.
CDN — це мережа edge-вузлів, розташованих ближче до користувачів. Коли клієнт запитує ресурс, CDN може повернути його з найближчого edge-вузла, не звертаючись щоразу до origin-сервера.
Типовий шлях запиту:
Клієнт надсилає запит до CDN.
CDN обчислює ключ кешу для цього запиту.
Якщо ресурс є в кеші й не протермінований, CDN повертає його як HIT.
Якщо ресурсу немає або він застарів, CDN звертається до origin-сервера.
CDN зберігає отриману відповідь і повертає її клієнту.
Це зменшує:
затримку для користувача;
кількість запитів до origin-сервера;
навантаження на базу даних і застосунок;
витрати на передавання даних.
Найчастіше через CDN кешують:
HTML-сторінки;
JavaScript і CSS-файли;
зображення;
відео;
шрифти;
публічні API-відповіді.
Не слід без додаткової перевірки кешувати:
персональні сторінки;
відповіді з приватними даними;
сторінки кошика;
відповіді, залежні від авторизації;
результати операцій POST, PUT, PATCH або DELETE.
CDN зазвичай кешує HTTP-відповідь, а не сам файл у файловій системі. На рішення про кешування впливають:
HTTP-метод;
URL;
заголовки відповіді;
статус відповіді;
налаштування CDN;
значення Cookie та інших заголовків, якщо вони входять до cache key.
Є два основні результати пошуку в кеші:
Cache hit — ресурс знайдено в кеші, origin не викликався.
Cache miss — ресурсу немає в кеші, тому CDN запитує його в origin.
Також ресурс може бути:
fresh — ще актуальний за TTL;
stale — термін кешування минув;
revalidated — CDN перевірив у origin, чи змінився ресурс.
Під час першого запиту до нового edge-вузла часто виникає cache miss. Наступні користувачі можуть отримати той самий ресурс із кешу.
Основний спосіб керувати кешуванням — заголовок Cache-Control.
max-agemax-age вказує, скільки секунд відповідь може вважатися актуальною для клієнта, зокрема браузера.
Cache-Control: public, max-age=3600У цьому прикладі браузер може використовувати відповідь протягом однієї години без повторного запиту.
s-maxages-maxage призначений для shared cache — CDN або проксі. Якщо він заданий, CDN може використовувати його замість max-age.
Cache-Control: public, max-age=60, s-maxage=3600Це означає:
браузер кешує відповідь на 60 секунд;
CDN кешує відповідь на 3600 секунд.
Так можна частіше оновлювати дані в браузері, але рідше звертатися до origin через CDN.
public і privatepublic дозволяє зберігати відповідь у спільному кеші:
Cache-Control: public, max-age=300private дозволяє кешувати відповідь лише в приватному кеші користувача, наприклад у браузері:
Cache-Control: private, max-age=300Для персональних відповідей зазвичай використовують:
Cache-Control: private, no-storeno-store і no-cacheno-store забороняє зберігати відповідь у кешах:
Cache-Control: no-storeЦе доречно для конфіденційних даних.
no-cache має інше значення: відповідь можна зберегти, але перед повторним використанням її потрібно перевірити в origin.
Cache-Control: no-cacheОтже, no-cache не означає «не кешувати».
immutableДля ресурсів із версіонованими іменами файлів можна використовувати:
Cache-Control: public, max-age=31536000, immutableimmutable повідомляє, що ресурс не зміниться протягом терміну кешування. Якщо вміст зміниться, застосунок опублікує файл під новим іменем.
Наприклад:
app.7f3a91.js
app.9c42bd.jsНижче наведено мінімальний HTTP-сервер на Node.js без зовнішніх залежностей. Він:
довго кешує версіоновані статичні файли;
коротко кешує публічний HTML;
не кешує приватні дані;
додає ETag для перевірки змін.
const http = require("node:http");
const crypto = require("node:crypto");
const port = 3000;
function sendResponse(req, res, body, contentType, cacheControl) {
const etag = `"${crypto
.createHash("sha1")
.update(body)
.digest("hex")}"`;
if (req.headers["if-none-match"] === etag) {
res.writeHead(304, {
ETag: etag,
"Cache-Control": cacheControl,
});
res.end();
return;
}
res.writeHead(200, {
"Content-Type": contentType,
"Cache-Control": cacheControl,
ETag: etag,
});
if (req.method !== "HEAD") {
res.end(body);
} else {
res.end();
}
}
const server = http.createServer((req, res) => {
if (req.method !== "GET" && req.method !== "HEAD") {
res.writeHead(405, { Allow: "GET, HEAD" });
res.end("Method Not Allowed");
return;
}
if (req.url === "/assets/app.7f3a91.js") {
const script = 'console.log("version 7f3a91");';
sendResponse(
req,
res,
script,
"application/javascript; charset=utf-8",
"public, max-age=31536000, immutable"
);
return;
}
if (req.url === "/") {
const html = `<!doctype html>
<html lang="uk">
<head>
<meta charset="utf-8">
<title>CDN demo</title>
</head>
<body>
<h1>Публічна сторінка</h1>
<script src="/assets/app.7f3a91.js"></script>
</body>
</html>`;
sendResponse(
req,
res,
html,
"text/html; charset=utf-8",
"public, max-age=60, s-maxage=300, stale-while-revalidate=30"
);
return;
}
if (req.url === "/api/profile") {
const response = JSON.stringify({
user: "current-user",
email: "user@example.com",
});
sendResponse(
req,
res,
response,
"application/json; charset=utf-8",
"private, no-store"
);
return;
}
res.writeHead(404, {
"Content-Type": "text/plain; charset=utf-8",
});
res.end("Not Found");
});
server.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});Запустіть сервер:
node server.jsПеревірте заголовки:
curl -i http://localhost:3000/
curl -i http://localhost:3000/assets/app.7f3a91.js
curl -i http://localhost:3000/api/profileДля перевірки ETag спочатку отримайте значення заголовка, а потім надішліть його в If-None-Match:
curl -i \
-H 'If-None-Match: "значення-etag"' \
http://localhost:3000/assets/app.7f3a91.jsЯкщо файл не змінився, сервер поверне:
HTTP/1.1 304 Not ModifiedВідповідь 304 не містить тіла ресурсу. Клієнт використовує вже наявну копію.
CDN визначає, чи є ресурс у кеші, за cache key. Часто до нього входять:
домен;
шлях URL;
query-параметри;
HTTP-метод;
окремі заголовки;
інколи значення cookies.
Наприклад, такі адреси можуть мати різні записи в кеші:
/products?page=1
/products?page=2Водночас параметр, який не впливає на вміст, може без потреби створювати багато варіантів:
/products?utm_source=newsletter
/products?utm_source=search
/products?utm_source=socialЧерез це зменшується cache hit ratio — частка запитів, які обслуговуються з кешу.
Під час налаштування CDN потрібно явно вирішити:
які query-параметри впливають на відповідь;
чи потрібно враховувати cookies;
чи потрібна різниця між HTTP-заголовками;
чи можна ігнорувати службові параметри аналітики.
Небезпечно повністю ігнорувати параметри або cookies, якщо вони змінюють вміст відповіді. Інакше CDN може повернути одному користувачу дані іншого.
Для статичних ресурсів найкраще використовувати версіонування імен:
styles.a81d2c.css
app.7f3a91.js
logo.4b19e0.svgПісля зміни файлу генерується нове ім’я:
app.7f3a91.js
app.9c42bd.jsСтарий ресурс можна кешувати дуже довго:
Cache-Control: public, max-age=31536000, immutableHTML зазвичай кешують коротше, оскільки саме він посилається на актуальні версії JavaScript і CSS:
Cache-Control: public, max-age=60, s-maxage=300Тоді після нового розгортання достатньо дочекатися оновлення HTML, а нові імена файлів гарантують отримання нового вмісту.
Якщо сторінка однакова для всіх користувачів, її можна кешувати на CDN:
Cache-Control: public, max-age=60, s-maxage=600Якщо HTML залежить від cookies або авторизації, публічне кешування потрібно вимкнути або дуже обережно налаштувати cache key.
Публічну відповідь API можна кешувати на короткий час:
Cache-Control: public, s-maxage=30, stale-while-revalidate=10Це підходить для даних, які не є персональними й можуть бути неактуальними протягом кількох секунд.
Для відповіді з даними поточного користувача:
Cache-Control: private, no-storeТаку відповідь не можна віддавати зі спільного CDN-кешу.
stale-while-revalidateДиректива stale-while-revalidate дозволяє CDN тимчасово віддати застарілу відповідь, паралельно перевіряючи її в origin:
Cache-Control: public, s-maxage=300, stale-while-revalidate=30У цьому прикладі:
протягом 300 секунд відповідь свіжа;
протягом наступних 30 секунд CDN може швидко повернути стару відповідь;
у фоновому режимі CDN перевіряє нову версію.
Це зменшує затримку для користувача під час оновлення кешу, але означає, що клієнт інколи отримає попередню версію даних.
Очищення кешу називають purge або invalidation. Воно видаляє ресурс із CDN до завершення його TTL.
Зазвичай CDN може очищати:
конкретний URL;
список URL;
усі ресурси певного шляху;
усі ресурси домену;
ресурси за тегом або surrogate key, якщо це підтримується конкретним CDN.
Наприклад, після зміни сторінки можна очистити:
/А після зміни файлів — конкретний ресурс:
/assets/app.7f3a91.jsТочний API очищення залежить від CDN, але загальний процес однаковий:
Розгорнути нову версію в origin.
Визначити ресурси, які змінилися.
Відправити запит на invalidation.
Дочекатися поширення очищення на edge-вузли.
Перевірити відповіді та заголовки CDN.
Очищення може поширюватися не миттєво на всі вузли. Тому критичні системи не повинні покладатися лише на purge.
Для статичних файлів переважно використовують версіонування:
app.7f3a91.jsПереваги:
не потрібно очищати старий файл;
старі сторінки можуть безпечно використовувати стару версію;
нова версія має інший cache key;
можна застосувати довгий TTL.
Purge краще використовувати для:
HTML;
конфігурацій;
ресурсів із незмінним URL;
термінового видалення помилкового або небезпечного вмісту.
Для діагностики потрібно перевіряти HTTP-заголовки. Конкретні назви залежать від CDN, але часто можна побачити:
Age — приблизний вік об’єкта в кеші;
Via — інформацію про проксі;
X-Cache — результат на кшталт HIT або MISS;
ETag — ідентифікатор версії ресурсу;
Cache-Control — правила кешування.
Зробіть кілька однакових запитів:
curl -I https://example.com/assets/app.7f3a91.js
curl -I https://example.com/assets/app.7f3a91.jsПерший запит може бути MISS, якщо ресурс ще не збережений на конкретному edge-вузлі. Наступний запит зазвичай має стати HIT.
Під час перевірки також важливо протестувати:
різні query-параметри;
авторизований і неавторизований запит;
наявність cookies;
поведінку після зміни ресурсу;
поведінку після purge.
Для багатьох вебзастосунків можна почати з такої стратегії:
версіоновані JS, CSS, зображення і шрифти:
Cache-Control: public, max-age=31536000, immutableпублічний HTML:
Cache-Control: public, max-age=60, s-maxage=300публічний API:
Cache-Control: public, s-maxage=30персональні дані:
Cache-Control: private, no-storeЦі значення не є універсальними. TTL потрібно вибирати за допустимою затримкою оновлення даних.
Якщо відповідь залежить від користувача, її не можна робити public без правильно налаштованого cache key.
Небезпечний приклад:
Cache-Control: public, s-maxage=600для відповіді, яка містить дані поточного користувача.
Якщо app.js кешується на рік і його URL не змінюється, користувачі можуть довго отримувати стару версію.
Для довгого TTL використовуйте імена з хешем або інший механізм версіонування.
no-cache і no-storeno-cache дозволяє зберегти відповідь, але вимагає повторної перевірки.
no-store забороняє зберігання.
Оновлення HTML може вимагати очищення конкретного HTML-шляху, а не лише статичного JavaScript-файлу. Потрібно врахувати всі ресурси, які могли залишитися в кеші.
Якщо вони змінюють відповідь, їх не можна виключати з cache key без додаткової логіки. Інакше CDN може змішати різні варіанти одного ресурсу.
Після очищення потрібно перевірити ресурс із різних точок або принаймні повторити запити й подивитися на статус кешу. Очистка в одному місці не завжди означає миттєве оновлення всіх edge-вузлів.
CDN зберігає HTTP-відповіді на edge-вузлах і повертає їх без звернення до origin.
Cache-Control визначає, хто і як довго може кешувати відповідь.
max-age переважно застосовується до браузера, а s-maxage — до shared cache, зокрема CDN.
Статичні файли безпечно кешувати надовго, якщо використовувати версіоновані імена.
Персональні відповіді потрібно позначати як private або no-store.
Cache key має враховувати всі параметри, які впливають на вміст відповіді.
Purge видаляє ресурси з CDN до завершення TTL, але може поширюватися не миттєво.
Для перевірки кешування аналізуйте Cache-Control, ETag, Age і службові заголовки CDN.