Backend·Publicado em 19 de ago. de 2026 · Atualizado em 4 de set. de 2026

Rate limiting em serverless: o bug que passa em todo teste local

Um contador em memória funciona perfeitamente na sua máquina e desaparece em produção, sem erro, sem log e sem nada que denuncie. É a falha mais silenciosa que encontrei.

O contador que some

A implementação ingênua de rate limiting guarda um Map em memória: chave é o IP, valor é a contagem na janela. Em um processo de longa duração isso funciona, pois o processo é um só e o mapa vive enquanto ele viver.

Em execução serverless, não existe "o processo". Cada requisição pode cair em uma instância diferente, criada agora e destruída em seguida. O contador que você incrementou na requisição anterior pode simplesmente não existir na próxima.

O código está sintaticamente correto, os testes passam, o deploy sobe, e a proteção contra força bruta simplesmente não existe.

Por que o teste local passa

Rodando localmente há um processo só. Seis requisições seguidas ao endpoint de contato batem no mesmo mapa, a sexta é bloqueada, o teste fica verde e a sensação é de dever cumprido.

O teste está medindo a coisa certa no ambiente errado. Ele prova que a lógica de contagem funciona; não prova que o estado é compartilhado, que é justamente a propriedade da qual o mecanismo depende.

Store compartilhado sem Redis

A resposta reflexa é adicionar Redis. Para um sistema com volume de dezenas a centenas de visitas por mês, isso significa um provedor a mais para configurar, pagar e manter, resolvendo um problema de escala que não existe.

O PostgreSQL que já está no projeto resolve, com uma tabela e um upsert atômico:

INSERT INTO rate_limit_counter (key, window_start, hits)
VALUES ($1, $2, 1)
ON CONFLICT (key, window_start)
DO UPDATE SET hits = rate_limit_counter.hits + 1
RETURNING hits;

O incremento é atômico por construção: duas instâncias concorrentes não perdem contagem, porque o banco serializa a operação. E a chave guarda o IP hasheado, nunca em claro.

Compartilhar
Rate limiting em serverless: o bug que passa em todo teste local | DaviDev | DaviDev