Configuraste el webhook en el panel del proveedor, lo apuntaste a http://localhost:3000 y… no llegó nada. No es un bug: es la red. Los proveedores entregan webhooks por internet, y tu localhost simplemente no existe para ella. En esta guía comparamos las cuatro estrategias para probar webhooks durante el desarrollo — endpoints de inspección, túneles, CLIs oficiales y reenvío — y en qué escenario cada una es la elección correcta.
Por qué el proveedor no ve tu localhost
Cuando un proveedor dispara un webhook, actúa como un cliente HTTP común: resuelve la dirección que registraste y abre una conexión hacia ella. Si esa dirección es localhost (o 127.0.0.1), cada máquina la resuelve hacia sí misma — para el servidor del proveedor, "localhost" es el propio servidor del proveedor, no tu máquina de desarrollo.
Y registrar la IP de tu máquina tampoco lo resuelve. En la mayoría de las redes estás detrás de NAT: el router comparte una única IP pública entre todos los dispositivos y no sabe a cuál de ellos dirigir una conexión que llega desde afuera. Súmale firewalls que bloquean conexiones entrantes e IPs residenciales dinámicas que cambian sin aviso. El resultado es que no existe ruta desde internet hasta el proceso corriendo en tu puerto 3000.
Para recibir una entrega de verdad necesitas una URL pública con HTTPS. Las estrategias de abajo son, en el fondo, cuatro maneras distintas de conseguir una — cada una con sus propios trade-offs. (Si el concepto de webhook en sí todavía te resulta difuso, vale la pena empezar por la guía definitiva sobre webhooks y volver aquí después.)
Estrategia 1 — Captura antes de programar: endpoints de inspección
Antes de escribir cualquier handler, conviene ver lo que el proveedor realmente envía. Un endpoint de inspección es una URL temporal que acepta cualquier solicitud y muestra todo lo que llegó — método, headers, query string y body formateado — directo en el navegador, sin que levantes ningún servidor. Herramientas como SafeHook, webhook.site y Beeceptor lo hacen en segundos: copias la URL generada, la registras en el proveedor, disparas un evento de prueba y examinas la entrega real.
Este paso parece prescindible, pero evita un error clásico: programar contra la documentación y descubrir en producción que el payload real es distinto. La documentación se atrasa; el payload que llega al endpoint de inspección no miente. Al examinar la captura, presta atención a:
- Headers de firma e identificación — el nombre exacto del header (cada proveedor usa el suyo), el formato del valor y si existe un ID de entrega para deduplicación.
- El content-type real — la mayoría envía
application/json, pero algunos usanapplication/x-www-form-urlencodedo incluso XML. - El envelope del evento — dónde están el tipo de evento, el ID y los datos en sí; algunos proveedores mandan el objeto completo, otros solo una referencia para que consultes la API.
Estrategia 2 — Túneles: expón tu localhost
Un túnel invierte la dirección del problema. En vez de esperar una conexión entrante (que el NAT bloquea), un agente en tu máquina abre una conexión de salida hacia un servidor público — y las conexiones de salida atraviesan NAT y firewall sin drama. El servidor público recibe las solicitudes en la URL que te dio y las despacha por el túnel hasta tu proceso local. Para el proveedor de webhooks, eres un servidor normal en internet.
ngrok
ngrok es el túnel más conocido. Un comando expone el puerto de tu app:
$ ngrok http 3000
Forwarding https://a1b2-203-0-113-7.ngrok-free.app -> http://localhost:3000Registra la URL https://…ngrok-free.app/webhooks en el proveedor y las entregas empiezan a llegar a tu handler local. ngrok además ofrece un panel local en http://127.0.0.1:4040 que registra cada solicitud y permite replay — reenvía la misma entrega sin que tengas que disparar el evento de nuevo en el proveedor. La limitación del plan gratuito: la URL es aleatoria y cambia en cada ejecución, obligándote a re-registrar el webhook en el panel del proveedor cada vez que reinicias el túnel; el dominio fijo es una función paga.
Cloudflare Tunnel
cloudflared cumple el mismo papel, con "quick tunnels" gratuitos que ni siquiera exigen cuenta:
$ cloudflared tunnel --url http://localhost:3000
https://random-words-here.trycloudflare.comCon una cuenta de Cloudflare y un dominio propio puedes crear túneles con nombre y URL estable — resolviendo el problema de la URL cambiante, gratis, al costo de un setup inicial un poco mayor.
Otras opciones
- localtunnel —
npx localtunnel --port 3000, sin instalación; simple, aunque menos estable para uso continuo. - Tailscale Funnel — excelente si tu equipo ya usa Tailscale; expone un servicio de tu tailnet hacia internet con URL estable.
- Dev tunnels de VS Code — el port forwarding integrado en el editor sirve para pruebas rápidas sin herramientas extra.
Estrategia 3 — CLIs oficiales: eventos reales sin exponer nada
Algunos proveedores resuelven el problema de raíz con una CLI que se conecta a tu cuenta y reenvía los eventos a localhost por una conexión de salida — sin URL pública, sin túnel, sin re-registrar nada. La Stripe CLI es el ejemplo canónico:
$ stripe listen --forward-to localhost:3000/webhooks
> Ready! Your webhook signing secret is whsec_xxxxxxxxxxxxx
# en otra terminal, dispara un evento de prueba:
$ stripe trigger payment_intent.succeededFíjate en el detalle importante: stripe listen imprime un signing secret local. Las entregas reenviadas vienen firmadas con él, lo que permite probar la validación de firma de verdad durante el desarrollo. GitHub tiene un equivalente vía extensión de la CLI oficial:
$ gh webhook forward --repo=tu-org/tu-repo --events=push \
--url=http://localhost:3000/webhooksCuando el proveedor ofrece una CLI así, suele ser la mejor opción para el día a día: eventos reales, firma verificable, cero exposición de tu máquina y ninguna URL que re-registrar. La limitación es obvia — no todo proveedor la tiene. Stripe y GitHub sí; la mayoría de las pasarelas de pago latinoamericanas, por ejemplo, no.
Estrategia 4 — Reenvío desde una URL pública estable
Las estrategias anteriores comparten una fricción: la URL registrada en el proveedor apunta a algo efímero (un túnel que muere, una captura temporal). El cuarto enfoque lo invierte: registras en el proveedor una URL pública persistente — un endpoint de captura/relay que es tuyo y no cambia — y es desde ella que los eventos se reenvían hacia donde los necesites: tu localhost durante el desarrollo, un ambiente de staging, o ambos.
Esto es especialmente útil en equipo: todos comparten el mismo endpoint registrado en el proveedor, y cada persona trae los eventos a su propia máquina cuando está trabajando en la integración. Algunas herramientas de inspección ofrecen ese reenvío integrado; también puedes montar tu propio relay minimalista en un VPS — un handler de veinte líneas que recibe, registra y reenvía vía HTTP a un destino configurable.
El cuidado aquí es recordar que el relay se convierte en infraestructura: si se cae, las entregas empiezan a fallar y se acumulan en los reintentos del proveedor. Para desarrollo eso es tolerable; si la misma pieza termina en producción, trátala con el rigor de producción.
Simulando entregas con curl
Nada te impide imitar al proveedor en tu propia máquina. Captura un payload real (con un endpoint de inspección, por ejemplo), guárdalo como fixture y dispáralo contra el handler local:
$ curl -X POST http://localhost:3000/webhooks \
-H "Content-Type: application/json" \
-H "X-Event-Id: evt_prueba_001" \
-d '{"type":"payment.approved","data":{"payment_id":"pay_123","amount":14990}}'Es la forma más rápida de probar ruteo, parsing y reglas de negocio — y la única que corre offline y cabe en una prueba automatizada de CI. La limitación: curl no sabe firmar la solicitud como la firma el proveedor. Para ejercitar la validación de firma necesitas o calcular el HMAC del cuerpo con un secreto de prueba en el propio script, o usar una entrega real vía túnel/CLI. El mecanismo completo de firma está desmenuzado en Seguridad de webhooks: firma HMAC y buenas prácticas.
¿Qué estrategia usar?
No existe un ganador único — existe la herramienta correcta para cada momento del flujo de trabajo:
| Escenario | Mejor estrategia | Por qué |
|---|---|---|
| Explorar el payload antes de programar | Endpoint de inspección | Cero setup; muestra headers y body reales sin escribir código |
| Desarrollar el handler con eventos reales | CLI oficial (si existe) o túnel | Entregas de verdad llegando a tu proceso local, con replay |
| Probar la validación de firma | CLI oficial o túnel | Son los únicos caminos con entregas firmadas de verdad |
| URL estable para el equipo o staging | Reenvío persistente | Se registra una vez en el proveedor; cada destino trae los eventos |
| Pruebas automatizadas / CI | curl + fixtures de payloads reales | Reproducible, offline y rápido; firma el cuerpo tú mismo si hace falta |
Resumen
- Localhost no es alcanzable desde internet por culpa de NAT, firewall e IP dinámica — probar webhooks exige una URL pública HTTPS, y cada estrategia es una manera de obtener una.
- Empieza por un endpoint de inspección para ver el payload real antes de escribir el handler.
- Para desarrollar, prefiere la CLI oficial del proveedor cuando exista; a falta de ella, un túnel (ngrok, cloudflared) resuelve — recordando que la URL gratuita cambia en cada sesión.
- El reenvío desde una URL persistente elimina el re-registro y funciona bien en equipo; curl con fixtures cubre las pruebas automatizadas.
- En cualquier camino: usa datos de sandbox en herramientas de terceros y valida la firma incluso en dev.