HTTP QUERY: зачем вебу новый метод, когда есть GET и POST
В июне 2026 года IETF опубликовало RFC 10008, где описан новый HTTP-метод `QUERY`.
IETF, или Internet Engineering Task Force, — это международное инженерное сообщество, которое разрабатывает и утверждает технические стандарты интернета: протоколы, форматы и правила их совместной работы.
Может возникнуть вопрос: а почему нельзя и дальше использовать GET и POST? Если кратко: можно, долгое время их действительно использовали как обходные решения для сложного кейса — когда нужно не создать ресурс, не изменить запись и не запустить бизнес-операцию, а получить выборку данных по условиям. Но условий может быть много: фильтры, сортировки, вложенные группы, JSONPath, SQL-подобные выражения, сложные правила поиска. В `GET` это приходится запихивать в URL. В `POST` можно положить данные в тело запроса, но по семантике он обычно используется для создания ресурсов, запуска обработки или других операций, которые могут изменять состояние системы.
Новый `QUERY` как раз становится компромиссом: у метода есть body, а смысл операции остается поисковым и безопасным. Он позволяет выполнять сложные операции чтения данных, не нарушая семантику HTTP и не упираясь в ограничения длины URL.
Несовершенство GET
Классический вариант запроса выглядит так:
```http
GET /orders?status=active®ion=RU&limit=50&sort=-created_at HTTP/1.1
Host: api.example.com
```
Для простых фильтров GET — это база, привычно и нормально: URL можно легко скопировать, поделиться. Инфраструктура тоже хорошо понимает `GET`: его можно кешировать, безопасно повторять, использовать с условными запросами.
Как только запрос становится сложнее, могут возникнуть проблемы:
GET /orders?status=paid,shipped&created_from=2026-01-01&created_to=2026-06-30&
min_total=10000¤cy=RUB®ion=ural&customer_type=business&include_archived=false&
sort=-created_at&limit=50&fields=id,number,status,total,created_at,customer_name&query=urgent%20delivery
Такой URL плохо читается, его трудно документировать и тестировать. У разных клиентов и серверов могут отличаться ограничения на длину URL. В RFC упоминается рекомендация поддерживать URI хотя бы до 8000 октетов, но на практике запрос проходит через браузер, прокси, балансировщик, веб-сервер, API-gateway, и каждый компонент может иметь свои лимиты.
Октет в URI — это 8 бит, то есть фактически 1 байт.
Рекомендация поддерживать URI длиной хотя бы 8000 октетов означает около 8000 байт, а не обязательно 8000 видимых символов. Для латиницы один символ обычно занимает 1 байт, а кириллица, эмодзи и спецсимволы после кодирования могут занимать больше.
У `QUERY` есть еще один практический нюанс: параметры запроса передаются не в URL, а в теле. Именно поэтому они реже попадают в стандартные логи серверов, прокси и систем аналитики, где обычно сохраняется именно строка запроса. Это особенно важно с точки зрения секьюрности, так как в фильтрах могут оказаться чувствительные данные: email, номера документов, внутренние идентификаторы, условия отбора по клиентам.
Почему не POST
Обычно в таких случаях разработчики используют `POST`:
```http
POST /orders/search HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"status": ["paid", "shipped"],
"createdFrom": "2026-01-01",
"createdTo": "2026-06-30",
"sort": "-created_at",
"limit": 50
}
```
На практике это удобный обходной путь: есть тело запроса, читабельный JSON, можно поместить сложные фильтры и не упаковывать в URL. Но с точки зрения RESTful и для HTTP это всё еще `POST`, а значит, по смыслу он ближе к действию с возможным изменением состояния, чем к обычному чтению данных.
По семантике `POST` обычно означает создание ресурса, запуск обработки или другое действие, которое может изменить состояние системы. Именно поэтому инфраструктура не может автоматически понять, что `POST /orders/search` только читает данные. Для клиента, прокси или браузера это может быть чем угодно: созданием отчета, запуском экспорта, изменением состояния, оформлением заказа.
Из минусов: POST не идемпотентен (опять же: по стандартной семантике, на практике очень часто пренебрегают RESTful-стандартами), и если соединение оборвалось, клиент уже не может просто так повторить запрос: вдруг первый `POST` всё-таки успел что-то изменить?
Идемпотентность — это когда один и тот же запрос можно выполнить несколько раз, а итоговое состояние системы будет таким же, как после одного выполнения.
Аналогично проблемы с кешированием: POST может кешироваться теоретически как GET, но на практике большинство кешей по умолчанию не кешируют POST.
Обычно POST воспринимается как метод для действия, а не для чтения.
Команда разработки может заложить правило, написать в инструкции: конкретно этот `POST` ничего не меняет, а только возвращает результаты поиска. Но клиент, прокси или CDN видят просто `POST` и не могут по самому методу понять, безопасно ли повторять запрос, кешировать ответ или относиться к нему как к обычному чтению данных.
Именно эту проблему и пытается решить новый метод QUERY.
Что предлагает QUERY
`QUERY` выглядит почти как `POST`, но означает другое:
```http
QUERY /orders HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json
{
"status": ["paid", "shipped"],
"createdFrom": "2026-01-01",
"createdTo": "2026-06-30",
"sort": "-created_at",
"limit": 50
}
```
Тело запроса передается как у `POST`, но смысл ближе к `GET`: сервер должен выполнить безопасную и идемпотентную операцию чтения.
Безопасная операция означает, что клиент не просит изменить состояние целевого ресурса. Идемпотентная означает, что повторение запроса не должно создавать новых сущностей или накапливать побочные эффекты. Если во время запроса произошел сетевой сбой, клиенту безопаснее повторить запрос `QUERY`: по смыслу метода он не должен создать дубликаты, повторно запустить операцию записи или изменить данные.
Условно:
GET /users/123
читает пользователя.
POST /users
может создать пользователя.
QUERY /users
ищет пользователей по сложным условиям, переданным в теле.
Главное отличие QUERY от GET и POST
Как я уже сказала ранее, мы используем `GET`, когда запрос можно выразить URI:
GET /products?category=laptop&brand=lenovo
При этом `QUERY` не заменяет все `GET`-запросы. Если фильтр короткий, ссылкой нужно делиться, а результат должен открываться из адресной строки, `GET` остается лучше.
`POST` подходит, когда клиент просит сервер что-то обработать с возможным изменением состояния:
POST /orders
или
POST /reports/export
Даже если ответом будет файл или результат операции, сама операция может создать задачу, запись, заказ, отчет.
`QUERY` нужен для случая между ними: данные для поиска слишком сложные для URL, но сама операция по смыслу остается чтением.

