Pular para o conteúdo

O HTTP te força a mentir

Você precisa buscar dados. A query tem filtros, ranges de data, campos aninhados. É uma leitura — não muda nada no servidor.

Query string transbordando do browser — 414 URI Too Long

Qual método HTTP você usa?

GET é o método correto semanticamente. É safe. É idempotent. Proxies podem cachear. Browsers podem prefetch.

Mas GET não aceita body.

Então você enfia tudo na query string:

GET /api/orders?status=pending&created_after=2026-01-01&created_before=2026-06-30&customer_id=8841&include=items,shipping,payment&sort=-created_at&fields=id,status,total,customer.name,items.sku,items.qty&page=2&per_page=50 HTTP/1.1

Funciona. Até não funcionar.

Agora imagina um filtro de dashboard real — com 15 facetas, 3 levels de nesting, e operadores lógicos:

GET /api/analytics/events?filter[0][field]=event_name&filter[0][op]=in&filter[0][value]=page_view,click,scroll&filter[1][field]=timestamp&filter[1][op]=gte&filter[1][value]=2026-01-01T00:00:00Z&filter[2][field]=timestamp&filter[2][op]=lte&filter[2][value]=2026-06-30T23:59:59Z&filter[3][field]=properties.country&filter[3][op]=in&filter[3][value]=BR,US,DE,JP,FR,GB,CA,AU,IN,MX&filter[4][field]=properties.device&filter[4][op]=neq&filter[4][value]=bot&group_by=event_name,properties.country&metrics=count,unique_users,avg_duration&having[0][metric]=count&having[0][op]=gte&having[0][value]=100&sort=-count&limit=500&offset=0&timezone=America/Sao_Paulo HTTP/1.1

São 742 caracteres. Num caso real, com UUIDs e nomes de campo de verdade, passa de 2000 fácil.

Problema 1: URLs têm limite — e ninguém concorda no tamanho

Seção intitulada “Problema 1: URLs têm limite — e ninguém concorda no tamanho”

A spec HTTP (RFC 9110) recomenda que servidores aceitem pelo menos 8.000 octets na request-line.

Na prática:

Componente Limite real
Chrome ~2 MB (mas quebra em outros lugares antes)
Apache (padrão) 8.190 bytes
Nginx (padrão) 4.096–8.192 bytes
IIS 16.384 bytes
CDNs (Cloudflare, AWS ALB) 8.192–16.384 bytes
Proxies corporativos Imprevisível — frequentemente 2.048

O problema não é o browser. É o caminho entre o browser e o seu servidor.

“In the wild, your link has to survive a gauntlet of browsers, proxies, CDNs, and ancient server configurations. If any one of those links in the chain decides your URL is too long, the whole thing snaps.”

Um proxy corporativo decide que sua URL é longa demais? 414 Request-URI Too Long. Sem retry. Sem fallback. Tela branca pro usuário.

Problema 2: POST mente sobre o que está acontecendo

Seção intitulada “Problema 2: POST mente sobre o que está acontecendo”

Quando a URL fica grande demais, todo mundo faz o mesmo:

POST /api/orders/search HTTP/1.1
Content-Type: application/json
{
"status": "pending",
"created_after": "2026-01-01",
"created_before": "2026-06-30",
"customer_id": 8841,
"include": ["items", "shipping", "payment"],
"sort": "-created_at",
"page": 2
}

Resolve o limite de URL. Mas agora você está mentindo.

POST significa “este request pode causar efeitos colaterais.” Intermediários, CDNs, e browsers não podem cachear. Se a conexão cair no meio, não podem reenviar automaticamente. Service meshes vão tratar como escrita. Ferramentas de observabilidade vão classificar como mutação.

Toda a infraestrutura HTTP — construída ao longo de 30 anos — interpreta POST como “cuidado, isso muda estado.”

Você não está mudando estado. Está lendo. Mas o protocolo inteiro acha que você está escrevendo.

Problema 3: Intermediários são hostis a GET com body

