ToolingApril 18, 20266 min read

Why ngrok Isn't Enough: Testing and Operating Webhooks Past localhost

ngrok is great for local dev. It's not a production strategy. Here is what you need past the tunnel, for staging, production, and the gray area in between.

TUNNEL · THE URL IS BOUND TO A LAPTOPStripetunnelrotates, expireslaptopasleepevent goneRELAY · THE URL IS BOUND TO NOTHINGStriperelaypersist + 200prodlaptopreplayable later
ngrok is excellent right up to the point where the URL has to outlive the laptop.

ngrok is the tool I reach for 30 seconds into integrating any webhook provider. You spin up ngrok http 3000, paste the public URL into Stripe's dashboard, and within a minute you're hitting breakpoints in your local handler. It's an obvious win for dev loop speed.

The trouble is that ngrok keeps getting reached for in contexts it was never meant for: staging environments, "temporary" production tunnels, debugging production outages. That's where teams burn hours. This post is about where ngrok's job ends and what you actually need past that.

TL;DR

  • ngrok is the right tool for local dev and nothing beats it there. Keep using it.
  • It's the wrong tool for anything that needs to survive a closed laptop: staging, production, long debug sessions, audit, outage recovery
  • Past local dev you want persistence, replay, and multi-destination fan-out, which is what a webhook relay gives you
  • The working pattern is ngrok and a relay, not one or the other

What ngrok is great at

Local signing-secret testing, for one. The only way to exercise signature verification is to actually receive a signed request, and ngrok lets you.

Testing failure paths, for another. Return a 500 from your handler, watch Stripe's dashboard mark the delivery as failed, verify your retry logic.

It's also the fastest way to see the exact headers a provider sends, including the weird ones. And for throwaway demos, when you want to show a teammate the integration working, you ngrok it for 20 minutes and move on.

For these, it's perfect. Keep using it.

Where ngrok stops being enough

Staging environments. You don't want staging to depend on a tunnel on someone's laptop, and a tunnel dies when the laptop sleeps. Staging becomes "whoever set up the webhook last," which is a shared dependency on one engineer's uptime.

Production. Obviously.

Long-running debug sessions. If you need 3 days of webhook capture to reproduce a bug that fires once a day, ngrok's free tier times out and rotates URLs. The paid tier is fine, but now you're paying for a tunnel to do logging, which is the wrong shape.

Handing off to support or non-dev teammates. They need a URL that works without "let me start ngrok first."

Auditability. If a compliance team asks "show me every webhook event we received in Q1," ngrok has nothing for you.

Recovery from outages. If your server was down and Stripe's retry window expired, ngrok doesn't have the events. They're gone.

What you need past the tunnel

Once you move past "just let me POST to localhost," the actual shape of the problem is:

  1. A stable public URL that doesn't depend on any laptop
  2. Persistence, so every inbound event is stored and readable later
  3. Delivery to multiple targets: localhost for dev, staging for a teammate, production for real traffic, all from one inbound URL
  4. Replay, as in "send that event from yesterday to my handler again"
  5. Retry and alerting, so a flaky endpoint gets retried and you hear about it

This is a different category of tool. Call it a webhook relay or a webhook proxy depending on who you ask.

Where webhook relays fit

A relay sits between the provider and your endpoints. Providers send to one stable URL (in.anyhook.net/you/stripe), and the relay handles delivery, retries, logging, and replay.

TUNNEL · THE URL IS BOUND TO A LAPTOPStripetunnelrotates, expireslaptopasleepevent goneRELAY · THE URL IS BOUND TO NOTHINGStriperelaypersist + 200prodlaptopreplayable later

For local dev, you add a localhost destination to the app alongside production, and every inbound event fans out to both. You get the ngrok experience, live events on your laptop, without the tunnel rot. When your laptop sleeps, only the localhost destination fails. Production keeps humming, events keep getting logged, and the failed localhost deliveries sit there to replay whenever you're back.

For staging, you give the staging environment its own URL that gets a subset of production traffic. For debugging, you filter the event log by status or destination and replay whatever you're trying to reproduce.

ngrok + AnyHook is the working pattern

The combination that works in practice: ngrok for the first 30 minutes of a new integration, because it's the fastest way to "works on my machine," and AnyHook for everything after that, meaning persistent logging, fan-out to multiple environments, replay, retries, and alerts.

AnyHook gives you an inbound URL that's always up, forwards to any number of destinations (prod, staging, localhost, wherever), and keeps a complete log with replay. You keep ngrok for local dev because nothing beats it there, and you stop using ngrok for anything you'd be sad to lose.

The cost of "we'll just ngrok it"

The teams that burn the most time on webhook issues are almost always the ones that treated ngrok as their production strategy:

  • Staging breaks every time someone closes their laptop
  • Production outages leave permanent data gaps
  • "What was the payload on that event?" has no answer two days later
  • New engineers can't reproduce bugs because there's no event history to replay from

None of that is fixed by paying for ngrok Pro. A faster tunnel to the same laptop is still a tunnel to a laptop.

Takeaway

What you want past local dev is a webhook layer that lives somewhere other than a developer's machine: persistent, replayable, and able to fan out to any number of destinations. That's the shift that makes webhooks stop being a weekly fire.

If you're still deciding whether to receive pushes at all, Webhooks vs Polling argues the choice on failure mode rather than efficiency. And once you are receiving them on serverless, Receiving Webhooks on Vercel Without Losing Them covers the parts that break in production but never locally.

All postsApril 18, 2026 · 6 min

Stop losing webhooks.

Change one URL. Get retries, event log, and one-click replay.