HTTP-запросы: как устроены запрос и ответ, методы и коды состояния

HTTP-запросы: как устроены запрос и ответ, методы и коды состояния Полезное

HTTP-запрос — это сообщение, которое клиент (браузер, мобильное приложение, утилита curl) отправляет серверу по протоколу HTTP, чтобы получить ресурс или передать данные. В паре с ним идет HTTP-ответ — сообщение, которым сервер отвечает клиенту. Это разные сообщения с похожей структурой, и путать их не стоит: запрос задает намерение (метод + адрес), ответ сообщает результат (код состояния + данные).

Ниже разберем по порядку: из чего состоят запрос и ответ, чем отличаются методы GET, POST, PUT, PATCH и DELETE, что означают коды 1xx-5xx, зачем нужен HTTPS и чем различаются HTTP/1.1, HTTP/2 и HTTP/3. Материал рассчитан на новичков, которые уже видели браузер и командную строку.

Как работает HTTP: клиент и сервер

HTTP — это протокол прикладного уровня для обмена данными в вебе (расшифровка — HyperText Transfer Protocol, «протокол передачи гипертекста»). Работает он по модели «запрос — ответ»: клиент шлет запрос, сервер его обрабатывает и возвращает ответ. Одно соединение может нести много таких пар.

По умолчанию обычный HTTP использует TCP-порт 80, а защищенный HTTPS — порт 443. Порт можно задать явно в адресе (например, :8080), тогда клиент обратится именно к нему.

Важно развести два соседних понятия. Сам HTTP отвечает за формат сообщений и семантику методов и кодов. Доставку байтов по сети обеспечивает нижележащий транспорт — TCP (в HTTP/1.1 и HTTP/2) или QUIC поверх UDP (в HTTP/3). Ниже видно, что версия HTTP — это в основном про то, как сообщение упаковано и передано, а не про смысл GET или кода 404.

Структура HTTP-сообщения

Запрос и ответ устроены по одной схеме из четырех частей:

  • Стартовая строка — одна строка. У запроса она задает метод, цель и версию; у ответа — версию, код состояния и его пояснение.
  • Заголовки (headers) — строки вида Имя: Значение, по одной на строку. Уточняют запрос или ответ: тип содержимого, длину, кодировку, авторизацию.
  • Пустая строка — отделяет заголовки от тела и сообщает, что метаданные закончились.
  • Тело (body) — опциональные данные: у запроса это, например, отправляемая форма или JSON, у ответа — HTML-страница, картинка или JSON.

Имена заголовков нечувствительны к регистру, но по соглашению каждое слово пишут с заглавной буквы (Content-Type, User-Agent).

Стартовая строка запроса

Стартовая строка запроса состоит из трех частей: метод, цель (обычно путь к ресурсу) и версия протокола. Например:

GET /journal/ HTTP/1.1

Здесь метод — GET, цель — путь /journal/, версия — HTTP/1.1. Домен в этой строке не пишут: его несет обязательный заголовок Host. Цель может включать строку запроса (query string) — параметры после ?, разделенные &: GET /search?q=http&page=2 HTTP/1.1.

Методы: GET, POST, PUT, PATCH, DELETE

Метод говорит, что сделать с ресурсом по указанному адресу. Помимо перечисленных есть HEAD (как GET, но сервер возвращает только заголовки без тела) и OPTIONS (какие методы допустимы для ресурса).

У методов есть два свойства, которые часто смешивают, но они про разное:

  • Безопасный (safe) — запрос не меняет состояние на сервере (только чтение).
  • Идемпотентный (idempotent) — повтор того же запроса оставляет состояние сервера таким же, как после одного запроса. Любой безопасный метод идемпотентен, но не наоборот.
Метод Что делает Безопасный Идемпотентный
GET запрашивает ресурс да да
HEAD как GET, но только заголовки да да
OPTIONS сообщает допустимые методы да да
POST отправляет данные, создает/запускает обработку нет нет
PUT помещает ресурс целиком по адресу нет да
PATCH частично изменяет ресурс нет обычно нет
DELETE удаляет ресурс нет да

