HTTP QUERY: el método que faltaba entre GET y POST
HTTP QUERY: el método que faltaba entre GET y POST
Durante casi treinta años, la Web ha convivido con una paradoja semántica que cualquiera que desarrolle para el backend conoce bien: ¿cómo se ejecuta una consulta compleja, voluminosa o que contiene datos sensibles, si el único método "seguro" disponible no permite un cuerpo de solicitud?
Pensemos en cuando necesitamos enviar una consulta de búsqueda estructurada con filtros anidados, ordenaciones y paginaciones multidimensionales. O quizás una consulta SQL, una expresión JSONPath o un documento GraphQL.
El primer pensamiento de quien empieza a programar es: añado un parámetro "q" a la URI y lo envío con GET, total, qué puede pasar.
Luego, con el tiempo, los parámetros pasan a ser 10, 100 o 1000, y la aplicación explota. Esto sucede porque el proxy que cruza el firewall, almacena los datos en Redis y realiza el balanceo de carga tiene un límite de 2048 caracteres para la URI. O bien ocurre porque el firewall registra la URI en texto plano y la envía a un SIEM, exponiendo datos sensibles como filtros de búsqueda o credenciales.
Meter todo esto en una URI con el método clásico GET nos expone a límites físicos de transporte y a graves riesgos de seguridad.
¿Qué hace en su lugar quien busca un mínimo de seguridad? Usa solo solicitudes POST: todo queda oculto en el cuerpo de la petición y no importa si destruimos la semántica, impedimos el almacenamiento en caché o desesperamos a nuestro profesor de sistemas, lo importante es mantener los datos a salvo.
He trabajado en un proyecto cuya base seguía esta misma lógica: rudimentaria, pero eficaz.
La RFC 10008, publicada por la IETF en junio de 2026 y clasificada como Proposed Standard, resuelve finalmente este dilema introduciendo el método QUERY. Intentemos comprender si vale la pena o si ha llegado demasiado tarde a nuestras mesas de trabajo. Una cosa es el estándar y otra muy distinta su adopción, para la cual harán falta años. Se trata de actualizar lenguajes, frameworks, librerías, proxies, balanceadores, CDNs, WAFs y navegadores.
Este nuevo verbo combina la garantía semántica de seguridad e idempotencia (típicas de GET) con la capacidad estructural de transportar un payload extendido en el cuerpo de la solicitud (típica de POST).
En este artículo exploraremos la génesis del método QUERY, cómo funciona bajo el capó, sus implicaciones en el almacenamiento en caché y la seguridad, y cómo podemos implementarlo hoy en nuestros backends (o al menos en el futuro, cuando el ecosistema deje de rechazar la petición con un desolador HTTP/1.0 405 Method Not Allowed).
QUERY transporta la consulta en el cuerpo de la solicitud, superando los límites de GET, pero con una semántica de seguridad que POST no puede garantizar.
La génesis de un estándar: AUTH48 y el papel del IETF
El método QUERY no nació de la noche a la mañana. Es el fruto del trabajo del grupo HTTPbis de la IETF, redactado por nombres reconocidos de la ingeniería de protocolos como Julian Reschke (greenbytes), James M. Snell (Cloudflare) y Mike Bishop (Akamai).
Un detalle interesante tiene que ver con la fase final de revisión del estándar, conocida como "AUTH48". Por primera vez, el RFC Production Center (RPC) utilizó un proyecto piloto basado en repositorios de GitHub en lugar de los tradicionales y lentos flujos de correo electrónico (https://github.com/rfc-editor-drafts/FinalReview-rfc10008).
Esto permitió una trazabilidad granular de los cambios de redacción, acelerando el lanzamiento de un estándar que promete cambiar las reglas del juego.
El problema: los límites físicos y semánticos de GET y POST
Para valorar la importancia de QUERY, debemos examinar de cerca las limitaciones que nos han acompañado durante décadas.
Los límites de GET: entre URIs demasiado largos y registros en texto plano
Con GET, la consulta vive por completo en la URI. Aunque la RFC 9110 recomienda que la infraestructura soporte URIs de al menos 8000 octetos, en la práctica nos topamos con firewalls, balanceadores de carga y CDNs no coordinados que aplican límites inferiores y variables. Quien trabaje en el ámbito enterprise sabe bien que 2048 caracteres es un límite habitual, mientras que algunos proxies más antiguos se quedan en 1024.
Superar estos límites genera errores como "414 URI Too Long", siempre difíciles de diagnosticar, especialmente cuando el problema solo se manifiesta en entornos de producción con tráfico real.
Además, hay un problema crítico de privacidad: las URIs se registran en texto plano en los logs de los servidores. Una simple búsqueda mediante grep puede extraer una enorme cantidad de datos sensibles con el mínimo esfuerzo.
Aún recuerdo cuando, durante una de mis primeras auditorías de seguridad de logs para un gran operador nacional de telecomunicaciones, salté de la silla: al analizarlos, me di cuenta de que todos los accesos de una determinada aplicación se realizaban en GET en lugar de POST. Las contraseñas de miles de usuarios estaban allí, escritas en texto plano en los archivos de texto del servidor web.
Aparte de esto, aunque el fenómeno está mucho más mitigado en comparación con el pasado, las cabeceras "Referer" hacia sitios de terceros pueden arrastrar datos que debían permanecer segregados en el servidor de origen.
Por estos motivos, GET carga con un pecado original: no es adecuado para transportar datos sensibles, a pesar de ser el único método seguro e idempotente disponible.
Por último, almacenar en caché estas peticiones es ineficiente. Cualquier variación sintáctica en el orden de los parámetros o en la codificación de caracteres invalida la caché, transformando consultas semánticamente idénticas en recursos distintos.
El recurso a POST: la pérdida del contrato semántico
Para evitar los límites de GET, la práctica común ha sido abusar de POST, desplazando la consulta al body: un enfoque sencillo, pero con consecuencias semánticas importantes.
De este modo ya no tenemos problemas de espacio en la URI y los logs permanecen limpios (es cierto que se puede habilitar el registro del body, pero no suele estar activo por defecto), pero este mecanismo rompe las garantías del protocolo. El método POST no es seguro ni idempotente por definición.
Como consecuencia, los intermediarios implicados en el transporte de una solicitud POST, al no saber si está leyendo datos o modificando una base de datos, evitan reenviar la información (como hacen los navegadores) o realizar el almacenamiento en caché (como hacen las CDNs).
Los precursores WebDAV y por qué evitar SEARCH o REPORT
Quienes tengan más experiencia o peinen alguna cana recordarán sin duda los intentos realizados con WebDAV y métodos como "SEARCH" o "PROPFIND". Todavía hay quien piensa que WebDAV es una solución perfecta, pero quizás debería cambiar de opinión tras conocer esta especificación y ver cómo ha omitido deliberadamente los métodos de WebDAV para crear uno completamente nuevo.
Se decidió, sin embargo, no reciclar esos métodos, que nacían estrechamente vinculados al Content-Type "application/xml", y empezar desde cero.
QUERY nace así agnóstico, no asociado a un formato concreto, de manera que cada desarrollador pueda gestionar su propio "Content-Type" con total autonomía.
Por lo tanto, estamos adoptando los aspectos positivos de GET: seguridad, idempotencia y almacenamiento en caché, sumándole el cuerpo de solicitud de POST.
Negociación de contenido y códigos de error
Al razonar con el "Content-Type" comienzan a aparecer las primeras limitaciones: si un cliente solicita un formato "application/xml" y el servidor ofrece un contenido distinto, la petición es rechazada. Lo mismo ocurre si el cliente no indica explícitamente este parámetro.
El estándar define de manera rigurosa este paso en la sección "2.1 Media Types and Content Negotiation":
- 400 Bad Request: si el tipo de medio está ausente o mal formado.
- 415 Unsupported Media Type: si el formato de la consulta no es compatible con el endpoint.
- 406 Not Acceptable: si el formato solicitado en "Accept" no está disponible.
- 422 Unprocessable Content: si la consulta es sintácticamente válida pero lógicamente no procesable (por ejemplo, hace referencia a una tabla o campo inexistente).
Discovery y la cabecera Accept-Query
¿Cómo sabe un cliente qué formatos de consulta acepta un endpoint? La RFC 10008 introduce la cabecera de respuesta "Accept-Query".
Esta cabecera adopta las especificaciones de los "Structured Fields" (RFC 9651). Al ser una lista formateada de tokens, permite a los clientes realizar el parseo de manera estándar, sin necesidad de expresiones regulares complejas:
Accept-Query: "application/jsonpath", "application/sql"
Un cliente puede descubrir las capacidades del servidor de dos maneras:
- Preventiva: envía una petición "OPTIONS" o "HEAD" y lee las cabeceras "Allow" (que incluye QUERY) y "Accept-Query".
- Agresiva: envía directamente una petición "QUERY". Si el servidor no soporta ese formato, responde con "415 Unsupported Media Type" e incluye la cabecera "Accept-Query" para indicar los formatos permitidos.
Obviamente, si existe la posibilidad de acordar el formato, este enfoque es preferible porque evita viajes de ida y vuelta (round-trips) adicionales, aunque no siempre es viable.
El concepto de "recurso equivalente"
Para respetar el principio de la Web según el cual cada recurso debe poseer una URI, la RFC introduce el "recurso equivalente": un recurso conceptual (accesible mediante GET) que representa el resultado de una petición QUERY específica junto con sus metadatos.
El servidor puede materializar este recurso ofreciendo dos cabeceras distintas en la respuesta:
- Location: apunta a la consulta activa y repetible. El cliente puede realizar un "GET" a esta URI para repetir la consulta y obtener datos actualizados. Esta URI puede ser temporal: si expira, el cliente simplemente vuelve a enviar la solicitud "QUERY" completa.
- Content-Location: identifica una captura estática e histórica de los resultados en ese momento preciso.
Gestión de redirecciones y peticiones condicionales
A diferencia de POST, donde las redirecciones "301" o "302" a menudo hacían que los navegadores transformaran la petición en un "GET" (perdiendo el body), con QUERY el cliente debe mantener el mismo método y cuerpo hacia la nueva URI para los códigos "301", "302", "307" y "308".
Solo el código "303 See Other" impone el cambio a un GET. Esto resulta de utilidad para consultas asíncronas muy pesadas: el servidor responde inmediatamente con "303" y una cabecera "Location", indicándole al cliente que recupere el resultado ya procesado mediante un "GET" posterior.
Para las peticiones condicionales, el cliente puede enviar cabeceras como "If-Modified-Since" basándose en las marcas de tiempo recibidas previamente. Si los datos subyacentes no han cambiado, el servidor responde con un ligero "304 Not Modified", ahorrando un valioso ancho de banda.
Ingeniería de la edge network: el almacenamiento en caché basado en el cuerpo
La parte más compleja de QUERY es, sin duda, la gestión de la caché. Para los proxies inversos o CDNs actuales, acostumbrados a almacenar en caché fijándose solo en la cabecera, implica introducir mecanismos capaces de tener en cuenta también el cuerpo de la solicitud.
Esto significa que el sistema de caché debe calcular un hash del cuerpo de la petición para identificar las llamadas repetidas. Dicha operación, según el tamaño del payload, puede introducir una velocidad proporcional a los datos enviados.
La normalización
Dados los problemas relativos al orden de los parámetros y a la ineficiencia de la caché, la RFC especifica:
Para mejorar la eficiencia de la caché, las cachés PUEDEN eliminar primero las diferencias semánticamente insignificantes del contenido de la solicitud y de los metadatos correlacionados
Este es un aspecto importante para mejorar la eficiencia de la caché, pero introduce un riesgo: si el parser del proxy y el del servidor de origen normalizan los datos de manera distinta, se corre el riesgo de sufrir "Parser Differentials"—la caché podría devolver un falso positivo (datos de una consulta para otra distinta).
En caso de que exista una cadena de procesos intermediarios que pueda dar lugar a este desajuste, la cabecera prevista es:
Cache-Control: no-transform
Para ser honestos, la cabecera no-transform representa el miedo visceral de quien diseñó esta especificación. Probablemente pasó noches en vela depurando cachés fallidas de las formas más sutiles posibles y terminó rindiéndose, introduciendo un parámetro de no alterabilidad justo dentro de una especificación que en teoría declaraba su viabilidad.
Esta cabecera acabará adoptándose en muchos escenarios reales precisamente para evitar cualquier desajuste impredecible entre parsers diferentes.
Seguridad, privacidad y preflight CORS
Hasta aquí todo parece excelente, pero la introducción de un nuevo método HTTP tiene repercusiones importantes en la seguridad y la privacidad.
- WAF Blind Spots: muchos Web Application Firewalls tradicionales analizan el body de las peticiones solo para métodos no seguros (POST, PUT, PATCH). Al ver un método "safe" como QUERY, podrían pasar por alto la inspección profunda, permitiendo que payloads dañinos (SQL Injection, XSS) alcancen el backend. Los equipos de seguridad deben actualizar los WAFs y configurar reglas para monitorizar de cerca el tráfico de QUERY.
- Controles anti-CSRF: si los endpoints de QUERY se implementan de forma deficiente y producen efectos colaterales en la base de datos (por ejemplo, utilizándolos como si fuesen un POST), los middlewares anti-CSRF podrían ignorarlos al considerarlos seguros, exponiendo la aplicación a vulnerabilidades.
- CORS Preflight: QUERY no es un método "safelisted" en las especificaciones Fetch de WHATWG. Las peticiones cross-origin requerirán siempre una solicitud de preflight "OPTIONS", duplicando el viaje de ida y vuelta de la red. Es fundamental configurar adecuadamente la cabecera "Access-Control-Max-Age" para almacenar en caché el preflight y evitar cuellos de botella en el rendimiento.
- Ventajas para la privacidad: al desplazar los parámetros sensibles de la URI al cuerpo opaco de la solicitud, QUERY previene fugas de datos en los logs de los servidores y en los historiales de los navegadores, alineándose perfectamente con el principio de minimización de datos del GDPR.
El punto de inflexión para GraphQL y headless commerce
GraphQL ha vivido durante años un dilema con el almacenamiento en caché. Al ser sus consultas demasiado largas para GET, históricamente se han enviado a través de POST, desactivando las cachés edge. Quienes se dedican a la ingeniería de software han tenido que idear soluciones complejas como las consultas persistentes (persisted queries) para restaurar el almacenamiento en caché con GET.
Con QUERY, los clientes de GraphQL pueden enviar consultas completas especificando "application/graphql", incrementando el rendimiento y reduciendo la complejidad de la aplicación.
Soporte en los backends
- Node.js: el parser C++ integrado en las versiones recientes de Node (21.x y 22+) soporta QUERY de forma nativa. Frameworks como Fastify permiten el enrutamiento explícito con el flag
{ hasBody: true }. - Go: el paquete "net/http" gestiona los métodos como cadenas de texto, y el enrutador introducido en Go 1.22 ya soporta el enrutamiento de QUERY por defecto.
- OpenAPI 3.2+: ofrece soporte oficial para la definición y documentación del método QUERY.
¿Y los navegadores?
Los debates en el WHATWG (issue #12594) están definiendo el soporte para <form method="query">. Cuando esto se implemente, podremos actualizar por fin los formularios de búsqueda web y decir adiós al molesto aviso de "confirmar reenvío de formulario" al actualizar la página.
¿Un protocolo finalmente maduro?
El método QUERY no es una simple adición sintáctica. Resolvió una incoherencia estructural que arrastramos desde los inicios de la Web.
Al devolver la dignidad semántica a las lecturas complejas, nos permite construir APIs más limpias, seguras y eficientes, liberándonos de la obligación de sacrificar la escalabilidad de la caché en favor de la privacidad y el espacio de transporte.
La transición requerirá tiempo, sobre todo para la adaptación de los clientes web y de los sistemas de seguridad de red. Pero el camino está trazado: consultar no es modificar, y a partir de hoy el protocolo HTTP lo sabe.