Splithook

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.

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:

Without re-signing — ngrok replay or curl // Your Stripe handler const event = stripe.webhooks.constructEvent( req.body, req.headers['stripe-signature'], endpointSecret, ); // ⚠ Stripe error: Timestamp outside tolerance zone (300s) // The signature was valid 2 hours ago, not now.
With Splithook re-signing // Same handler — zero changes const event = stripe.webhooks.constructEvent( req.body, req.headers['stripe-signature'], // t=now, v1=fresh_hmac endpointSecret, ); // ✓ Signature valid. Timestamp = 3 seconds ago. // Exact same code path as production.

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:

Stripe / Shopify / GitHub │ ▼ POST to your permanent Splithook URL ┌──────────┐ │ Splithook │ capture · re-sign · fan-out └────┬─────┘ │ ▼ forward with valid HMAC ┌──────────┐ │ ngrok │ tunnel through NAT └────┬─────┘ │ ▼ localhost:3000 your handler verifies normally

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?
No. Splithook doesn't expose your localhost to the internet — it captures, inspects, and re-signs webhooks. If you need a tunnel (and most local dev setups do), you still need ngrok, cloudflared, or a similar tool. They're complementary, not competitive.
What if I don't verify signatures in my handler?
Then you don't need re-signing, and ngrok alone covers your use case. That said, every provider's docs recommend verifying signatures — if you plan to do it eventually, starting with re-signed replays from day one means your dev and prod code paths stay identical.
How does Splithook know how to re-sign for different providers?
Each provider has a dedicated signer that follows their exact spec: Stripe's 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?
It depends on what you're comparing. ngrok's free tier is generous for tunneling. Splithook's free tier gives you 500 events/month with 10 re-signed replays — enough to evaluate the re-signing flow. For paid plans, Splithook ranges from €9 to €49/mo; ngrok from $0 to $20/mo. Most teams that use both spend less than $30/mo total.
Can I replay a webhook captured in Splithook through my ngrok tunnel?
Yes. Set your ngrok tunnel URL (e.g. 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