Seção intitulada “Problema 3: Intermediários são hostis a GET com body”

“Tá, mas e se eu mandar GET com body?”

Tecnicamente, HTTP/1.1 não proíbe. Mas:

  • RFC 9110 (§9.3.1): “content within a GET request has no defined semantics”
  • Proxies descartam o body silenciosamente — seu filtro complexo simplesmente some
  • WAFs bloqueiam — “GET request with body? Likely malicious”
  • Client libraries ignoram — muitas descartam o body de um GET sem aviso
  • Caches não consideram o body — requests diferentes retornam respostas iguais do cache

“There is software that expects GET to not have a body, so if it encounters such an aberration, it may decide it is a bad request.” — u/azhder, r/programming (112 upvotes)

O resultado? Bug silencioso. O request chega sem body, retorna dados errados, e você leva horas debugando por que o filtro “não funciona em produção mas funciona local.”

O caso mais visível. Uma query GraphQL é uma string potencialmente enorme:

query GetDashboardData($orgId: ID!, $dateRange: DateRangeInput!, $filters: [FilterInput!]!) {
organization(id: $orgId) {
analytics(dateRange: $dateRange, filters: $filters) {
events { name count uniqueUsers avgDuration }
topPages(limit: 20) { path views bounceRate }
funnels { name steps { label conversionRate dropoff } }
}
users(filter: { active: true, role: ADMIN }) {
edges { node { id email lastSeen permissions } }
}
}
}

Com variáveis, serializada e URL-encoded num GET, pode passar de 4KB facilmente. Por isso o GraphQL over HTTP spec recomenda POST para queries. Ou seja — o padrão da indústria é aceitar que o HTTP está quebrado e usar o método errado.

POST /products/_search HTTP/1.1
Content-Type: application/json
{
"query": {
"bool": {
"must": [
{ "match": { "title": "wireless headphones" } },
{ "range": { "price": { "gte": 50, "lte": 200 } } },
{ "terms": { "brand": ["Sony", "Bose", "Apple", "JBL"] } }
],
"filter": [
{ "term": { "in_stock": true } },
{ "geo_distance": { "distance": "50km", "location": "-23.55,-46.63" } }
]
}
},
"aggs": {
"price_ranges": { "range": { "field": "price", "ranges": [...] } },
"brands": { "terms": { "field": "brand", "size": 20 } }
},
"sort": [{ "_score": "desc" }, { "created_at": "desc" }],
"size": 20,
"from": 40
}

Elasticsearch usava GET com body por anos. Até que não deu mais — proxies e API gateways quebravam. Agora aceitam tanto GET quanto POST. A documentação oficial diz: “Both HTTP GET and HTTP POST can be used to execute search with body.”

Isso é a definição de gambiarra institucionalizada.

Todo SaaS com um painel de analytics, toda plataforma com busca avançada, todo e-commerce com filtro de produtos — todos enfrentam a mesma escolha:

  1. URL gigante → quebra em proxies e CDNs
  2. POST /search → perde caching, retry, e semântica correta
  3. GET com body → bug silencioso em produção

Não existe opção boa. Até agora.

“Every time you filter a database or build a dashboard, you are committing an HTTP crime.” — Byte Pint, YouTube

Não é drama. É a realidade arquitetural. Estamos usando um protocolo de 1996 que nunca previu queries estruturadas no corpo de uma requisição safe.

A comunidade sabia disso. A IETF sabia disso. O primeiro draft de um método para resolver esse problema — na época chamado SEARCH — é de 2015. Foram mais de 10 anos de discussão, revisões, e debates sobre semântica.

“The whole process (from its inception as SEARCH) took more than 10 years.” — u/Nimelrian, r/programming (337 upvotes)

Em junho de 2026, saiu o RFC 10008. O método se chama QUERY.

Safe. Idempotent. Cacheable. Com body.


Próximo: Como funciona →

O QUERY resolve cada um desses problemas. Veja a semântica, o caching, e o que muda na prática.