PUT идемпотентен, потому что кладет ресурс в заданное состояние: сколько раз ни повтори тот же PUT, на сервере будет один и тот же результат. POST не идемпотентен — два одинаковых POST на создание заказа обычно создадут два заказа.

DELETE идемпотентен по итоговому состоянию: первый запрос удаляет ресурс, второй уже ничего не меняет (ресурса нет). Код при этом может отличаться — первый вернет 200 или 204, второй нередко 404, но состояние сервера одинаково. PATCH идемпотентен не всегда: он зависит от вида правки (например, «увеличить счетчик на 1» повтор изменит).

Коды состояния: группы 1xx-5xx

Стартовая строка ответа несет код состояния (status code) — трехзначное число, по которому клиент понимает исход. Коды делятся на пять групп по первой цифре:

Группа Смысл Частые примеры
1xx информационные, промежуточные 100 Continue, 101 Switching Protocols
2xx успех 200 OK, 201 Created, 204 No Content
3xx перенаправление 301 Moved Permanently, 302 Found, 304 Not Modified
4xx ошибка на стороне клиента 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 429 Too Many Requests
5xx ошибка на стороне сервера 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable

Пара соседних кодов, которые путают: 401 — «не аутентифицирован» (сервер не знает, кто ты, нужен вход), а 403 — «аутентифицирован, но доступ запрещен». И 301 (перенаправление навсегда, поисковики переносят вес на новый URL) против 302 (временно, старый URL остается основным).

Тело в ответе есть не всегда: 204 No Content и 304 Not Modified его не несут по определению — вся нужная информация в заголовках и самом коде.

Пример реального запроса и ответа

Так выглядит типичный обмен по HTTP/1.1 (это упрощенный пример; точный набор заголовков и их значения зависят от клиента, сервера и момента). Запрос:

