Webhooks explicados: payloads firmados y reintentos

Backend2026-10-16TryQuickToolBox

Acabas de integrar una pasarela de pago. La documentación dice: "Enviaremos una petición POST a tu endpoint cuando un pago se complete con éxito". Fácil, ¿verdad? Creas una ruta sencilla que acepta JSON, actualiza tu base de datos y devuelve 200. Entonces ocurre: el pago de un cliente se completa, pero tu servidor está caído brevemente. El webhook nunca llega. O peor aún, llega dos veces y abonas el importe duplicado en la cuenta. Los webhooks parecen simples hasta que fallan.

Este artículo explica cómo funcionan realmente los webhooks, por qué los payloads firmados son esenciales para la seguridad y cómo construir una lógica de reintentos que mantenga tu integración confiable, incluso cuando las cosas van mal.

¿Qué es un webhook?

Un webhook es un callback HTTP: en lugar de que tu aplicación consulte una API en busca de cambios, el proveedor envía un POST HTTP a una URL que especificas cuando ocurre un evento. Es push en lugar de pull.

Ejemplos comunes incluyen confirmaciones de pago (Stripe, PayPal), notificaciones de compilación CI/CD (GitHub, GitLab) y plataformas de mensajería (Slack, Discord). El patrón es simple: registras una URL, el proveedor envía payloads JSON a esa URL y tu servidor los procesa.

Pero esta simplicidad esconde dos desafíos críticos: autenticidad (¿cómo sabes que la petición realmente proviene del proveedor?) y confiabilidad (¿qué ocurre si tu endpoint está caído o lento?).

Por qué importan los payloads firmados

Tu endpoint de webhook es una URL pública. Cualquiera puede enviarle una petición POST. Sin verificación, un atacante podría falsificar un evento de pago exitoso, desencadenar un envío o corromper tus datos.

Los payloads firmados resuelven esto incluyendo una firma criptográfica en las cabeceras de la petición. El proveedor calcula un HMAC (código de autenticación de mensajes basado en hash) usando un secreto compartido y el cuerpo de la petición, y luego lo envía junto con el payload. Tu servidor recalcula el HMAC y lo compara. Si coinciden, la petición es auténtica.

Cómo funciona la firma HMAC

  1. El proveedor y tú compartís una clave secreta (proporcionada durante la configuración).
  2. Para cada webhook, el proveedor concatena el timestamp y el cuerpo crudo de la petición, y luego calcula un hash HMAC-SHA256 usando el secreto.
  3. La firma se envía en una cabecera, a menudo algo como X-Signature o Stripe-Signature.
  4. Tu servidor recalcula el HMAC usando el mismo secreto y lo compara con la firma recibida.
  5. Si coinciden, procesas el evento; de lo contrario, lo rechazas con un 401.

Usa siempre el cuerpo crudo de la petición para el cálculo del HMAC. Si tu framework parsea el JSON antes de que verifiques, el cuerpo puede alterarse (espacios en blanco, orden de claves), provocando que la verificación falle.

Ejemplo: verificar una firma de webhook en 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');
});

Observa el uso de timingSafeEqual para prevenir ataques de temporización. Nunca uses una comparación simple con === para las firmas.

Diseñar la lógica de reintentos

Incluso con firmas, los webhooks pueden fallar. Tu servidor podría estar caído, la red podría perder la petición o tu procesamiento podría agotar el tiempo de espera. Los proveedores normalmente reintentan las entregas fallidas, pero tú también deberías diseñar tu sistema para manejar los reintentos con elegancia.

¿Qué cuenta como fallo?

La mayoría de los proveedores considera cualquier respuesta que no sea 2xx como un fallo. Esto incluye redirecciones 3xx, errores de cliente 4xx y errores de servidor 5xx. Algunos también aplican un timeout (por ejemplo, 5 segundos). Si no respondes rápido, la entrega se marca como fallida y se reintenta.

Backoff exponencial con jitter

Los proveedores suelen reintentar con backoff exponencial: primer reintento tras 1 minuto, luego 5 minutos, 30 minutos, etc. Añadir jitter (variación aleatoria) previene estampidas si muchos webhooks fallan simultáneamente.

Si estás construyendo un emisor de webhooks, implementa el backoff así:

Tras un número máximo de intentos, mueve el evento a una cola de mensajes muertos para inspección manual.

Idempotencia: manejar entregas duplicadas

Los reintentos implican que tu endpoint puede recibir el mismo evento varias veces. Tu procesamiento debe ser idempotente: procesar el mismo evento dos veces debe tener el mismo efecto que procesarlo una vez.

Estrategias comunes:

Buenas prácticas para consumidores de webhooks

PrácticaPor qué importa
Verificar firmasPreviene peticiones falsificadas
Responder rápido (en menos de 2 s)Evita timeouts y reintentos innecesarios
Procesar de forma asíncronaDesacopla la recepción del procesamiento pesado
Usar manejadores idempotentesSeguro frente a entregas duplicadas
Registrar todos los eventosFacilita la depuración y la auditoría
Monitorizar fallosDetecta problemas antes de que escalen

Considera también validar el timestamp en la firma para prevenir ataques de replay. Muchos proveedores incluyen un timestamp en el payload firmado; rechaza eventos con más de unos minutos de antigüedad.

Buenas prácticas para productores de webhooks

Si estás construyendo un servicio que envía webhooks, sigue estas pautas:

FAQ

¿Qué es un payload de webhook firmado?

Un payload firmado incluye una firma criptográfica (normalmente HMAC) calculada a partir del cuerpo de la petición y un secreto compartido. El receptor verifica la firma para asegurar que la petición es auténtica y no ha sido modificada.

¿Cuántas veces se debe reintentar un webhook?

No hay un número universal, pero la mayoría de los proveedores reintentan de 3 a 5 veces durante varias horas. Después, el evento normalmente se marca como fallido y puede quedar disponible para reenvío manual. Tu sistema debe manejar duplicados de forma idempotente independientemente del número de reintentos.

¿Puedo usar webhooks sin HTTPS?

Técnicamente sí, pero siempre deberías usar HTTPS. Los webhooks a menudo contienen datos sensibles y, sin TLS, un atacante podría interceptar o manipular el payload. HTTPS también garantiza que estás enviando al servidor previsto.

Depurar webhooks con logs de Nginx

Cuando los webhooks fallan, el primer lugar donde mirar son los logs de tu servidor web. Si usas Nginx, los logs de acceso y errores registran cada petición, incluidos los códigos de estado y los tiempos de respuesta. Puedes detectar patrones rápidamente: 401 repetidos (fallo en la verificación de firma), 500 (errores de aplicación) o timeouts.

Para un análisis más rápido, prueba el Nginx Log Analyzer para parsear y resumir el tráfico de webhooks, filtrar por código de estado e identificar endpoints problemáticos.