Compare
Splithook vs ngrok for webhook testing:
two tools, two jobs, one workflow.
If you develop against webhooks from Stripe, GitHub, Shopify, or any provider that signs payloads with HMAC, you've probably used ngrok. You may have also hit the moment where ngrok alone isn't enough — a replayed payload whose signature expired, a staging environment that can't receive hooks, or a Stripe account that can only send to one URL while three services need the event.
The short version: ngrok gives your localhost a public URL. Splithook captures webhooks, re-signs them with a valid HMAC, and fans them out to any number of destinations — including a localhost behind ngrok. They solve different halves of the webhook development problem, and they work well together.
This page breaks down where each tool shines, where their scopes diverge, and how to combine them for a webhook workflow where signatures always pass and events always arrive.
When ngrok is all you need
ngrok is an excellent tool for what it does — and for many webhook workflows, it's genuinely all you need. Don't over-engineer a stack if a tunnel solves your problem.
- You're building a chatbot or OAuth callback. The provider doesn't sign payloads, or signature verification isn't part of your flow. You just need a public URL that routes to your local dev server.
- You're doing a live demo. A stable HTTPS URL that maps to your laptop, with a web inspector to show traffic in real time — ngrok was built for this.
- Your webhook handler doesn't verify signatures yet. If your code doesn't call
stripe.webhooks.constructEvent()or the equivalent, a tunnel is the only thing standing between you and live events on localhost. ngrok is the fastest path. - You need TCP/gRPC tunneling. ngrok supports protocols beyond HTTP. Splithook is HTTP-only.
If any of these describe your situation, ngrok is the right choice. No need to add another tool.
When Splithook becomes necessary
The gap appears when your webhook handler does verify signatures — which is every production-grade Stripe, GitHub, or Shopify integration. At that point, three specific scenarios break the ngrok-only workflow.
1. Replay with valid signatures
You captured a payment_intent.succeeded event two hours ago. You need to replay it to test an edge case. With ngrok alone, the original Stripe signature has expired — the timestamp is outside the 5-minute tolerance window. Your handler rejects it, or you skip verification in dev, which means you're not testing the code that runs in production.
With Splithook, the replay re-computes the HMAC with a fresh timestamp using your real Stripe secret. The handler receives a payload it can verify normally:
2. Fan-out to multiple destinations
Your Stripe account fires checkout.session.completed to one URL. But your payment service, your notification service, and your analytics pipeline all need that event. With ngrok, you'd configure three separate webhook endpoints in Stripe — or route everything through a manual proxy.
Splithook captures the event once and fans it out to all three destinations simultaneously, each with its own re-signed HMAC. One webhook URL in Stripe, three services receiving verified events.
3. Persistent capture and async debugging
ngrok streams events in real time — if your tunnel is down, the event is lost. If you want to inspect a payload from yesterday, there's no history. Splithook stores every webhook with headers and body for up to 90 days. You can inspect, search, filter by event type, and replay at any time — even if your dev machine was off when the event fired.
Feature-by-feature comparison
| Capability | Splithook | ngrok |
|---|---|---|
| Tunnel localhost to public URL | Via ngrok/cloudflared | ✓ Native |
| TCP / gRPC / non-HTTP tunneling | ✗ | ✓ |
| Stable public capture URL | ✓ Permanent | Paid (static domains) |
| HMAC re-signing on replay | ✓ Per-provider | ✗ |
| Provider-aware signature schemes | ✓ Stripe v1, GitHub sha256, Shopify base64, Twilio SHA-1, Svix, Generic | ✗ |
| Fan-out (1 event → N destinations) | ✓ Up to 10 | ✗ |
| Persistent event history & search | ✓ Up to 90 days | Session-only inspector |
| One-click replay to localhost | ✓ | ✗ |
| Side-by-side payload diff | ✓ | ✗ |
| Filter expressions (event type, JSONPath) | ✓ | ✗ |
| Custom request/response transforms | ✗ | ✓ Edge modules |
| IP restriction & OAuth on tunnel | ✗ | ✓ |
Can you use both together?
Yes — and it's the setup we recommend for most webhook-heavy projects. The architecture is simple:
Why this works well: Splithook gives you a stable URL you configure once in your provider dashboard — it never changes when you restart a tunnel. ngrok gives you the NAT traversal to reach your dev machine. Splithook handles re-signing and fan-out; ngrok handles the last mile. Neither tool tries to do the other's job.
You can also use Splithook with cloudflared, tailscale funnel, or any other tunnel. Splithook forwards to any HTTP URL — if your tunnel exposes localhost on a public URL, point a Splithook destination at it and you're done.
Frequently asked questions
Does Splithook replace ngrok?
What if I don't verify signatures in my handler?
How does Splithook know how to re-sign for different providers?
t=timestamp,v1=hmac format, GitHub's X-Hub-Signature-256 hex digest, Shopify's base64 HMAC, Twilio's SHA-1 URL-aware scheme. You store your signing secret (encrypted at rest with libsodium), and Splithook picks the right signer based on the detected provider.
Is Splithook more expensive than ngrok?
Can I replay a webhook captured in Splithook through my ngrok tunnel?
https://abc123.ngrok-free.app/webhooks/stripe) as a Splithook destination. When you click replay, Splithook re-signs the payload and POSTs it to that URL. Your local handler receives it with a valid signature, through the ngrok tunnel, as if Stripe had just sent it.
Try re-signing on your next Stripe replay.
10 re-signed replays on the free plan. See the difference in one debug session.
Keep verification on — start free