Пошук уроків, статей та іншого контенту
Дізнаємося, як заголовки передають метадані, керують кешуванням, типами даних і поведінкою з’єднання.
HTTP-заголовки — це пари «назва–значення», які передають метадані разом із HTTP-запитом або відповіддю.
Метадані описують не самі дані, а умови їх передавання та обробки:
який тип даних передається;
яким клієнтом надіслано запит;
чи можна зберігати відповідь у кеші;
чи потрібно підтримувати з’єднання відкритим;
який розмір має тіло повідомлення.
Приклад HTTP-запиту:
GET /users HTTP/1.1
Host: example.com
Accept: application/json
User-Agent: Mozilla/5.0
Connection: keep-aliveУ цьому прикладі:
Host містить назву сервера;
Accept повідомляє, який формат відповіді бажаний;
User-Agent описує клієнт;
Connection визначає поведінку мережевого з’єднання.
Відповідь сервера також має заголовки:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Content-Length: 27
{"message":"Привіт!"}Заголовки запиту надсилає клієнт серверу. Вони допомагають серверу зрозуміти контекст запиту.
Поширені заголовки запиту:
Host — назва сервера;
Accept — формати даних, які клієнт може обробити;
Content-Type — формат тіла запиту;
Content-Length — розмір тіла запиту;
User-Agent — інформація про клієнт;
Authorization — дані для автентифікації;
Cache-Control — інструкції щодо кешування.
Заголовки відповіді надсилає сервер клієнту.
Поширені заголовки відповіді:
Content-Type — формат тіла відповіді;
Content-Length — розмір тіла відповіді;
Cache-Control — правила кешування;
Location — адреса для перенаправлення;
Set-Cookie — встановлення cookie;
Connection — поведінка з’єднання.
Заголовки не замінюють тіло запиту або відповіді. Вони описують це тіло та визначають правила його обробки.
У вбудованому модулі http заголовки запиту доступні через req.headers.
const http = require('node:http');
const server = http.createServer((req, res) => {
console.log('Метод:', req.method);
console.log('URL:', req.url);
console.log('Заголовки:', req.headers);
const userAgent = req.headers['user-agent'];
const acceptedType = req.headers.accept;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end(
`User-Agent: ${userAgent || 'не вказано'}\n` +
`Accept: ${acceptedType || 'не вказано'}\n`
);
});
server.listen(3000, () => {
console.log('Сервер запущено на http://localhost:3000');
});Запустіть файл:
node server.jsПісля відкриття http://localhost:3000 браузер надішле власні заголовки, а сервер виведе їх у консоль.
Назви HTTP-заголовків нечутливі до регістру. Наприклад, це однакові заголовки:
Content-Type
content-type
CONTENT-TYPEУ Node.js назви заголовків в об’єкті req.headers зазвичай представлені малими літерами:
const contentType = req.headers['content-type'];Тому для вхідних заголовків краще використовувати нижній регістр.
Для встановлення заголовка відповіді використовуйте res.setHeader():
const http = require('node:http');
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.setHeader('X-App-Version', '1.0');
res.end(JSON.stringify({
message: 'Відповідь сервера'
}));
});
server.listen(3000);У відповіді будуть приблизно такі заголовки:
Content-Type: application/json; charset=utf-8
X-App-Version: 1.0Назва X-App-Version є прикладом власного заголовка застосунку. Власні заголовки не мають спеціального значення для HTTP-клієнта, якщо програма не домовилася, як їх обробляти.
Деякі заголовки можуть мати кілька значень. Наприклад, сервер може встановити кілька cookie:
res.setHeader('Set-Cookie', [
'theme=dark',
'language=uk'
]);Масив потрібно використовувати лише для заголовків, які справді підтримують кілька значень.
Значення заголовка можна змінити повторним викликом setHeader():
res.setHeader('Content-Type', 'text/plain');
res.setHeader('Content-Type', 'text/html');У результаті буде використано останнє значення.
Для видалення заголовка використовуйте removeHeader():
res.setHeader('X-Debug', 'true');
res.removeHeader('X-Debug');Заголовки потрібно встановити до надсилання першої частини тіла відповіді.
Після виклику res.write() або res.end() Node.js зазвичай надсилає заголовки клієнту. Після цього змінювати їх уже не можна.
const http = require('node:http');
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/plain');
res.write('Перша частина відповіді');
// Цей заголовок буде встановлено запізно
// res.setHeader('X-Status', 'late');
res.end('\nКінець відповіді');
});
server.listen(3000);Перевірити стан можна через res.headersSent:
if (!res.headersSent) {
res.setHeader('X-Status', 'ready');
}Зазвичай простіше спочатку встановити всі заголовки, а потім викликати res.end().
Content-TypeЗаголовок Content-Type описує формат тіла повідомлення.
Приклади:
Content-Type: text/plain
Content-Type: text/html
Content-Type: application/json
Content-Type: application/octet-streamДля текстових даних часто вказують кодування:
Content-Type: text/plain; charset=utf-8Для JSON-відповіді в Node.js:
const data = {
name: 'Олена',
role: 'developer'
};
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.end(JSON.stringify(data));JSON.stringify() перетворює JavaScript-об’єкт на текст JSON. Саме цей текст передається в тілі HTTP-відповіді.
Якщо сервер повертає JSON, але вказує:
Content-Type: text/plainклієнт може обробити відповідь не так, як очікує програма.
Коли клієнт надсилає тіло запиту, він повинен повідомити його формат:
Content-Type: application/jsonСервер може прочитати цей заголовок:
const contentType = req.headers['content-type'];
if (contentType === 'application/json') {
console.log('Очікуємо JSON');
}На практиці значення може містити додаткові параметри, наприклад:
application/json; charset=utf-8Тому перевірка лише на повну рівність не завжди підходить. Для простої перевірки формату можна використати startsWith():
const contentType = req.headers['content-type'] || '';
if (contentType.startsWith('application/json')) {
console.log('Запит містить JSON');
}Content-LengthContent-Length повідомляє розмір тіла повідомлення в байтах.
Для вхідного запиту Node.js може надати цей заголовок через:
const length = req.headers['content-length'];Не слід безумовно довіряти цьому значенню. Якщо програма приймає великі тіла запитів, вона повинна мати обмеження розміру та коректно обробляти потік даних.
Для звичайної відповіді Node.js часто сам визначає необхідні службові параметри. Наприклад:
const http = require('node:http');
const server = http.createServer((req, res) => {
const body = 'Вітаю!';
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.setHeader('Content-Length', Buffer.byteLength(body));
res.end(body);
});
server.listen(3000);Buffer.byteLength() важливий для тексту з не-ASCII символами, оскільки кількість символів і кількість байтів може відрізнятися.
Кеш може зберігати відповідь, щоб не запитувати її із сервера щоразу. Це зменшує кількість запитів і пришвидшує завантаження.
Основні правила кешування передаються через Cache-Control.
Для приватних або одноразових даних можна використати:
res.setHeader('Cache-Control', 'no-store');no-store означає, що відповідь не потрібно зберігати в кеші.
Наприклад, це може бути доречно для:
персональних даних;
результатів операцій, які не можна повторно показувати;
відповідей із тимчасовою або конфіденційною інформацією.
Заголовок:
res.setHeader('Cache-Control', 'public, max-age=60');означає, що відповідь можна зберігати та вважати актуальною протягом 60 секунд.
public дозволяє кешувати відповідь спільним кешам;
max-age=60 задає час актуальності в секундах.
Наприклад, для незмінного публічного ресурсу:
res.setHeader('Cache-Control', 'public, max-age=3600');Директива no-cache має інше значення, ніж no-store. Вона дозволяє зберегти відповідь, але перед повторним використанням кеш повинен перевірити її актуальність на сервері.
res.setHeader('Cache-Control', 'no-cache');Для початку важливо розрізняти:
no-store — не зберігати відповідь;
no-cache — можна зберігати, але потрібно перевіряти перед використанням;
max-age — скільки секунд відповідь вважається актуальною.
ConnectionЗаголовок Connection описує поведінку TCP-з’єднання в HTTP/1.1.
Поширені значення:
Connection: keep-alive
Connection: closekeep-alive означає, що з’єднання можна використати для наступних запитів;
close означає, що з’єднання буде закрито після завершення відповіді.
У Node.js HTTP-сервер сам керує з’єднаннями, але за потреби сервер може явно вказати close:
const http = require('node:http');
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.setHeader('Connection', 'close');
res.end('Після відповіді з’єднання буде закрито');
});
server.listen(3000);Зазвичай не потрібно вручну встановлювати Connection для кожної відповіді. Спеціальне значення цього заголовка з’являється, коли застосунку потрібно явно керувати завершенням з’єднання.
Поточний заголовок запиту можна прочитати так:
const connection = req.headers.connection;
console.log(connection);Цей сервер демонструє:
читання заголовків запиту;
встановлення Content-Type;
різні правила кешування;
керування заголовком Connection.
const http = require('node:http');
const server = http.createServer((req, res) => {
const userAgent = req.headers['user-agent'] || 'невідомий клієнт';
const connection = req.headers.connection || 'не вказано';
if (req.url === '/json') {
const responseBody = JSON.stringify({
message: 'Дані у форматі JSON',
userAgent
});
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.setHeader('Cache-Control', 'public, max-age=60');
res.end(responseBody);
return;
}
if (req.url === '/private') {
const responseBody = JSON.stringify({
message: 'Приватні дані'
});
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.setHeader('Cache-Control', 'no-store');
res.end(responseBody);
return;
}
if (req.url === '/close') {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.setHeader('Connection', 'close');
res.end('З’єднання буде закрито після відповіді');
return;
}
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end(
`Метод: ${req.method}\n` +
`URL: ${req.url}\n` +
`User-Agent: ${userAgent}\n` +
`Connection: ${connection}\n\n` +
'Доступні адреси:\n' +
'/json\n' +
'/private\n' +
'/close\n'
);
});
server.listen(3000, () => {
console.log('Сервер запущено на http://localhost:3000');
});Після запуску можна перевірити різні відповіді:
curl -i http://localhost:3000/json
curl -i http://localhost:3000/private
curl -i http://localhost:3000/closeПараметр -i показує не лише тіло відповіді, а й HTTP-заголовки.
res.end()Неправильно:
res.end('Готово');
res.setHeader('Content-Type', 'text/plain');Правильно:
res.setHeader('Content-Type', 'text/plain');
res.end('Готово');Content-TypeЯкщо відповідь містить JSON, використовуйте:
res.setHeader('Content-Type', 'application/json; charset=utf-8');Якщо це звичайний текст:
res.setHeader('Content-Type', 'text/plain; charset=utf-8');no-store і no-cacheno-store забороняє зберігати відповідь;
no-cache не забороняє зберігання, але вимагає перевірки актуальності.
Клієнт може надіслати довільний User-Agent, Referer або інший заголовок. Вхідні заголовки потрібно вважати даними від зовнішнього користувача, а не перевіреним джерелом інформації.
req.headersУ Node.js вхідні назви заголовків представлені малими літерами:
req.headers['content-type'];
req.headers['user-agent'];Content-Length вручну без розрахункуРозмір потрібно вказувати в байтах:
const body = 'Привіт';
const length = Buffer.byteLength(body);Неправильне значення може призвести до обрізаної відповіді або помилок під час її обробки.
HTTP-заголовки передають метадані запиту або відповіді.
У Node.js заголовки запиту доступні через req.headers.
Заголовки відповіді встановлюються методом res.setHeader().
Content-Type описує формат даних.
Content-Length описує розмір тіла в байтах.
Cache-Control визначає правила кешування.
Connection впливає на поведінку з’єднання.
Заголовки потрібно встановити до надсилання тіла відповіді.
Вхідним заголовкам від клієнта не слід безумовно довіряти.