Skip to main content

Що таке CDN і навіщо воно потрібне?

CDN (Content Delivery Network) - це глобальна мережа проксі-серверів, яка кешує статичні ресурси і роздає їх з точок, найближчих до користувача.

Теорія

TL;DR

  • CDN - як мережа піцерій: місцева точка видає піцу з готового тіста (кеш), а не чекає поставки від центрального складу з іншого кінця країни
  • Скорочує затримку з 200-500ms до 20-100ms, направляючи запити до найближчого Point of Presence (PoP)
  • Знімає 70-95% статичного трафіку з основного сервера
  • Варто додавати при глобальній аудиторії або коли статичні файли (зображення, JS, CSS) складають більшу частину трафіку

Швидкий приклад

html
<!-- Без CDN: кожен запит іде на твій єдиний сервер --> <link rel="stylesheet" href="/styles.css"> <!-- Користувач у США при сервері в ЄС: ~400ms затримки --> <!-- З CDN: найближчий edge-сервер роздає закешовану копію --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"> <!-- Затримка: ~50ms, файл закешований глобально -->

Відкрий вкладку Network в DevTools. Запит через CDN покаже малий TTFB (Time to First Byte). Запит до origin - великий. Це і є та різниця, яку CDN усуває.

Головна різниця

Без CDN кожен запит до ресурсу потрапляє на твій єдиний origin-сервер, незалежно від того, де знаходиться користувач. Людина в Токіо, яка завантажує файл із сервера у Франкфурті, чекає 300-500ms до появи першого байта. CDN перехоплює цей запит на токійському edge-сервері й роздає закешовану копію за 20-50ms. Origin підключається лише при першому запиті до файлу або після закінчення TTL.

Коли використовувати CDN

  • Глобальна аудиторія з користувачами в різних країнах: CDN скорочує географічну затримку на 50-80%
  • Сайт із важкими статичними ресурсами (зображення, JS-бандли, шрифти складають більшість трафіку): CDN
  • Пікові навантаження навколо запусків продукту або розпродажів: CDN поглинає трафік без падіння сервера
  • Локальний прототип або внутрішній інструмент для кількох людей: достатньо звичайного хостингу
  • Суто динамічний API без кешованих відповідей: CDN тут не допоможе

Як CDN працює зсередини

Браузер резолвить CDN-хостнейм через anycast DNS до найближчого PoP. Edge-сервер перевіряє кеш (за TTL, наприклад Cache-Control: max-age=3600 для JS-файлів). Є в кеші - роздає одразу з диска або пам'яті. Немає - запитує origin, зберігає копію, потім роздає.

Ця pull-модель стоїть за замовчуванням у CloudFront і Cloudflare. Такі провайдери, як Akamai, запускають Varnish-подібні проксі з LRU-витісненням на цих вузлах. Ключовий момент: заголовки Cache-Control у відповіді твого origin-сервера визначають, як довго CDN тримає файл.

Типові помилки

Немає заголовків Cache-Control. Якщо origin не надсилає заголовки кешування, CDN вважає кожен файл некешованим і пересилає всі запити на origin. Нуль користі.

js
// Неправильно: без заголовків CDN не може нічого кешувати app.use('/static', express.static('public')); // Правильно: кажемо CDN тримати файли рік app.use('/static', express.static('public', { maxAge: '1y', immutable: true }));

Забули про інвалідацію кешу після деплою. Ти задеплоїв новий app.js, але назва файлу та сама. CDN роздає стару версію до закінчення TTL, іноді кілька днів. Рішення: хеш у назві файлу (app.abc123.js). Vite і Webpack роблять це автоматично.

Кешування динамічного або персоналізованого контенту. Cache-Control: public, max-age=3600 на маршруті /api/user/profile означає, що CDN закешує дані одного користувача і роздасть їх усім іншим. Для персональних даних - тільки Cache-Control: private, no-cache.

Де використовується на практиці

  • React/Vite: production-збірка виводить dist/assets/index-[hash].js, завантажується на CDN з immutable-заголовками кешу
  • Next.js: Edge Network від Vercel автоматично кешує все з _next/static
  • Express: Cloudflare перед папкою public/, плюс заголовки кешу на статичних відповідях
  • Netflix: власна CDN під назвою Open Connect доставляє 99% відеотрафіку через обладнання, вбудоване прямо в мережі провайдерів

Follow-up питання

Q: Як DNS-резолвінг працює в CDN?


A: CDN-провайдери використовують anycast-маршрутизацію. Одна IP-адреса оголошується з сотень локацій по всьому світу, і DNS веде до найближчої. cdn.cloudflare.com може резолвитись у Франкфурті для берлінського користувача і в Далласі для користувача з Х'юстона.

Q: Яка різниця між push і pull CDN?


A: Pull CDN (за замовчуванням у CloudFront і Cloudflare) сам забирає контент з origin при першому промаху кешу і зберігає копію. Push CDN вимагає попередньо завантажити контент до того, як прийдуть запити. Push підходить для великих файлів, як-от відеоманіфести, коли ти вже знаєш, що попит буде.

Q: Як керувати інвалідацією кешу в масштабі?


A: Стандартний підхід - інвалідація за шляхом (/images/*) через API CDN. Повне скидання кешу - крайній захід, бо спричиняє хвилю запитів до origin. Краще проєктувати систему так, щоб інвалідація була не потрібна: хеш у назвах файлів означає, що URL змінюється з кожним деплоєм, а старі записи в CDN стають недосяжними самі по собі.

Q: Коли CDN погіршує продуктивність?


A: Перший запит після промаху кешу (cold start) додає затримку, бо edge мусить звернутись до origin. Надмірне кешування динамічного контенту призводить до застарілих даних. Петлі редиректів трапляються, коли CDN і origin по черзі намагаються перенаправити один і той самий URL один до одного.

Приклади

Підключення Bootstrap через CDN

Найпростіший кейс: завантаження бібліотеки з публічного CDN замість самостійного хостингу.

html
<!DOCTYPE html> <html> <head> <!-- Роздається з найближчого jsDelivr PoP, закешовано глобально --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" /> </head> <body> <button class="btn btn-primary">Натисни</button> </body> </html>

Файл вже закешований на edge-серверах jsDelivr по всьому світу. Користувач у Токіо отримує його з токійського PoP. Користувач у Бразилії - з місцевого. Твій сервер у цьому не бере участі взагалі.

Інвалідація кешу в Express і CloudFront

Тут більшість джунів стикаються з реальним багом. Ти оновлюєш файл на S3, але користувачі годинами бачать стару версію.

js
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3'); const { CloudFrontClient, CreateInvalidationCommand } = require('@aws-sdk/client-cloudfront'); const s3 = new S3Client({ region: 'us-east-1' }); const cf = new CloudFrontClient({ region: 'us-east-1' }); app.post('/update-image', async (req, res) => { // Завантажуємо новий файл на S3 await s3.send(new PutObjectCommand({ Bucket: 'myapp-assets', Key: 'img.jpg', Body: req.body })); // Інвалідуємо кеш CDN, щоб користувачі отримали нову версію await cf.send(new CreateInvalidationCommand({ DistributionId: 'E123ABC', InvalidationBatch: { Paths: { Quantity: 1, Items: ['/img.jpg'] }, CallerReference: Date.now().toString() } })); res.json({ ok: true }); });

Без виклику інвалідації користувачі бачать старий img.jpg до 24 годин (дефолтний TTL CloudFront). З ним кеш очищається за менш ніж 5 хвилин. Цей баг я бачив у продакшені мінімум тричі в командах, що вперше будували щось на S3 і CloudFront.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?