Этап «давайте сначала изучим стандарты» при обучении веб‑разработке иногда сразу пропускают (или рассматривают буквально в двух словах), переходя к фреймворкам, абстракциям и решениям, которые за вас уже приняли авторы этих фреймворков. В моменте это удобно, можно быстро написать работающий код, не зная что происходит под капотом. Но потом возникают вопросы: «А где в конкретном фреймворке личное решение автора, а где следование стандартам?», «Будет ли та или иная логика аналогично реализована в другом фреймворке, если придется на него переехать?».
В этой статье мы разберем все 9 методов HTTP-запросов, опираясь на тексты документов, которыми эти методы определены (RFC 9110 и RFC 5789).
Статья подойдёт тем, кто делает первые шаги в веб-разработке и хочет понять HTTP глубже, чем это позволяют туториалы фреймворков.
Если вы еще не особо понимаете, как в целом устроен обмен данными по HTTP-протоколу, то рекомендую сначала прочитать статью Андрея Созыкина "Протокол HTTP", здесь, на Хабре.
Что такое методы в протоколе HTTP и какими спецификациями они определены
Метода запроса — это основной источник семантики запроса; он указывает цель, с которой клиент сделал запрос, и что клиент ожидает получить в качестве успешного результата. — RFC 9110, секция 9.1
Метод позволяет заключить между клиентом и сервером соглашение: клиент объявляет, что хочет от сервера, сервер обязуется реагировать соответственно. Один и тот же URI при разных методах означает разные операции над ресурсом.
Метод указывается в строке запроса (request line), первой строке HTTP-запроса в формате метод URI версия-протокола.
GET /articles/42 HTTP/1.1
где GET — имя метода. По соглашению имена методов пишутся всегда заглавными буквами.
Всего определено 9 методов (8 в RFC 9110 и один, PATCH, в RFC 5789).
Метод | Описание по RFC |
|---|---|
GET | Передать текущее представление целевого ресурса |
HEAD | То же что GET, но без тела ответа |
POST | Выполнить специфичную для ресурса обработку тела запроса |
PUT | Создать или заменить состояние целевого ресурса телом запроса |
PATCH | Применить частичные изменения к целевому ресурсу |
DELETE | Отвязать целевой ресурс от его URI |
CONNECT | Установить туннель к серверу, идентифицированному целевым ресурсом |
OPTIONS | Описать параметры взаимодействия для целевого ресурса |
TRACE | Выполнить петлевой тест сообщения по пути к целевому ресурсу |
В прикладной веб-разработке регулярно используются GET, HEAD, POST, PUT, PATCH и DELETE.
Почему важно знать требования спецификаций, если есть веб-фреймворки
Фреймворки берут на себя маршрутизацию, валидацию и сериализацию, но не семантику методов. Правильные коды ответов, идемпотентность PUT, атомарность PATCH, заголовок Location при создании ресурса, всё это остаётся на стороне разработчика.
Спецификации описывают, каких правил должны придерживаться клиент и сервер при работе по HTTP. Фреймворк помогает написать сервер, но не гарантирует что он полностью соблюдает эти правила.
Свойства методов
Прежде чем разбирать каждый метод, нужно понять 3 фундаментальных свойства, которые определяет RFC 9110.
Safe (безопасный)
Метод безопасен, если его семантика по существу доступна только для чтения. Клиент, отправляя безопасный запрос, не ожидает и не запрашивает изменения состояния на сервере. Это не гарантия, сервер физически может что-то изменить (логи, счётчики), но клиент в этом не виновен.
Безопасные методы: GET, HEAD, OPTIONS, TRACE.
Idempotent (идемпотентный)
Метод идемпотентен, если несколько идентичных запросов дают один и тот же эффект на сервере. Речь идёт именно о стороне сервера, а не о содержимом ответа.
Все безопасные методы идемпотентны по определению: их ожидаемая семантика не предполагает изменения состояния ресурса по воле клиента. Дополнительно идемпотентны: PUT, DELETE.
Cacheable (кешируемый)
Ответ на запрос можно хранить в кеше и использовать повторно. Это зависит не только от метода, но и от заголовков Cache-Control, Vary, Expires и т. д.
Кешируемые по умолчанию: GET, HEAD.
POST и PATCH только при явных заголовках кешируемости (Cache-Control + Content-Location).
Метод GET
GET запрашивает передачу текущего выбранного представления целевого ресурса.
GET выступает первичным механизмом получения информации по протоколу HTTP. Если вы открываете страницу в браузере, переходите по ссылке, вбиваете URL в адресную строку, браузер отправляет запрос с методом GET.
Тело в GET-запросе технически допустимо, но семантически не определено. Сервер вправе отклонить такой запрос или закрыть соединение. Клиент не должен отправлять тело в GET, если сервер явно не обозначил поддержку этого.
Объем запрашиваемого GET-запросом ресурса можно уменьшить, отправив в запросе заголовок Range с указанием требуемого диапазона байт.
Ответ на GET-запрос кешируем. Кешированный ответ может использоваться для последующих HEAD-запросов на тот же URI.
Статусы ответов
Основной код 200 OK. Он возвращается при успешной передаче ресура (например, HTML-страницы для отображения в браузере).
Два дополнительных кода:
206 Partial Content — ответ на Range-запрос. Клиент, к примеру, передал заголовок
Range: bytes=0-1023, сервер вернул только запрошенный фрагмент. Используется при докачке файлов и потоковом видео. Сервер обязан включить заголовок Content-Range с указанием отданного диапазона и полного размера ресурса304 Not Modified — ответ на условный GET-запрос. Клиент передал заголовок
If-None-Matchс кешированным значениемETagилиIf-Modified-Sinceс датой последнего полученного ответа. Если ресурс не изменился, сервер возвращает 304 без тела. Клиент использует закешированную копию. Это основной механизм валидации кеша по RFC.
Оба кода не являются ошибками — это успешные ответы с оптимизированной передачей данных. 304 формально относится к классу 3xx, но семантически означает "твоя копия актуальна", а не редирект.
Справочно:
ETag— заголовок ответа сервера, который содержит уникальный идентификатор текущей версии ресурса. Как правило это хеш содержимого: изменился ресурс, изменилсяETag. Клиент сохраняет это значение и при следующем запросе передаёт его в заголовкеIf-None-Match;Last-Modified— заголовок ответа сервера, который содержит дату и время последнего изменения ресурса. Клиент сохраняет это значение и при следующем запросе передаёт его в заголовкеIf-Modified-Since;If-None-Match— заголовок запроса клиента, в котором клиент передаёт сохранённое значениеETag. Сервер сравнивает его с текущимETagресурса.
Примеры
GET /articles/42 HTTP/1.1 Host: example.com Accept: application/json
# Через curl curl -i https://example.com/articles/42 # Частичный запрос (Range) curl -i -H "Range: bytes=0-1023" https://example.com/files/big.pdf
Метод HEAD
"Метод HEAD идентичен GET, за исключением того, что сервер НЕ ДОЛЖЕН отправлять тело в ответе."
HEAD предназначен для получения метаданных о ресурсе (заголовков ответа) без необходимости получать от сервера тело ответа.
В RFC приводятся типичные кейсы использования HEAD-запросов:
проверка гиперссылок (существует ли ресурс)
обнаружение свежих изменений (валидация кеша).
Сервер должен возвращать те же заголовки, что и при GET. Допустимое исключение: заголовки, значение которых определяется только в процессе генерации тела.
К примеру, у сервера есть динамический эндпоинт, где значение для заголовка Content-Length вычисляется только в процессе генерации тела. Сервер буферизует ответ, пока не накопит минимальный объём данных, и только тогда знает его размер.
При GET это работает нормально. При HEAD сервер стоит перед выбором:
сгенерировать тело целиком, узнать точное значение для
Content-Length, отправить только заголовки, тело отбросить.не генерировать тело вовсе, но тогда
Content-Lengthв ответе на HEAD-запрос будет отсутствовать.
По RFC второй вариант предпочтительнее, потому что HEAD запрашивается ради эффективности. Генерировать тело только ради того, чтобы узнать его длину и сразу же отбросить, противоречит самому смыслу HEAD.
HEAD-ответ кешируем. Более того, он может инвалидировать ранее кешированный GET-ответ, если метаданные ответа изменились.
RFC требует поддержки HEAD на любом URI, где поддерживается GET.
Замечание о реализации этого требования в конкретных фреймворках. Starlette в Python автоматически добавляет HEAD к любому GET-маршруту. FastAPI, хоть и опирается на Starlette, но переопределяет множество методов в классе
APIRouteи эту логику не наследует, HEAD нужно добавлять явно через@app.api_route("/path", methods=["GET", "HEAD"]).
Статусы ответов
Основной код 200 OK.
304 Not Modified — ответ на условный HEAD-запрос (по аналогии с GET).
Примеры
HEAD /files/report.pdf HTTP/1.1 Host: example.com
# curl -I отправляет именно HEAD curl -I https://example.com/files/report.pdf # Ответ содержит заголовки, но не тело: # HTTP/1.1 200 OK # Content-Type: application/pdf # Content-Length: 204800 # ETag: "abc123"
Метод POST
"POST запрашивает, чтобы целевой ресурс обработал представление, вложенное в запрос, в соответствии с собственной семантикой ресурса."
POST — метод с наиболее широкой семантикой. RFC перечисляет типичные варианты применения:
передача данных обработчику (например, данных из HTML-формы);
публикация в блоге, форуме, новостной ленте;
создание нового ресурса, URI которого ещё не известен клиенту;
добавление данных к существующему ресурсу.
POST не идемпотентен и не безопасен.
При успешном создании ресурса сервер должен вернуть статус 201 Created с заголовком Location, указывающим URI созданного ресурса.
Ответ кешируем только при наличии явных заголовков свежести и заголовка Content-Location со значением, совпадающим с URI запроса.
Статусы ответов
Явные предписания только два:
201 Created + заголовок Location при успешном создании ресурса
303 See Other + заголовок Location: сервер обработал POST и перенаправляет клиента на GET к результату (зачем это см. PRG-паттерн).
Три кода которые никогда не могут быть ответом на POST:
206 Partial Content — частичный контент, специфичен для Range-запросов, POST их не делает
304 Not Modified — механизм условных запросов, к POST неприменим
416 Range Not Satisfiable — неудовлетворимый Range, по той же причине что и 206.
Примеры
POST /articles HTTP/1.1 Host: example.com Content-Type: application/json {"title": "HTTP-методы", "body": "..."}
curl -i -X POST https://example.com/articles \ -H "Content-Type: application/json" \ -d '{"title": "HTTP-методы"}' # Ожидаемый ответ: # HTTP/1.1 201 Created # Location: /articles/43
Метод PUT
"PUT запрашивает, чтобы состояние целевого ресурса было создано или заменено состоянием, определённым представлением, вложенным в тело запроса"
PUT означает: "установи состояние ресурса именно таким". Успешный PUT подразумевает, что последующий GET на тот же URI вернёт эквивалентное представление.
Критерием для разграничения PUT и POST выступает то, известен ли изначально клиенту точный URI ресурса:
если да, то используется PUT.
PUT /users/42означает "установи состояние ресурса по этому адресу вот таким". Если ресурса не было, то сервер создаст, если был, то полностью обновит.
если точного URI ресурса нет, то используется метод POST. В этом случае URI нового ресурса определяет сервер.
POST /usersозначает "создай пользователя, адрес придумай сам". Сервер вернёт точный URI созданного ресурса (например,/users/42) в заголовке ответаLocation.
Частая ошибка — использовать PUT для частичного обновления. PUT заменяет ресурс целиком. Если нужно изменить только одно поле, то нужно выбрать метод PATCH (о нем ниже).
PUT идемпотентен: несколько одинаковых запросов дают тот же результат, что и один.
Ответы не кешируются. Успешный PUT инвалидирует кешированные ответы на тот же URI.
Сервер вправе вернуть ETag / Last-Modified в ответе на PUT только если сохранил данные без каких-либо трансформаций (включая, к примеру, нормализацию).
Статусы ответов
Если ресурс не существовал и создан, сервер обязан вернуть статус 201 Created.
Если ресурс существовал и изменён, сервер обязан вернуть 200 OK или 204 No Content. 204 используют когда нет смысла возвращать тело, например при обновлении конфига или замене файла. 200 уместен, когда сервер возвращает обновлённое представление ресурса.
Примеры
PUT /articles/42 HTTP/1.1 Host: example.com Content-Type: application/json {"title": "Обновлённый заголовок", "body": "..."}
curl -i -X PUT https://example.com/articles/42 \ -H "Content-Type: application/json" \ -d '{"title": "Обновлённый заголовок", "body": "..."}'
Метод PATCH
PATCH зафиксирован в отдельном документе RFC 5789.
"PATCH запрашивает, чтобы набор изменений, описанных в теле запроса, был применён к ресурсу, идентифицированному URI запроса."
Ключевое слово тут "набор изменений". В отличие от PUT, который заменяет ресурс целиком, PATCH передаёт инструкции по модификации в формате, определяемом в заголовке Content-Type запроса. Этот формат называется "патч" (patch document).
Разница между PUT и PATCH формулируется в RFC так:
в PUT тело запроса — новое состояние ресурса, которое нужно сохранить.
в PATCH тело запроса — описание того, как изменить текущее состояние ресурса.
Характеристики PATCH:
не безопасен
не идемпотентен
не кешируется (если обратное не указано явно)
PATCH не идемпотентен, потому что результат зависит от текущего состояния ресурс�� в момент применения патча. Инструкция "удалить строку 5" даст разный результат в зависимости от того, что там сейчас находится. Повторный запрос может либо сломать ресурс, либо вернуть ошибку. Вместе с тем PATCH может быть идемпотентным. RFC указывает, что некоторые форматы патчей не зависят от базового состояния, например, добавление строки в лог-файл или вставка неколлизионных строк в таблицу. В таких случаях идемпотентность возможна, но она в зоне ответственности клиента и формата патча, а не гарантия метода.
RFC устанавливает требование атомарности патча. Патч применяется полностью или не применяется вовсе. Частичное применение запрещено.
Форматы патчей
Единого формата нет. Сервер принимает те форматы, которые сам поддерживает для конкретного ресурса. Клиент указывает формат через заголовок Content-Type. Два наиболее распространённых формата:
application/json-patch+json(определен в RFC 6902) — список операций над JSON-документом:add,remove,replace,move,copy,test.application/merge-patch+json(определен в RFC 7396) — слияние объектов: поля сnullудаляются, остальные обновляются или добавляются, отсутствующие в патче остаются нетронутыми.
Конкурентные изменения и заголовок If-Match
Поскольку PATCH применяется к конкретному состоянию ресурса, два параллельных запроса опаснее чем два параллельных PUT. PUT просто перезаписывает, PATCH сильнее зависит от текущего состояния ресурса. RFC рекомендует использовать условные запросы: клиент передаёт заголовок If-Match с текущим значением заголовка ETag ресурса, и сервер применяет патч только если состояние не изменилось. Если кто-то успел изменить ресурс между GET и PATCH, сервер вернёт 412 Precondition Failed.
Обработка ошибок
RFC 5789 (разд. 2.2) перечисляет коды ошибок и ситуации, в которых их следует возвращать:
Ситуация | Код |
|---|---|
Некорректный формат patch document |
|
Формат не поддерживается сервером |
|
Формат понятен, но применить нельзя (например, XML перестанет быть well-formed) |
|
Ресурс не существует, и формат не может быть применён к null |
|
Конфликт состояния (структура, которую нужно изменить, не существует) |
|
Precondition не выполнен ( |
|
Сервер не может обработать конкурентные запросы |
|
Примеры
PATCH /articles/42 HTTP/1.1 Host: example.com Content-Type: application/json-patch+json If-Match: "e0023aa4e" [ {"op": "replace", "path": "/title", "value": "Новый заголовок"}, {"op": "remove", "path": "/draft"} ]
PATCH /articles/42 HTTP/1.1 Host: example.com Content-Type: application/merge-patch+json {"title": "Новый заголовок", "draft": null}
curl -i -X PATCH https://example.com/articles/42 \ -H "Content-Type: application/json-patch+json" \ -H 'If-Match: "e0023aa4e"' \ -d '[{"op": "replace", "path": "/title", "value": "Новый заголовок"}]'
Метод DELETE
"DELETE запрашивает, чтобы сервер удалил связь между целевым ресурсом и его текущей функциональностью."
DELETE, как указывает RFC, похож на rm в UNIX. Он удаляет связь между URI и ресурсом, но не обязательно сам ресурс. Реальное удаление данных на сервере является деталью реализации, спецификация её не регламентирует. DELETE уместен там где физическое удаление действительно предусмотрено: файл в хранилище, статья в блоге, запись в справочнике.
Коды ответа по RFC:
Код | Значение |
|---|---|
Действие будет выполнено, но ещё не выполнено | |
Выполнено, тело ответа отсутствует | |
Выполнено, тело содержит описание результата |
DELETE идемпотентен: повторный DELETE не должен менять итоговое состояние сервера; на практике серверы часто отвечают 404 Not Found или 410 Gone, но это уже вопрос конкретной реализации.
Ответы не кешируются. Успешный DELETE инвалидирует кешированные ответы.
Примеры
DELETE /articles/42 HTTP/1.1 Host: example.com
curl -i -X DELETE https://example.com/articles/42 # Типичные ответы: # HTTP/1.1 204 No Content # HTTP/1.1 404 Not Found (уже удалён)
Метод OPTIONS
"OPTIONS запрашивает информацию о параметрах взаимодействия, доступных для целевого ресурса."
OPTIONS позволяет клиенту узнать, какие методы и параметры доступны для ресурса без выполнения каких-либо действий над ним.
Два режима работы:
OPTIONS * HTTP/1.1 # Запрос к серверу в целом OPTIONS /articles/42 HTTP/1.1 # Запрос к конкретному ресурсу
OPTIONS * — запрос к серверу в целом; полезен как «ping» для проверки совместимости с HTTP/1.1. Сервер при успешном ответе на OPTIONS обычно возвращает заголовки, описывающие доступные возможности ресурса, например Allow.
Главный кейс сегодня CORS preflight. Браузер автоматически отправляет OPTIONS перед "небезопасным" cross-origin запросом, чтобы проверить, разрешает ли сервер операцию.
Метод CONNECT
"CONNECT запрашивает, чтобы получатель установил туннель к целевому серверу, идентифицированному в запросе."
CONNECT — специальный метод для создания сквозного туннеля, как правило через прокси. Используется прежде всего для HTTPS через HTTP-прокси (туннель под TLS). Бэкендеры обычно с ним не работают. Метод нужен тем, кто пишет или конфигурирует HTTP-прокси (nginx в режиме прокси, корпоративные шлюзы). Если пишете бизнес-логику на FastAPI или Django, этот раздел для общей картины.
Метод TRACE
"TRACE запрашивает, чтобы получатель вернул полученное сообщение обратно отправителю."
Сервер (или первый получатель с Max-Forwards: 0) отражает полученный запрос обратно в теле ответа 200 OK. Это диагностический инструмент: клиент видит, что реально пришло на другой конец цепочки. На практике большинство серверов и CDN отключают TRACE из соображений безопасности.
Сравнительная таблица свойств основных методов HTTP-запросов согласно RFC
Метод | Безопасность | Идемпотентность | Кешируемость |
|---|---|---|---|
GET | ✅ | ✅ | ✅ |
HEAD | ✅ | ✅ | ✅ |
POST | ❌ | ❌ | ⚠️ только явно |
PUT | ❌ | ✅ | ❌ |
PATCH | ❌ | ❌ | ⚠️ только явно |
DELETE | ❌ | ✅ | ❌ |