GET /journal/ HTTP/1.1
Host: otus.ru
User-Agent: curl/8.4.0
Accept: */*

Пустая строка в конце обязательна — она завершает заголовки. Тела у такого GET нет. Ответ:

HTTP/1.1 200 OK
Server: nginx
Date: Thu, 18 Sep 2026 09:00:00 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 51234

<!doctype html>
<html>...

Стартовая строка ответа HTTP/1.1 200 OK сообщает версию, код 200 и пояснение OK. Дальше идут заголовки, пустая строка и тело — HTML-страница.

Как отправить запрос из Python

Ниже — вариант через популярную библиотеку requests. Она не входит в стандартную библиотеку Python, поэтому в изолированной среде или песочнице модуля может не быть — тогда сначала pip install requests. Пример также требует доступа в сеть, поэтому вывод зависит от текущего ответа сервера.

# pip install requests  (в стандартную библиотеку не входит; нужен доступ в сеть)
import requests

resp = requests.get("https://otus.ru/journal/", timeout=10)
print(resp.status_code)               # 200 при успешном ответе
print(resp.request.method)            # GET
print(resp.headers["Content-Type"])   # напр.: text/html; charset=UTF-8
# Точные значения зависят от сети и текущего ответа сервера

Если ставить пакеты нельзя, тот же GET делает модуль urllib из стандартной библиотеки (доступ в сеть по-прежнему нужен):

# Только стандартная библиотека - без установки пакетов (нужен доступ в сеть)
from urllib.request import Request, urlopen

req = Request("https://otus.ru/journal/", method="GET")
with urlopen(req, timeout=10) as resp:
    print(resp.status)             # 200
    print(resp.headers["Server"])  # напр.: nginx

Для POST с телом (requests) данные обычно кладут в JSON, а метод и заголовки библиотека проставит сама:

import requests

resp = requests.post(
    "https://httpbin.org/post",
    json={"login": "user", "id": 42},
    timeout=10,
)
print(resp.status_code)        # 200
print(resp.json()["json"])     # {'login': 'user', 'id': 42}
# httpbin возвращает присланное тело обратно; вывод зависит от доступности сервиса

HTTPS: тот же HTTP, но по шифрованному каналу

HTTPS — это тот же HTTP, но поверх защищенного соединения TLS (порт 443). TLS решает три задачи: шифрует содержимое (стартовую строку, заголовки и тело), проверяет подлинность сервера по его сертификату и защищает данные от изменения в пути. Без него запросы идут открытым текстом, и их можно прочитать или подменить.

Здесь важна граница упрощения. HTTPS скрывает путь, заголовки и тело, но не прячет полностью сам факт соединения: IP-адрес сервера виден, а имя домена обычно видно на этапе установки TLS (поле SNI). И «замок» в браузере означает лишь шифрование канала до сервера, а не то, что сайт добросовестный, — фишинговый сайт тоже может работать по HTTPS.

HTTP/1.1, HTTP/2 и HTTP/3: коротко о различиях

Смысл методов и кодов от версии не меняется — меняется то, как сообщение упаковано и доставлено. Кратко:

Версия Год Транспорт Что нового
HTTP/1.1 1997 TCP, текстовый формат постоянные соединения, но запросы в соединении идут по очереди
HTTP/2 2015 TCP, бинарные фреймы мультиплексирование потоков в одном соединении, сжатие заголовков (HPACK)
HTTP/3 2022 QUIC поверх UDP убирает блокировку очереди на транспорте, быстрее устанавливает соединение (QPACK)

Главная проблема HTTP/1.1 — блокировка очереди (head-of-line): медленный ответ задерживает следующие в том же соединении. HTTP/2 решил это на уровне HTTP, введя параллельные потоки, но осталась блокировка на уровне TCP: потеря одного пакета тормозит все потоки. HTTP/3 переносит транспорт на QUIC (UDP), где потоки независимы, поэтому потеря пакета в одном потоке не блокирует другие.

Выводы

  • HTTP-запрос и HTTP-ответ — разные сообщения одной структуры: стартовая строка, заголовки, пустая строка и опциональное тело.
  • Метод задает намерение; свойства «безопасный» и «идемпотентный» — про разное: GET/HEAD/OPTIONS безопасны, идемпотентны также PUT и DELETE, а POST и PATCH в общем случае нет.
  • Код состояния читается по группам: 2xx — успех, 3xx — перенаправление, 4xx — ошибка клиента, 5xx — ошибка сервера; 401 и 403, 301 и 302 — разные по смыслу.
  • HTTPS шифрует и заверяет канал (TLS, порт 443), но «замок» не гарантирует добросовестности сайта.
  • Версии HTTP/1.1, HTTP/2 и HTTP/3 меняют упаковку и транспорт, а не смысл методов и кодов; HTTP/3 работает поверх QUIC (UDP).

Где применяется / связь с практикой

Понимание HTTP — базовый навык не только для веб-разработчика, но и для того, кто настраивает и диагностирует сети: за кодами 502/504, редиректами и таймаутами часто стоят прокси, балансировщики и правила фаервола, а не сам код приложения. Читать заголовки, различать 4xx и 5xx и понимать, где TCP, а где QUIC, — это ежедневная работа при разборе инцидентов.

Освойте тему на практике

Системно эти темы (протоколы, TCP/IP, HTTP, TLS, диагностика трафика) разбирают на курсе Сетевой инженер. Если хочется сначала посмотреть формат и уровень без обязательств, загляните на открытые уроки Otus — там разбирают отдельные темы вживую.

Смежные темы: HTTP-сервер на Java: серверные основы на примере своего чата.

FAQ

Чем GET отличается от POST при отправке данных? GET передает параметры в строке запроса внутри URL — они видны в адресе, логах и истории, а длина ограничена; такой запрос удобно кэшировать и повторять. POST кладет данные в тело, они не видны в URL, подходят для больших и приватных данных и для операций, которые меняют состояние.

DELETE идемпотентен, но второй запрос вернул 404 — это ошибка? Нет. Идемпотентность — про итоговое состояние сервера, а не про код: после первого DELETE ресурса уже нет, второй запрос ничего не меняет и корректно отвечает 404. Состояние одинаково, отличается лишь код ответа.

Если у сайта HTTPS и «замок», ему можно доверять? HTTPS гарантирует только шифрование канала и подлинность сертификата для этого домена, но не добросовестность владельца. Мошеннический сайт тоже может получить сертификат и показывать «замок», поэтому проверять стоит само доменное имя, а не только наличие шифрования.

OTUS Журнал
Скидка 5% 12-19 октября на курсы (popup)
Бесплатные открытые уроки (поп-ап)