Кеширование и URI для результата
Кроме того, RFC 10008 содержит интересный момент — у `QUERY` есть одна особенность, которой нет у обычного `POST`: сервер может связать сложный запрос с обычной ссылкой, выступив своего рода мостиком.
Например, клиент отправляет сложный запрос на поиск заказов:
```http
QUERY /orders HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json
{
"status": ["paid", "shipped"],
"createdFrom": "2026-01-01",
"createdTo": "2026-06-30",
"minTotal": 10000
}
```
Сервер возвращает найденные заказы:
```http
HTTP/1.1 200 OK
Content-Type: application/json
Content-Location: /orders/results/17
Location: /orders/queries/42
[
{ "id": 123, "status": "paid", "total": 12000 },
{ "id": 124, "status": "shipped", "total": 18500 }
]
```
Заголовок `Content-Location` в данном ответе — это URI, по которому доступно возвращенное представление:
```http
GET /orders/results/17
Host: api.example.com
```
То есть сервер может вернуть не только результат, но и URI, по которому к этому результату или к самому запросу можно будет обратиться позже.
Почему сообщество спорит
Реакция профессионального сообщества ожидаемо противоречивая.
Справедливости ради надо отметить, что RESTful-принципы на практике часто соблюдают неидеально. Во многих проектах можно встретить API, где почти вся логика построена на `POST`-запросах: поиск, изменение, запуск операций, иногда даже чтение справочников. На таком фоне новый метод кажется части разработчиков избыточным: если команды и раньше не стремились строго следовать HTTP-семантике, не факт, что они начнут делать это из-за появления `QUERY`.
Некоторые комментаторы встретили `QUERY` скептически, отмечая, что проблему можно решить и без нового метода. По их мнению, повторяющиеся записи при повторном `POST` — это скорее признак неудачной реализации и плохо написанного кода, а не недостаток самого HTTP: можно использовать идемпотентные ключи, одноразовые токены или другую защиту от дублей. Они также напоминают, что на практике многие команды и так сводят разные действия к `POST`, редко используя `PUT` и `DELETE` в строгом RESTful-смысле. Именно поэтому `QUERY` для них выглядит как еще одна сущность, которой будет пользоваться ограниченный круг разработчиков, а большинство API продолжит жить на привычных `GET` и `POST`.
Некоторые считают метод избыточным, полагая, что для простого CRUD-приложения он не нужен.
Кроме того, встречается скепсис о том, что кеширование по телу запроса сложное и потенциально опасное: для `GET` кешу достаточно URI и набора заголовков, а для `QUERY` нужно учитывать тело. Если тело большое, произвольное или допускает разные эквивалентные формы записи, реализация кеша усложняется. Некоторые участники обсуждений считают, что такие кеши либо будут малоэффективными, либо создадут новые классы ошибок.
Еще один контраргумент: поддержка появится не сразу и не везде. Сам по себе HTTP-метод технически выглядит как строка, и в некоторых клиентах `QUERY` уже можно отправить как пользовательский метод. Но этого мало: его должны корректно понимать библиотеки, прокси, CDN, API-gateway, веб-серверы, инструменты тестирования и средства мониторинга. Пока вся эта цепочка не привыкнет к новому методу, в продакшене придется внимательно проверять совместимость. Отдельный нюанс — CORS: для `QUERY` браузер будет делать preflight-запрос, потому что этот метод не входит в список простых CORS-методов.
Где QUERY может быть уместен
Можно попробовать рассматривать `QUERY` не как обязательную замену, а как новый инструмент для конкретных случаев.
Он уместен, если:
- фильтры становятся слишком длинными или вложенными для URL;
- запрос должен оставаться операцией чтения;
- важно явно показать идемпотентность;
- есть смысл в повторном выполнении запроса;
- API работает со сложными языками выборки: JSONPath, SQL-подобными выражениями, аналитическими фильтрами;
- параметры нежелательно светить в URL.
Он не нужен, если:
- обычного `GET` с query parameters достаточно;
- пользователям важно копировать URL из адресной строки;
- инфраструктура пока не пропускает новый метод;
- операция реально меняет состояние;
- команда не готова поддерживать сценарий, нестандартный для многих клиентов.
Вместо заключения
Надо понимать, что «признание» `QUERY` — не революция и не замена всему старому HTTP. Скорее это аккуратная попытка назвать HTTP-глагол более точно и семантически правильно: сложный запрос на чтение с телом запроса.
Возможно, метод позволит больше не маскировать чтение под `POST` только потому, что `GET` неудобен для сложного тела. Но внедрять `QUERY` прямо сейчас во все продакшен-API, наверное, будет преждевременно: поддержка еще будет догонять стандарт, а кеширование и совместимость потребуют внимательного проектирования.