HTTP протокол
Зачем нужно
HTTP (HyperText Transfer Protocol) — основной протокол передачи данных в вебе. Каждый раз, когда браузер загружает страницу, отправляет форму или SPA делает API-запрос — используется HTTP. Понимание HTTP необходимо для работы с API, отладки сетевых проблем и оптимизации производительности.
Где используется
- Загрузка веб-страниц (HTML, CSS, JS, изображения)
- API-запросы (REST, GraphQL)
- Отправка форм
- Загрузка/скачивание файлов
- WebSocket-хендшейк начинается как HTTP
Структура HTTP-запроса
POST /api/users HTTP/1.1 ← Стартовая строка (метод, путь, версия)
Host: example.com ← Заголовки
Content-Type: application/json
Authorization: Bearer eyJ...
Content-Length: 45
← Пустая строка (разделитель)
{"name": "Антон", "age": 25} ← Тело запроса (body)
Структура HTTP-ответа
HTTP/1.1 201 Created ← Стартовая строка (версия, статус, текст)
Content-Type: application/json
Set-Cookie: session=abc123
Cache-Control: no-cache
← Пустая строка
{"id": 1, "name": "Антон"} ← Тело ответа
HTTP-методы
// GET — получить данные (без тела запроса)
fetch('/api/users');
fetch('/api/users/42');
// POST — создать ресурс
fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Антон', email: 'a@mail.ru' }),
});
// PUT — полная замена ресурса
fetch('/api/users/42', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Антон', email: 'new@mail.ru', age: 26 }),
});
// PATCH — частичное обновление ресурса
fetch('/api/users/42', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'new@mail.ru' }),
});
// DELETE — удалить ресурс
fetch('/api/users/42', { method: 'DELETE' });
// HEAD — как GET, но без тела ответа (только заголовки)
// OPTIONS — узнать допустимые методы (используется в CORS preflight)
Свойства методов
| Метод | Идемпотентный | Безопасный | Тело запроса | Тело ответа |
|---|---|---|---|---|
| GET | Да | Да | Нет | Да |
| POST | Нет | Нет | Да | Да |
| PUT | Да | Нет | Да | Да |
| PATCH | Нет | Нет | Да | Да |
| DELETE | Да | Нет | Опционально | Опционально |
| HEAD | Да | Да | Нет | Нет |
Идемпотентный — повторный запрос даёт тот же результат. Безопасный — не изменяет данные на сервере.
Коды состояния (Status Codes)
1xx — Информационные
100 Continue — сервер готов принять тело запроса
101 Switching Protocols — переключение протокола (WebSocket upgrade)
2xx — Успех
200 OK — запрос выполнен успешно
201 Created — ресурс создан (после POST)
204 No Content — успех, но тело ответа пустое (после DELETE)
3xx — Перенаправление
301 Moved Permanently — ресурс переехал навсегда (SEO: обновить ссылки)
302 Found — временное перенаправление
304 Not Modified — ресурс не изменился (используй кэш)
307 Temporary Redirect — временный redirect, метод сохраняется
308 Permanent Redirect — постоянный redirect, метод сохраняется
4xx — Ошибка клиента
400 Bad Request — неверный запрос (невалидный JSON, не те параметры)
401 Unauthorized — не авторизован (нет/невалидный токен)
403 Forbidden — доступ запрещён (авторизован, но нет прав)
404 Not Found — ресурс не найден
405 Method Not Allowed — метод не поддерживается для этого URL
409 Conflict — конфликт (ресурс уже существует)
422 Unprocessable Entity — валидация не прошла
429 Too Many Requests — превышен лимит запросов (rate limiting)
5xx — Ошибка сервера
500 Internal Server Error — общая ошибка сервера
502 Bad Gateway — сервер-прокси получил невалидный ответ
503 Service Unavailable — сервер временно недоступен
504 Gateway Timeout — прокси не дождался ответа от upstream
Заголовки (Headers)
Заголовки запроса
fetch('/api/data', {
headers: {
// Тип содержимого
'Content-Type': 'application/json',
// Авторизация
'Authorization': 'Bearer eyJhbGciOiJIUzI1...',
// Что клиент принимает
'Accept': 'application/json',
'Accept-Language': 'ru,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
// Кэширование
'Cache-Control': 'no-cache',
'If-None-Match': '"abc123"', // ETag для 304
// Куки
'Cookie': 'session=xyz; theme=dark',
// Пользовательские
'X-Request-ID': 'uuid-123',
},
});
Заголовки ответа
Content-Type: application/json; charset=utf-8
Content-Length: 1234
Cache-Control: max-age=3600, public
ETag: "abc123"
Set-Cookie: session=xyz; HttpOnly; Secure; SameSite=Strict
Access-Control-Allow-Origin: https://example.com
X-RateLimit-Remaining: 99
HTTP/2
HTTP/1.1:
- Один запрос за соединение (или pipelining, но с head-of-line blocking)
- Заголовки в текстовом виде
- Нет приоритетов
HTTP/2:
- Мультиплексирование: много запросов по одному соединению
- Бинарный протокол (быстрее парсить)
- Сжатие заголовков (HPACK)
- Server Push: сервер отправляет ресурсы до запроса
- Приоритизация потоков
HTTP/1.1: 6 параллельных соединений к одному домену
┌──req1──resp1──┐ ┌──req2──resp2──┐ ┌──req3──resp3──┐
├──req4──resp4──┤ ├──req5──resp5──┤ ├──req6──resp6──┤
HTTP/2: 1 соединение, все запросы параллельно
┌──req1──req2──req3──req4──req5──req6──┐
│ resp3 resp1 resp5 resp2 resp4 resp6 │
└──────────────────────────────────────┘
HTTP/3
HTTP/2 → TCP (head-of-line blocking на уровне TCP)
HTTP/3 → QUIC (UDP) — нет blocking, быстрее на плохих сетях
Преимущества HTTP/3:
- 0-RTT connection setup (мгновенное подключение при повторном визите)
- Нет head-of-line blocking
- Миграция соединения при смене сети (Wi-Fi → 4G)
- Встроенное шифрование (TLS 1.3)
Практический пример: работа с fetch
// Полный пример с обработкой ошибок
async function apiRequest(url, options = {}) {
const config = {
headers: {
'Content-Type': 'application/json',
...options.headers,
},
...options,
};
if (options.body && typeof options.body === 'object') {
config.body = JSON.stringify(options.body);
}
const response = await fetch(url, config);
// fetch НЕ бросает ошибку при 4xx/5xx
if (!response.ok) {
const error = await response.json().catch( => ({}));
throw new Error(error.message || `HTTP ${response.status}`);
}
// 204 No Content — нет тела
if (response.status === 204) return null;
return response.json();
}
// Использование
try {
const user = await apiRequest('/api/users', {
method: 'POST',
body: { name: 'Антон', email: 'a@mail.ru' },
});
console.log('Создан:', user);
} catch (err) {
console.error('Ошибка:', err.message);
}
Частые ошибки
- PUT вместо PATCH — PUT заменяет ресурс целиком, PATCH обновляет частично
- GET с телом — технически возможно, но на практике серверы/прокси могут отбросить тело
- Не проверяют
response.ok— fetch не бросает ошибку при 404/500 - Путают 401 и 403 — 401 = «кто ты?» (нет токена), 403 = «тебе нельзя» (есть токен, нет прав)
- Нет Content-Type — сервер не знает как парсить тело запроса
- Не обрабатывают сетевые ошибки — fetch бросает ошибку только при сетевых проблемах, не при HTTP-ошибках
Практика
- Отправить GET, POST, PUT, PATCH, DELETE запросы через fetch
- Изучить запросы в DevTools → Network: заголовки, статусы, тело
- Написать обёртку над fetch с обработкой ошибок и retry
- Проверить HTTP/2: открыть сайт → DevTools → Network → Protocol
- Реализовать кэширование через ETag и If-None-Match
Связанные темы
- REST API — архитектура API поверх HTTP
- CORS — контроль доступа между доменами
- Клиент-серверное взаимодействие — полный цикл запрос/ответ
- JWT — авторизация через HTTP-заголовки