Límites de tamaño de solicitudes: endurecer el parseo del cuerpo para prevenir DoS
Establece límites de tamaño de petición y reglas de parseo más seguras para evitar picos de memoria, ralentizaciones y denegación de servicio por peticiones sobredimensionadas o malformadas.

Por qué las solicitudes sobredimensionadas son un problema real
Las solicitudes demasiado grandes suelen parecer inofensivas hasta que tumban una app. Una sola subida enorme, un blob JSON gigantesco o una petición atascada en un bucle de reintentos puede devorar memoria y CPU, ralentizar a todos y, a veces, provocar el crash del servidor.
Algunos términos claros ayudan:
- Payload: los datos que envía un cliente.
- Body: donde suelen vivir esos datos dentro de una petición HTTP (por ejemplo, JSON o un archivo).
- Parser: el código (o librería) que lee el body y lo convierte en algo que tu app puede usar, como un objeto, una cadena o un fichero guardado.
El riesgo no viene solo de “hackers”. Muchos incidentes son accidentales: un bug en móvil que envía 50 MB, un frontend que codifica una imagen en base64 dentro de JSON, o una integración que sigue concatenando campos hasta que el body es masivo. El resultado puede parecer un denial-of-service aunque nadie quisiera causar daño.
Por supuesto, la misma debilidad es fácil de abusar a propósito. Si tu servidor acepta bodies ilimitados, un atacante puede enviar peticiones muy grandes (o muchas medianas) y forzar un parseo costoso. Eso puede dejar sin memoria a tu app, llenar discos y bloquear tráfico legítimo.
Los límites de tamaño importan, pero no son “poner y olvidar”. Límites demasiado bajos rompen usuarios reales (especialmente con uploads). Límites demasiado altos aún permiten picos de memoria. La meta es un valor seguro por defecto, con excepciones claras para los pocos endpoints que realmente necesitan cuerpos mayores.
Qué puede salir mal cuando aceptas payloads grandes
Aceptar “lo que el cliente mande” es una forma rápida de convertir un endpoint en una causa de outage. Sin límites de tamaño, un solo POST sobredimensionado puede empujar tu app a alto uso de memoria, tiempos de respuesta largos y fallos en cascada.
El primer golpe suele ser la memoria. Muchos frameworks bufferizan todo el body antes de que tu handler se ejecute. Un payload JSON grande o una subida multipart puede copiarse varias veces durante el buffering, el decode y la validación. Eso multiplica el coste de memoria, y no hacen falta muchas peticiones concurrentes para agotar un contenedor o VM.
El CPU es el siguiente problema. Parsear JSON enorme es caro, y objetos muy anidados lo empeoran. Incluso cuando el payload es “válido”, el servidor puede pasar segundos solo convirtiendo bytes en objetos, dejando menos CPU para trabajo real. Algunos parsers hacen trabajo extra en entradas grandes (coerción, validación), lo que aumenta el coste por petición.
Los bodies sobredimensionados también ocupan workers. Una subida lenta mantiene una conexión abierta, reteniendo un hilo worker o la atención del event loop y empujando a otros usuarios a timeouts. Bajo carga, los reintentos se acumulan y amplifican el daño.
Las subidas pueden dañar silenciosamente tu disco también. Si los ficheros temporales acaban en el sitio incorrecto (o nunca se limpian), un pico de peticiones grandes puede llenar el disco y romper partes no relacionadas de la app.
Los equipos suelen subestimar los costes “secundarios”: picos de ancho de banda y facturas cloud (especialmente con reintentos), colas de background más lentas por presión de CPU y memoria, logs ruidosos y puntos ciegos porque algunos errores ocurren antes de que el código de la app se ejecute. Otro problema común es hacer checks de autenticación después de parsear el body, lo que abarata el abuso.
Un escenario realista: un endpoint de signup acepta JSON con un campo “profile”. Un cliente buggy envía un blob de 50 MB. El servidor lo bufferiza, lo parsea y se queda bloqueado. Añade unas pocas peticiones más en paralelo y el servicio deja de responder.
Dónde aplicar límites para que funcionen de verdad
Los límites más fiables se aplican en más de un sitio. Si solo pones un límite dentro de la app, el servidor aún puede pasar tiempo y memoria leyendo un body gigante antes de que tu código lo rechace. Si solo lo pones en el edge, igual quieres controles más estrictos para endpoints específicos.
1) Capa edge: para las solicitudes enormes antes de que lleguen a la app
Tu primera línea de defensa debe ser la capa que recibe el tráfico primero, como un CDN, load balancer, reverse proxy o API gateway. Ahí puedes rechazar bodies sobredimensionados temprano, devolver un HTTP 413 y evitar ocupar workers de la app. También ayuda con subidas lentas y grandes que buscan mantener conexiones abiertas.
Mantén el límite en el edge estricto para el tráfico general. Si necesitas uploads mayores, trátalos por una ruta o servicio separado en lugar de subir el tope para todo.
2) Capa de la app: añade límites por endpoint y defaults más seguros
Dentro de la app, aplica límites otra vez para que cada endpoint tenga el techo adecuado. Un endpoint de login debe aceptar un JSON pequeño. Un endpoint de foto de perfil puede aceptar más.
Un patrón práctico es un tope global pequeño para la mayoría de rutas API, con caps por ruta para excepciones. Los endpoints públicos deben ser más estrictos que los autenticados. Rechaza temprano cuando sea posible (comprobando Content-Length cuando esté presente) y usa timeouts para la lectura del body, no solo para el procesamiento.
Las subidas merecen tratamiento especial. Trata los uploads como un flujo diferente a las APIs JSON normales, porque las subidas pueden disparar picos de memoria si un parser bufferiza todo. Prefiere manejo en streaming o chunked, escribe en disco u object storage y valida tipo y tamaño de fichero antes de trabajo costoso.
Cómo elegir límites seguros sin romper a los usuarios
Empieza listando cada endpoint que acepta un body. Los límites son seguros solo cuando coinciden con el uso real. La forma más rápida de lograrlo es inventariar lo que realmente aceptas, no lo que asumes que aceptas.
Agrupa endpoints por lo que deberían recibir. Las APIs JSON suelen necesitar los topes más bajos. Los form posts a menudo están en medio. Los uploads necesitan límites mayores. Los webhooks pueden sorprender con ráfagas ocasionales grandes.
Un punto de partida práctico es empezar con defaults estrictos y subir límites solo cuando haya una razón clara. Por ejemplo:
- Endpoints JSON: 16 KB a 256 KB
- Form posts (sin archivos): 64 KB a 512 KB
- Uploads de archivos: 5 MB a 25 MB (solo en rutas específicas)
- Webhooks: 256 KB a 2 MB (según docs y logs del proveedor)
Esos números no son universales, pero el patrón importa: la mayoría de rutas debe ser pequeña y solo unas pocas deben permitir tamaños grandes.
Cuando elijas un tope, recuerda que limitas más que “datos de usuario”. Headers, cookies y límites multipart añaden overhead. Base64 también hincha el contenido en torno a un tercio. Una imagen de 2 MB puesta dentro de JSON puede llegar cerca de 2.7 MB antes de que la app empiece a procesarla.
Planifica excepciones de forma explícita. Si un endpoint necesita verdaderamente 25 MB, dáselo solo a ese endpoint y mantén el default bajo, en lugar de dejar toda la app “ilimitada”. Anota quién usa la excepción, qué envían y cuál es un límite razonable.
Un olor común es un único endpoint genérico (como “/api/save”) que acepta cualquier cosa. Dividirlo en un pequeño endpoint JSON y una ruta de upload separada suele evitar picos de memoria sin romper usuarios normales.
Reglas de parseo del body que reducen picos de memoria y CPU
La forma más rápida de provocar picos es dejar que la app adivine qué contiene un body y luego parsearlo automáticamente. Endurecer el parseo significa ser estricto: acepta solo lo que esperas y parsea solo cuando realmente lo necesitas.
Empieza con una allowlist de Content-Types por endpoint. Si un endpoint espera JSON, acepta solo application/json (y las variantes exactas que soportes). Si falta Content-Type o es desconocido en un endpoint que necesita body, recházalo temprano. Esto evita parseos accidentales de textos enormes, codificaciones extrañas o inputs que hagan trabajar más al parser.
Pon límites al parseo JSON
Si tu framework lo soporta, limita la complejidad JSON, no solo el tamaño. Una petición pequeña aún puede ser costosa si está muy anidada o tiene miles de keys.
Buenos defaults a considerar:
- Profundidad máxima de JSON (ejemplo: 20 a 50 niveles)
- Conteo máximo de campos (ejemplo: 1.000 a 10.000 keys)
- Longitud máxima de string por campo (ejemplo: 10 KB a 100 KB)
- Manejo UTF-8 estricto (rechazar secuencias inválidas)
- Fallar rápido ante keys duplicadas (si está soportado)
A continuación, desactiva el parseo automático en rutas que no lo necesitan. Muchas apps parsean JSON globalmente para cada petición, incluso checks de salud, endpoints de verificación de webhook y rutas GET simples. Eso es trabajo desperdiciado y un objetivo fácil.
Para uploads, prefiere streaming (procesar por chunks) en lugar de leer el fichero entero en memoria antes de escribirlo a disco u object storage. Combina streaming con límites de tamaño para que una sola petición no pueda llenar la RAM.
Ejemplo: un endpoint de signup espera un JSON pequeño. Si el servidor acepta cualquier Content-Type y parsea automáticamente, un atacante puede enviar un payload de varios megabytes con una estructura compleja que deje pegado el CPU. Reglas estrictas de Content-Type más caps de profundidad y campos convierten eso en un rechazo rápido en lugar de un outage.
Paso a paso: añadir límites de petición y endurecer el parseo
La mayoría de outages por solicitudes sobredimensionadas golpean endpoints fáciles de alcanzar: formularios públicos, APIs no autenticadas y webhooks. Empieza listando cada ruta que acepta body y marca cuáles son públicas, llamadas por terceros y cuáles aceptan archivos.
Checklist práctico
Comienza en el edge (load balancer, reverse proxy, CDN o API gateway). Los controles en el edge son tu primera línea porque bloquean la petición antes de que tu app gaste memoria en parsearla.
- Identifica endpoints de alto riesgo (públicos, no autenticados, webhooks, uploads).
- Establece caps en el edge: tamaño máximo de body, tamaño máximo de headers y un read timeout corto.
- Añade excepciones por path solo cuando las puedas justificar (por ejemplo, una ruta de upload).
- Asegúrate de que el edge devuelve HTTP 413 para bodies que excedan el límite.
- Confirma que el límite se aplica realmente (algunas stacks bufferizan antes de rechazar).
Luego pónganlo seguro dentro de la app. Los límites en la app te protegen cuando el tráfico salta el edge (llamadas internas, mala configuración) y permiten reglas más específicas que un tope global.
- Aplica límites de tamaño por ruta y por content type (JSON vs multipart upload).
- Configura defaults del parser: tamaño máximo JSON, profundidad máxima y parseo estricto (rechazar JSON inválido; rechazar keys duplicadas si se soporta).
- Evita parsear en memoria cuando puedas: haz streaming de uploads y valida tipo y tamaño temprano.
- Prueba peticiones normales y deliberadamente sobredimensionadas, incluyendo bodies comprimidos si los aceptas.
- Observa logs de 413s y read timeouts durante una semana y ajusta solo para necesidades comprobadas.
Un caso real: un endpoint de webhook acepta JSON sin tope. Un payload sobredimensionado puede disparar memoria y reiniciar el servicio. Un pequeño límite JSON por ruta en el webhook (mientras se permiten límites mayores solo en rutas autenticadas de upload) evita el pico sin romper el tráfico normal.
Manejo de fallos seguro y útil para el usuario
Una vez que aplicas límites, la siguiente pregunta es qué ve el cliente cuando sobrepasa el tope. Un buen manejo de fallos detiene el ataque (o el accidente) temprano, pero aún indica al usuario cómo arreglar la petición.
Usa códigos de estado que coincidan con el problema. Un payload demasiado grande debe devolver 413 Payload Too Large. Un body que no aceptas (por ejemplo, XML en un endpoint solo JSON) debe devolver 415 Unsupported Media Type. Si la petición está malformada, 400 Bad Request suele ser suficiente. Que el error sea claro ayuda a evitar reintentos ciegos.
Mantén los mensajes de error cortos y prácticos. Indica qué cambiar: “File too large. Max 10 MB.” o “Only application/json is supported.” No repitas bodies, headers ni inputs del usuario en la respuesta para reducir el riesgo de filtrar secretos.
Para logging, quieres suficiente contexto para depurar sin almacenar el payload rechazado. Un buen equilibrio es registrar el endpoint y método, código de estado (413, 415), el Content-Length observado (si se proporcionó) y el límite configurado, Content-Type, además de un request ID y user/account ID (si se conocen).
Decide dónde fallar rápido por endpoint. Para endpoints públicos de upload y rutas de autenticación, fallar en el edge ahorra que la app haga cualquier trabajo. Para endpoints cuyos límites dependen de reglas por ruta, la app puede decidir, pero aún debe rechazar antes de parsear el body completo.
Errores comunes que conducen a outages o DoS fáciles
La mayoría de outages por solicitudes sobredimensionadas no ocurren por “falta total de límites”. Pasan cuando los límites existen pero son desiguales, se esquivan o se aplican demasiado tarde para proteger la memoria.
Un patrón común es poner un único tope global y pensar que eso basta. Luego, una ruta de upload, un webhook o una excepción del reverse proxy permite cuerpos mucho mayores. Los atacantes no necesitan golpear tu API principal: solo un endpoint débil.
Errores que aparecen repetidamente:
- Limitar bodies JSON pero olvidar uploads y webhooks (o ponerlos en una categoría “ilimitada”).
- Subir límites “solo por hoy” para desbloquear a un cliente y no volver a bajarlos.
- Parsear el body primero y comprobar el tamaño después. Cuando lo rechazas, ya pagaste el coste de memoria y CPU.
- Aceptar cualquier Content-Type y dejar que librerías adivinen cómo parsear, lo que puede activar rutas lentas o descompresión inesperada.
- Permitir archivos base64 dentro de JSON sin caps estrictos, de modo que un “archivo de 10 MB” se hinche en tránsito y en memoria.
Otro error fácil es bloquear usuarios reales porque nunca se testearon los límites. Clientes móviles pueden enviar headers más grandes. Webhooks de partners pueden incluir metadatos verbosos. Si eliges un número y lo lanzas, lo descubrirás en una hora punta.
Mejor enfoque: probar límites con tus clientes reales antes de aplicarlos estrictamente. Recoge algunos payloads representativos (uno normal, uno típico, uno peor-caso), fija límites con margen y mantén mensajes de error claros.
Comprobaciones rápidas que puedes hacer en 15 minutos
No necesitas un proyecto de seguridad completo para reducir riesgo rápido. Una pasada sobre límites de petición y ajustes de parseo puede evitar picos de memoria y ataques fáciles de denegación de servicio.
Comprobación de 15 minutos
Empieza en el edge (load balancer, CDN, reverse proxy o API gateway). Si acepta bodies enormes, tu app quizá nunca tenga oportunidad de protegerse. Luego entra a las rutas de la app.
- Confirma que hay un tope de tamaño de body duro en el edge y que se aplica realmente (intenta una petición que lo supere).
- Elige 3–5 endpoints que acepten bodies y anota sus tamaños máximos previstos.
- Añade una allowlist de Content-Type simple para los endpoints con body.
- Revisa la configuración del parser JSON para un límite máximo y limita profundidad o complejidad si el framework lo permite.
- Para uploads, evita bufferizar todo en memoria. Usa streaming o un flujo de upload dedicado.
Prueba de fallos en 5 minutos
Asegúrate de que la app falla de forma segura y predecible. Quieres una respuesta clara, consistente y visible en logs y monitorización.
Prueba estos casos de extremo a extremo:
- HTTP 413 cuando el payload es demasiado grande (y la conexión se cierra limpiamente).
- HTTP 415 cuando el Content-Type no está permitido.
- Timeouts que corten subidas lentas e interminables.
- Un IP ruidoso no puede disparar parseos costosos repetidamente.
Ejemplo: detener un pico real de memoria en un endpoint
Un endpoint público de signup parecía bien en pruebas, pero empezó a hacer timeouts durante una ráfaga de tráfico. CPU subió, memoria también y la app se reiniciaba cada pocos minutos. Parecía “demasiados usuarios”, pero los logs mostraron otra cosa: un pequeño número de peticiones tardaban muchísimo más que el resto.
La causa oculta era la forma del payload, no solo el conteo. Atacantes (y algunos clientes buggy) enviaban JSON sobredimensionado y objetos profundamente anidados. Incluso cuando la petición luego se rechazaba por validación, el servidor ya había gastado tiempo y memoria leyéndola y parseándola. Un puñado de esas peticiones podía llevar el proceso a thrashing de GC y luego a OOM.
La solución fue simple: límites de tamaño y reglas de parseo más estrictas, aplicadas antes de la lógica de la app.
Qué cambió
Ajustamos el endpoint de signup para aceptar solo lo que realmente necesita:
- Tamaño máximo pequeño para bodies JSON
- Profundidad máxima de JSON
- Comprobación estricta de Content-Type (solo JSON)
- Timeouts cortos para la lectura del body
- Respuestas HTTP 413 claras con mensajes breves
Después de eso, la misma ráfaga no provocó reinicios. La memoria se mantuvo estable porque los bodies gigantes nunca llegaban al parser JSON, y los payloads muy anidados se rechazaban rápido. Los logs quedaron más limpios: en vez de long traces, aparecían entradas cortas y consistentes como “Payload too large” o “JSON too deep.”
Para usuarios reales, casi nada cambió. Las inscripciones normales siguieron funcionando. La única diferencia visible fue que clientes rotos recibieron un error rápido y claro en lugar de una petición que giraba y acababa en timeout genérico.
Próximos pasos y cuándo pedir ayuda
Antes de cambiar nada, haz un snapshot rápido de lo que estás protegiendo. Esto te ayuda a no romper usuarios reales mientras cierras las rutas de DoS más sencillas.
Recolecta:
- Una lista de endpoints que aceptan bodies (JSON, forms, uploads)
- Tus límites actuales de petición (por endpoint y en proxy/servidor)
- Logs recientes de 413s, timeouts, picos de memoria y peticiones lentas
- Notas sobre quién usa cada endpoint y tamaños de payload típicos
- Cualquier job en background que posteé payloads grandes internamente
Desde ahí, despliega cambios en el orden más seguro: rutas públicas primero, luego receptores de webhooks y luego cualquier endpoint que parsee JSON no confiable. Mantén los límites estrictos en esas rutas y relájalos solo cuando haya una necesidad clara y una vía segura de parseo.
Tras el despliegue, evita que los límites suban con el tiempo. Guarda unos cuantos requests representativos (uno normal, uno cerca del límite, uno claramente demasiado grande) y ejecútalos tras cada release para confirmar rechazo rápido, manejo correcto de HTTP 413 y estabilidad de memoria y CPU.
Si heredaste una base de código generada por IA, merece la pena una auditoría focalizada por defaults inseguros como parsers de body ilimitados, endpoints de upload sin caps y checks de entrada débiles. FixMyMess (fixmymess.ai) se especializa en diagnosticar y reparar aplicaciones generadas por IA, incluyendo ajustar límites de petición, arreglar rutas de parseo que bufferizan en memoria y endurecer problemas relacionados. También ofrecen una auditoría gratuita para identificar problemas antes de hacer cambios.
Preguntas Frecuentes
Why do oversized requests cause outages so easily?
Un buen valor por defecto es poner un tope global pequeño para la mayoría de endpoints JSON y permitir límites mayores solo en las rutas que realmente los necesiten (por ejemplo, uploads). Así se evita que una sola petición accidental o maliciosa consuma suficiente memoria y CPU para ralentizar o reiniciar el servidor.
How do I pick a request size limit that won’t break real users?
Empieza por comprobar qué envían realmente tus clientes y elige un tope que cubra el uso normal y el “peor normal” con un pequeño margen. Mantén la mayoría de rutas JSON estrictas y trata uploads y webhooks como casos especiales con sus propios límites, en lugar de subir el tope para toda la app.
Do I really need limits both at the edge and inside the app?
Cuanto antes rechaces la petición, menos coste tiene. Los límites en el edge pueden detener un body enorme antes de que ocupe workers, buffers y CPU del parser; los límites en la app permiten reglas por ruta y protegen llamadas internas que podrían saltarse el edge.
What should my API return when the payload is too big?
Devuelve 413 Payload Too Large cuando el body excede el límite y mantén el mensaje breve y accionable, por ejemplo: “File too large. Max 10 MB.” Evita reflejar bodies o headers en la respuesta para no filtrar secretos.
Why is checking size after parsing a common mistake?
Porque el coste suele ocurrir antes de que tu handler se ejecute. Muchos stacks hacen buffer y parsean el body completo primero; si rechazas después, ya habrás pagado el coste en memoria y CPU y podrías provocar timeouts o reinicios.
How strict should I be about Content-Type on body endpoints?
Haz una allowlist de los Content-Type exactos que esperas por endpoint y rechaza tempranamente tipos faltantes o inesperados. Esto evita que el servidor adivine cómo parsear inputs extraños y reduce la posibilidad de activar rutas de parseo costosas por accidente o abuso.
What JSON parsing limits help prevent CPU spikes?
El tamaño por sí solo no basta: una petición pequeña pero muy anidada puede ser costosa. Si tu framework lo soporta, añade topes de profundidad JSON, conteo máximo de campos y longitud máxima de strings para fallar rápido ante inputs diseñados para consumir CPU.
Why is base64 in JSON a bad idea for uploads?
Base64 infla los datos y suele obligar al servidor a mantener grandes strings en memoria durante el parseo y la validación. Mejor usar un flujo de uploads dedicado que haga streaming del archivo y aplique límites de tamaño, en lugar de incrustar ficheros dentro de JSON.
What should I log when rejecting oversized requests?
Registra la ruta, el código de estado, el límite configurado, el Content-Length observado (cuando esté presente), el Content-Type y un request ID, pero no el body. Eso te da contexto para ajustar límites sin almacenar payloads grandes o datos sensibles.
When should I ask for help hardening request limits and parsers?
Si heredaste una base de código generada por IA, es habitual que los límites estén en valores inseguros o aplicados de forma inconsistente. FixMyMess puede hacer una auditoría gratuita para encontrar parsers peligrosos, caps faltantes y problemas de uploads, y luego aplicar correcciones verificadas rápidamente.