Webhooks Explicados: Payloads Assinados e Retries

Backend2026-10-16TryQuickToolBox

Você acabou de integrar um gateway de pagamento. A documentação diz: "Enviaremos uma requisição POST para o seu endpoint quando um pagamento for bem-sucedido." Fácil, certo? Você cria uma rota simples que aceita JSON, atualiza seu banco de dados e retorna 200. Então acontece: o pagamento de um cliente é bem-sucedido, mas seu servidor fica brevemente indisponível. O webhook nunca chega. Ou pior, chega duas vezes, e você credita a conta em dobro. Webhooks parecem simples até falharem.

Este artigo explica como webhooks realmente funcionam, por que payloads assinados são essenciais para a segurança e como construir uma lógica de retry que mantém sua integração confiável—mesmo quando as coisas dão errado.

O Que É um Webhook?

Um webhook é um callback HTTP: em vez de sua aplicação fazer polling em uma API para detectar mudanças, o provedor envia um POST HTTP para uma URL que você especifica quando um evento ocorre. É push em vez de pull.

Exemplos comuns incluem confirmações de pagamento (Stripe, PayPal), notificações de build de CI/CD (GitHub, GitLab) e plataformas de mensagens (Slack, Discord). O padrão é simples: você registra uma URL, o provedor envia payloads JSON para essa URL e seu servidor os processa.

Mas essa simplicidade esconde dois desafios críticos: autenticidade (como você sabe que a requisição realmente veio do provedor?) e confiabilidade (o que acontece se seu endpoint estiver fora do ar ou lento?).

Por Que Payloads Assinados Importam

Seu endpoint de webhook é uma URL pública. Qualquer pessoa pode enviar uma requisição POST para ele. Sem verificação, um atacante poderia forjar um evento de pagamento bem-sucedido, disparar um envio ou corromper seus dados.

Payloads assinados resolvem isso incluindo uma assinatura criptográfica nos headers da requisição. O provedor calcula um HMAC (hash-based message authentication code) usando um segredo compartilhado e o corpo da requisição, e então o envia junto com o payload. Seu servidor recalcula o HMAC e compara. Se coincidirem, a requisição é autêntica.

Como a Assinatura HMAC Funciona

  1. O provedor e você compartilham uma chave secreta (fornecida durante a configuração).
  2. Para cada webhook, o provedor concatena o timestamp e o corpo bruto da requisição, e então calcula um hash HMAC-SHA256 usando o segredo.
  3. A assinatura é enviada em um header, geralmente algo como X-Signature ou Stripe-Signature.
  4. Seu servidor recalcula o HMAC usando o mesmo segredo e o compara com a assinatura recebida.
  5. Se coincidirem, você processa o evento; caso contrário, rejeita com 401.

Sempre use o corpo bruto da requisição para o cálculo do HMAC. Se seu framework fizer o parse do JSON antes da verificação, o corpo pode ser alterado (espaços em branco, ordem das chaves), fazendo a verificação falhar.

Exemplo: Verificando uma Assinatura de Webhook em Node.js

const crypto = require('crypto');
const express = require('express');
const app = express();

// Use raw body for signature verification
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-signature'];
  const secret = process.env.WEBHOOK_SECRET;
  const payload = req.body; // Buffer

  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(401).send('Invalid signature');
  }

  // Process the event
  const event = JSON.parse(payload.toString());
  // ... handle event

  res.status(200).send('OK');
});

Observe o uso de timingSafeEqual para prevenir ataques de temporização. Nunca use uma comparação simples com === para assinaturas.

Projetando Lógica de Retry

Mesmo com assinaturas, webhooks podem falhar. Seu servidor pode estar fora do ar, a rede pode derrubar a requisição, ou seu processamento pode atingir timeout. Provedores geralmente tentam novamente entregas falhas, mas você também deve projetar seu sistema para lidar com retries de forma graciosa.

O Que Conta Como Falha?

A maioria dos provedores considera qualquer resposta diferente de 2xx como falha. Isso inclui redirecionamentos 3xx, erros de cliente 4xx e erros de servidor 5xx. Alguns também impõem um timeout (por exemplo, 5 segundos). Se você não responder rapidamente, a entrega é marcada como falha e tentada novamente.

Backoff Exponencial com Jitter

Provedores geralmente tentam novamente com backoff exponencial: primeira tentativa após 1 minuto, depois 5 minutos, 30 minutos, etc. Adicionar jitter (variação aleatória) previne thundering herds se muitos webhooks falharem simultaneamente.

Se você está construindo um emissor de webhooks, implemente o backoff assim:

Após um número máximo de tentativas, mova o evento para uma dead-letter queue para inspeção manual.

Idempotência: Lidando com Entregas Duplicadas

Retries significam que seu endpoint pode receber o mesmo evento múltiplas vezes. Seu processamento deve ser idempotente: processar o mesmo evento duas vezes deve ter o mesmo efeito que processá-lo uma vez.

Estratégias comuns:

Melhores Práticas para Consumidores de Webhooks

PráticaPor Que Importa
Verificar assinaturasPrevine requisições forjadas
Responder rapidamente (em até 2s)Evita timeouts e retries desnecessários
Processar de forma assíncronaDesacopla o recebimento do processamento pesado
Usar handlers idempotentesSeguro contra entregas duplicadas
Registrar todos os eventosAuxilia na depuração e auditoria
Monitorar falhasDetecta problemas antes que escalem

Considere também validar o timestamp na assinatura para prevenir ataques de replay. Muitos provedores incluem um timestamp no payload assinado; rejeite eventos mais antigos que alguns minutos.

Melhores Práticas para Produtores de Webhooks

Se você está construindo um serviço que envia webhooks, siga estas diretrizes:

FAQ

O que é um payload de webhook assinado?

Um payload assinado inclui uma assinatura criptográfica (geralmente HMAC) calculada a partir do corpo da requisição e um segredo compartilhado. O receptor verifica a assinatura para garantir que a requisição é autêntica e não modificada.

Quantas vezes um webhook deve ser tentado novamente?

Não há um número universal, mas a maioria dos provedores tenta novamente de 3 a 5 vezes ao longo de várias horas. Depois disso, o evento geralmente é marcado como falho e pode ficar disponível para replay manual. Seu sistema deve lidar com duplicatas de forma idempotente independentemente do número de retries.

Posso usar webhooks sem HTTPS?

Tecnicamente sim, mas você deve sempre usar HTTPS. Webhooks frequentemente contêm dados sensíveis, e sem TLS, um atacante poderia interceptar ou adulterar o payload. HTTPS também garante que você está enviando para o servidor pretendido.

Depurando Webhooks com Logs do Nginx

Quando webhooks falham, o primeiro lugar para olhar são os logs do seu servidor web. Se você usa Nginx, os logs de acesso e erro registram cada requisição, incluindo códigos de status e tempos de resposta. Você pode identificar rapidamente padrões: 401s repetidos (falha na verificação de assinatura), 500s (erros da aplicação) ou timeouts.

Para uma análise mais rápida, experimente o Nginx Log Analyzer para fazer o parse e resumir o tráfego de webhooks, filtrar por código de status e identificar endpoints problemáticos.