Limites de tamanho de requisição: endureça o processamento do corpo para prevenir DoS
Defina limites de tamanho de requisição e regras de parsing mais seguras para evitar picos de memória, lentidão e negação de serviço por requisições grandes ou malformadas.

Por que requisições muito grandes viram um problema real
Requisições exageradas parecem inofensivas até derrubar um app. Um único upload grande, um blob JSON enorme ou uma requisição presa em um loop de retry pode consumir memória e CPU, deixar todo mundo lento e, às vezes, travar o servidor.
Alguns termos simples ajudam:
- Payload: os dados que o cliente envia.
- Corpo: onde esses dados normalmente ficam dentro de uma requisição HTTP (por exemplo, JSON ou um arquivo).
- Parser: o código (ou biblioteca) que lê o corpo e o transforma em algo que seu app pode usar, como um objeto, uma string ou um arquivo salvo.
O risco não é só “hackers”. Muitos incidentes são acidentais: um bug móvel que envia 50 MB, um frontend que codifica uma imagem em base64 dentro de JSON, ou uma integração que vai adicionando campos até o corpo ficar massivo. O resultado pode parecer uma negação de serviço mesmo quando ninguém quis causar dano.
Claro, a mesma fragilidade é fácil de abusar de propósito. Se seu servidor aceita corpos ilimitados, um atacante pode enviar requisições muito grandes (ou muitas médias) e forçar parsing custoso. Isso pode deixar seu app sem memória, encher discos e bloquear tráfego legítimo.
Limites de tamanho importam, mas não são “configure e esqueça”. Limites muito baixos quebram usuários reais (especialmente uploads). Limites muito altos ainda permitem picos de memória. O objetivo é um padrão seguro, com exceções claras para os poucos endpoints que realmente precisam de corpos maiores.
O que pode dar errado quando você aceita payloads grandes
Aceitar “o que o cliente enviar” é um jeito rápido de transformar um endpoint em uma causa de outage. Sem limites de tamanho, um POST oversized pode empurrar seu app para uso elevado de memória, tempos de resposta longos e falhas em cascata.
O primeiro impacto costuma ser memória. Muitos frameworks fazem buffer do corpo inteiro antes do seu handler rodar. Um payload JSON grande ou um upload multipart pode ser copiado várias vezes durante buffering, decodificação e validação. Isso multiplica o custo de memória, e não é preciso muitas requisições concorrentes para esgotar um container ou VM.
CPU é o próximo problema. Parsear JSON enorme é custoso, e objetos profundamente aninhados podem piorar isso. Mesmo quando o payload é “válido”, o servidor pode gastar segundos apenas transformando bytes em objetos, deixando menos CPU para trabalho real. Alguns parsers fazem trabalho extra em entradas grandes (coerção, validação), o que aumenta o custo por requisição.
Corpos exagerados também prendem workers. Um upload lento mantém a conexão aberta, ocupando uma thread de worker ou atenção do event loop e empurrando outros usuários para timeouts. Sob carga, os retries se acumulam e amplificam o dano.
Uploads podem prejudicar seu disco silenciosamente também. Se arquivos temporários caírem no lugar errado (ou nunca forem limpos), uma explosão de requisições grandes pode encher o disco e quebrar partes não relacionadas do app.
Times frequentemente subestimam os custos “secundários”: picos de banda e contas de nuvem (especialmente quando clientes re-tentam), filas de background mais lentas por pressão de CPU e memória, logs barulhentos e pontos cegos porque alguns erros acontecem antes do código da aplicação rodar. Outro problema comum é fazer checagens de autenticação depois que o corpo já foi parseado, o que torna o abuso mais barato.
Um cenário realista: um endpoint de cadastro aceita JSON com um campo “profile”. Um cliente bugado envia um blob de 50 MB. O servidor faz buffer, parseia e trava. Adicione mais alguns em paralelo e o serviço fica indisponível.
Onde impor limites para que realmente funcionem
Os limites de tamanho mais confiáveis são aplicados em mais de um lugar. Se você só coloca um limite dentro do app, o servidor ainda pode gastar tempo e memória lendo um corpo gigante antes do seu código rejeitá-lo. Se você só coloca um limite na borda, ainda pode querer controles mais rígidos para endpoints específicos.
1) Camada de borda: pare requisições grandes antes de chegarem ao app
Sua primeira linha de defesa deve ser a camada que recebe o tráfego primeiro, como CDN, load balancer, reverse proxy ou API gateway. É aqui que você pode rejeitar corpos grandes cedo, retornar um HTTP 413 e evitar ocupar workers do app. Também ajuda contra uploads lentos e exagerados que visam manter conexões abertas.
Mantenha o limite da borda estrito para tráfego geral. Se precisar de uploads maiores, trate-os por um caminho separado ou serviço em vez de aumentar o limite para tudo.
2) Camada do app: limites por endpoint e padrões mais seguros
Dentro do app, imponha limites novamente para que cada endpoint tenha o teto correto. Um endpoint de login deve aceitar um JSON pequeno. Um endpoint de foto de perfil pode aceitar mais.
Um padrão prático é um tamanho máximo global pequeno para a maioria das rotas da API, com limites por rota para exceções. Endpoints públicos devem ser mais estritos que endpoints autenticados. Rejeite cedo quando possível (checando Content-Length quando presente) e use timeouts para leitura do corpo, não apenas para processamento.
Uploads merecem tratamento especial. Trate upload de arquivos como um fluxo diferente das APIs JSON normais, porque uploads podem disparar grandes picos de memória se um parser fizer buffer de tudo. Prefira manipulação por streaming ou em chunks, escreva em disco ou storage de objetos e valide tipo e tamanho do arquivo antes de trabalho custoso.
Como escolher limites seguros sem quebrar usuários
Comece listando cada endpoint que aceita corpo. Limites são seguros apenas quando refletem o uso real. A maneira mais rápida é inventariar o que você realmente aceita, não o que você supõe aceitar.
Agrupe endpoints pelo que devem receber. APIs JSON geralmente precisam dos menores limites. Form posts costumam ficar no meio. Uploads precisam de limites maiores. Webhooks podem surpreender com picos ocasionais.
Um ponto de partida prático é definir padrões apertados e aumentar limites apenas onde há razão clara. Por exemplo:
- Endpoints JSON: 16 KB a 256 KB
- Form posts (sem arquivos): 64 KB a 512 KB
- Uploads de arquivos: 5 MB a 25 MB (apenas em rotas específicas)
- Webhooks: 256 KB a 2 MB (baseado em docs do provedor e logs)
Esses números não são universais, mas o padrão importa: a maioria das rotas deve ser pequena, e poucas devem ser permitidas grandes.
Ao escolher um teto, lembre-se que você está limitando mais que “dados do usuário”. Headers, cookies e boundaries multipart adicionam overhead. Base64 também incha o conteúdo em cerca de um terço. Uma imagem de 2 MB dentro de JSON pode chegar perto de 2,7 MB antes do app começar a processá-la.
Planeje exceções explicitamente. Se um endpoint precisa mesmo de 25 MB, dê 25 MB só para ele e mantenha o padrão baixo, em vez de definir tudo como “ilimitado”. Anote quem usa a exceção, o que eles enviam e qual é um limite razoável.
Um cheiro comum é um endpoint genérico (como “/api/save”) que aceita qualquer coisa. Dividir isso em um endpoint JSON pequeno e um endpoint de upload separado frequentemente impede picos de memória repentinos sem quebrar usuários normais.
Regras de parsing do corpo que reduzem picos de memória e CPU
A maneira mais rápida de disparar picos de memória e CPU é deixar o app adivinhar o que um corpo contém e então parseá-lo automaticamente. Endurecer o parsing do corpo significa ser estrito: aceite apenas o que espera e só parseie quando realmente precisar.
Comece com uma allowlist de Content-Types por endpoint. Se um endpoint espera JSON, aceite apenas application/json (e as variantes exatas que você suporta). Se o Content-Type estiver ausente ou desconhecido em um endpoint com corpo, rejeite cedo. Isso evita parsing acidental de textos enormes, codificações estranhas ou entradas que façam o parser trabalhar demais.
Coloque guardrails no parsing de JSON
Se seu framework suportar, limite a complexidade do JSON, não apenas o tamanho. Uma requisição pequena ainda pode ser cara se for profundamente aninhada ou tiver milhares de chaves.
Bons padrões a considerar:
- Profundidade máxima de JSON (exemplo: 20 a 50 níveis)
- Contagem máxima de campos (exemplo: 1.000 a 10.000 chaves)
- Comprimento máximo de string por campo (exemplo: 10 KB a 100 KB)
- Tratamento UTF-8 estrito (rejeitar sequências inválidas)
- Falha rápida em chaves duplicadas (se suportado)
Em seguida, desative parsing automático em rotas que não precisam. Muitos apps parseiam JSON globalmente para toda requisição, incluindo health checks, endpoints de verificação de webhook e rotas GET simples. Isso é trabalho desperdiçado e um alvo fácil.
Para uploads, prefira streaming (processar em chunks) em vez de ler o arquivo inteiro na memória antes de gravar em disco ou storage. Combine streaming com limites de tamanho para que uma única requisição não consiga encher a RAM.
Exemplo: um endpoint de cadastro espera um JSON pequeno. Se o servidor aceita qualquer Content-Type e parseia automaticamente, um atacante pode enviar um payload multi-megabyte com estrutura complicada que consome CPU. Regras rígidas de Content-Type juntamente com limites de profundidade e de campos transformam isso em uma rejeição rápida em vez de uma outage.
Passo a passo: adicionar limites e endurecer parsing
A maioria das outages por requisições exageradas atingem endpoints fáceis de alcançar: formulários públicos, APIs não autenticadas e webhooks. Comece listando cada rota que aceita corpo, então marque quais são públicas, quais são chamadas por terceiros e quais aceitam arquivos.
Checklist prático
Comece na borda (load balancer, reverse proxy, CDN ou API gateway). Controles na borda são sua primeira linha de defesa porque bloqueiam a requisição antes do app gastar memória em parsing.
- Identifique endpoints de alto risco (públicos, não autenticados, webhooks, uploads).
- Configure limites na borda: tamanho máximo do corpo, tamanho máximo de headers e timeout curto de leitura.
- Adicione exceções por caminho apenas quando puder justificá-las (por exemplo, uma rota de upload).
- Garanta que a borda retorne HTTP 413 para corpos que excedem o limite.
- Confirme que o limite é realmente aplicado (algumas stacks fazem buffer antes de rejeitar).
Então trave isso dentro do app. Limites dentro do app protegem quando o tráfego contorna a borda (chamadas internas, misconfigurações) e permitem regras mais específicas que um único teto global.
- Aplique limites de tamanho por rota e por Content-Type (JSON vs multipart upload).
- Defina padrões do parser: tamanho máximo de JSON, profundidade máxima e parsing estrito (rejeitar JSON inválido; rejeitar chaves duplicadas se suportado).
- Evite parsear na memória quando possível: faça streaming de uploads e valide tipo e tamanho cedo.
- Teste requisições normais e intencionalmente oversized, incluindo corpos comprimidos se você os aceitar.
- Observe logs de 413s e timeouts de leitura por uma semana, então ajuste somente para necessidades comprovadas.
Um caso real: um endpoint de webhook aceita JSON sem um teto. Um payload oversized pode subir memória e reiniciar o serviço. Um limite JSON pequeno por rota no webhook (enquanto se permite limites maiores apenas em rotas de upload autenticadas) previne o pico sem quebrar o tráfego normal.
Tratamento de falhas seguro e amigável ao usuário
Depois de impor limites, a próxima pergunta é o que o cliente vê ao ultrapassar o limite. Um bom tratamento de falha impede o ataque (ou acidente), mas ainda diz ao usuário real como consertar a requisição.
Use códigos de status que correspondam ao problema. Um payload grande demais deve retornar 413 Payload Too Large. Um corpo que você não aceita (por exemplo, XML em endpoint JSON-only) deve retornar 415 Unsupported Media Type. Se a requisição estiver malformada, 400 Bad Request costuma bastar. Mensagens correspondentes ajudam clientes a evitar retries cegos.
Mantenha mensagens de erro curtas e práticas. Diga o que mudar: “Arquivo muito grande. Máx 10 MB.” ou “Somente application/json é suportado.” Não echoe corpos ou headers na resposta. Isso reduz chance de vazar segredos.
Para logging, queira contexto suficiente para debugar sem armazenar o payload rejeitado. Um meio-termo é logar endpoint e método, código de status (413, 415), Content-Length observado (se fornecido) e o limite configurado, Content-Type, além de request ID e user/account ID (se conhecidos).
Decida onde falhar rápido por endpoint. Para endpoints públicos de upload e rotas de autenticação, falhar na borda economiza trabalho ao app. Para endpoints cujos limites dependem de regras por rota, o app pode precisar decidir, mas ainda deve rejeitar antes de parsear o corpo completo.
Erros comuns que levam a outages ou DoS fácil
A maioria das outages por requisições grandes não vem de “sem limites”. Acontecem quando limites existem, mas são desiguais, contornáveis ou aplicados tarde demais para proteger a memória.
Um padrão comum é definir um único teto global e achar que está tudo resolvido. Aí uma rota de upload, um webhook ou uma exceção do reverse proxy permite corpos muito maiores. Atacantes não precisam atingir sua API principal, só uma rota fraca.
Erros recorrentes:
- Limitar corpos JSON mas esquecer uploads e webhooks (ou definir uma categoria separada como “ilimitada”).
- Elevar limites “só por hoje” para desbloquear um cliente e nunca reverter.
- Parsear o corpo antes e checar o tamanho depois. Quando você rejeita, já pagou o custo de memória e CPU.
- Aceitar qualquer Content-Type e deixar bibliotecas adivinharem como parsear, o que pode disparar caminhos de parsing lentos ou descompressão inesperada.
- Permitir arquivos em base64 dentro de JSON sem caps rígidos, fazendo um “arquivo de 10 MB” inflar em trânsito e na memória.
Outro erro comum é bloquear usuários reais porque limites nunca foram testados. Clientes móveis podem enviar headers maiores. Webhooks de parceiros podem incluir metadados verbose. Se você chutar um número e colocar em produção, aprenderá isso em uma hora de pico.
Uma abordagem melhor é testar limites com seus clientes reais antes de aplicá-los fortemente. Colete alguns payloads representativos (um pequeno, um típico, um perto do limite), ajuste limites com margem e mantenha mensagens de erro claras.
Verificações rápidas que você pode fazer em 15 minutos
Você não precisa de um projeto de segurança completo para reduzir risco rapidamente. Uma revisão rápida de limites e configurações de parsing pode prevenir picos de memória e tentativas fáceis de DoS.
Verificação de 15 minutos
Comece na borda (load balancer, CDN, reverse proxy ou API gateway). Se ele aceita corpos enormes, seu app pode nunca ter chance de se proteger. Depois siga para as rotas do app.
- Confirme que existe um limite rígido de tamanho do corpo na borda e que ele é realmente aplicado (teste com uma requisição que exceda).
- Escolha 3 a 5 endpoints que aceitam corpos e registre os tamanhos máximos pretendidos.
- Adicione uma allowlist de Content-Type para endpoints de corpo.
- Verifique as configurações do parser JSON quanto a limite de tamanho e limite de profundidade/complexidade se o framework suportar.
- Para uploads, evite buffer do arquivo inteiro na memória. Use streaming ou um fluxo de upload dedicado.
Teste de falha de 5 minutos
Garanta que o app falhe de forma segura e previsível. Você quer uma resposta clara e consistente visível em logs e monitoramento.
Teste estes comportamentos fim a fim:
- HTTP 413 quando o payload é grande demais (e a conexão é fechada limpidamente).
- HTTP 415 quando o Content-Type não é permitido.
- Timeouts que interrompem uploads lentos e intermináveis.
- Um IP barulhento não pode disparar parsing caro repetidamente.
Exemplo: parar um pico de memória real em um endpoint
Um endpoint público de cadastro parecia bem em testes, mas começou a dar timeouts durante um pico de tráfego. CPU subiu, memória pulou e o app reiniciava a cada poucos minutos. Parecia “muitos usuários”, mas os logs mostraram outra coisa: poucas requisições demoravam muito mais que o restante.
A causa oculta era a forma do payload, não só a quantidade. Atacantes (e alguns clientes com bugs) enviavam JSONs exagerados e objetos profundamente aninhados. Mesmo quando a requisição era rejeitada depois por validação, o servidor já havia gasto tempo e memória lendo e parseando. Alguns poucos desse tipo podiam empurrar o processo para thrash de garbage collection e depois OOM.
A correção foi simples: limites de tamanho e regras de parsing mais rígidas, aplicadas antes da lógica da aplicação.
O que mudou
Tornamos o endpoint de cadastro estrito quanto ao que ele realmente precisa:
- Um tamanho máximo pequeno para requisições JSON
- Profundidade máxima para objetos JSON
- Checagem estrita de Content-Type (apenas JSON)
- Timeouts curtos para leitura do corpo
- Respostas HTTP 413 claras com mensagem curta
Depois disso, o mesmo pico de tráfego não causou reinícios. A memória ficou estável porque corpos gigantes nunca alcançaram o parser JSON, e payloads muito aninhados foram rejeitados rapidamente. Os logs também ficaram mais limpos: em vez de longas pilhas, apareciam entradas curtas e consistentes como “Payload too large” ou “JSON too deep”.
Para usuários reais, quase nada mudou. Cadastros normais continuaram funcionando. A única diferença visível foi que clientes quebrados receberam um erro claro e rápido em vez de uma requisição travada e timeout genérico.
Próximos passos e quando pedir ajuda
Antes de mudar qualquer coisa, faça um snapshot rápido do que você está protegendo. Isso ajuda a evitar quebrar usuários reais enquanto fecha as rotas de DoS mais fáceis.
Colete:
- Uma lista de endpoints que aceitam corpos (JSON, forms, uploads)
- Seus limites atuais de tamanho de requisição (por endpoint e no proxy/servidor do app)
- Logs recentes de 413s, timeouts, picos de memória e requisições lentas
- Notas sobre quem usa cada endpoint e tamanhos típicos de payload
- Quaisquer jobs de background que postem payloads grandes internamente
A partir daí, faça rollout das mudanças na ordem mais segura: rotas públicas primeiro, depois receptores de webhook, e então qualquer endpoint que parseie JSON não confiável. Mantenha limites apertados nessas rotas e afrouxe somente quando houver necessidade comprovada e um fluxo de parsing seguro.
Depois do deploy, evite que os limites aumentem sem controle. Salve alguns requests representativos (um normal, um perto do limite, um claramente oversized) e rode-os após cada release para confirmar rejeição rápida, tratamento correto de HTTP 413 e estabilidade de memória/CPU.
Se você herdou uma base de código gerada por IA, vale a pena uma auditoria focada em defaults arriscados como parsers de corpo ilimitados, endpoints de upload sem caps e checagens fracas de entrada. FixMyMess (fixmymess.ai) é especializado em diagnosticar e reparar aplicações geradas por IA, incluindo apertar limites de requisição, consertar caminhos de parsing que fazem buffer em memória e endurecer questões relacionadas. Eles também oferecem uma auditoria de código gratuita para identificar problemas antes de você aplicar mudanças.
Perguntas Frequentes
Por que requisições grandes causam facilmente indisponibilidade?
Um bom padrão é definir um limite global pequeno para a maioria dos endpoints JSON e permitir aumentos apenas nas rotas que realmente precisam (como uploads). Isso evita que uma requisição acidental ou maliciosa consuma memória e CPU suficientes para deixar o servidor lento ou causar crash.
Como escolho um limite de tamanho de requisição sem quebrar usuários reais?
Comece verificando o que seus clientes realmente enviam e escolha um limite que cubra o uso normal e o “pior caso normal” com uma pequena margem. Mantenha a maioria das rotas JSON estritas e trate uploads e webhooks como exceções com limites próprios, para não aumentar o teto para todo o app.
Eu realmente preciso de limites tanto na borda quanto dentro do app?
Sim. Quanto mais cedo você rejeitar, menos custo terá. Limites na borda podem bloquear corpos enormes antes de ocupar workers, buffers ou CPU do parser; limites na aplicação permitem regras específicas por rota e protegem tráfego interno que pode contornar a borda.
O que minha API deve retornar quando o payload for grande demais?
Retorne 413 Payload Too Large quando o corpo exceder o limite e mantenha a mensagem curta e acionável, por exemplo: “Payload muito grande. Máx 256 KB.” Não ecoe o corpo da requisição ou cabeçalhos na resposta para evitar vazamento de segredos.
Por que checar o tamanho depois do parsing é um erro comum?
Porque o custo frequentemente ocorre antes do seu handler ser executado. Muitos stacks fazem buffer e parse do corpo completo primeiro, então rejeitar depois ainda consome memória e CPU e pode provocar timeouts ou reinícios sob concorrência.
Quão estrito devo ser sobre Content-Type em endpoints que aceitam corpo?
Use uma allowlist com os valores exatos de Content-Type esperados por rota e rejeite tipos ausentes ou inesperados cedo. Isso evita que o servidor tente adivinhar como interpretar entradas estranhas e reduz o risco de caminhos de parsing caros serem acionados por acidente ou abuso.
Quais limites de parsing JSON ajudam a prevenir picos de CPU?
Tamanho sozinho nem sempre basta: payloads pequenos mas profundamente aninhados podem custar muito para parsear. Se seu framework permitir, adicione limites de profundidade JSON, número total de campos e tamanho máximo de strings para falhar rápido em entradas projetadas para consumir CPU.
Por que base64 dentro de JSON é uma má ideia para uploads?
Base64 infla os dados e frequentemente força o servidor a manter grandes strings na memória durante o parsing e validação. Uma alternativa melhor é um fluxo de upload dedicado que faça streaming do arquivo e aplique limites de tamanho no próprio arquivo, em vez de embuti-lo no JSON.
O que devo logar ao rejeitar requisições grandes?
Registre o endpoint, o código de status, o limite configurado, o Content-Length observado (quando presente), o Content-Type e um request ID — mas não armazene o corpo. Isso fornece contexto suficiente para depurar e ajustar limites sem salvar payloads grandes ou dados sensíveis.
Quando devo pedir ajuda para endurecer limites de requisição e parsers?
Peça ajuda se herdou um código gerado por IA ou se os defaults parecem inseguros ou inconsistentes. FixMyMess pode executar uma auditoria gratuita para encontrar parsers arriscados, caps ausentes e problemas em uploads, e então aplicar correções verificadas rapidamente.