How ngrok Custom Domains Make Webhooks Production-Ready
Changing your webhook URL every time your tunnel restarts is the fastest way to break your Stripe integration. ngrok custom domains fix this. So does something cheaper.
It's a Tuesday morning standup and the Stripe payments just stopped working.
Not because of a bug. Not because of a deploy. Because someone restarted their dev machine last night, the ngrok tunnel URL rotated, and nobody remembered to update the webhook endpoint in the Stripe dashboard. The URL that Stripe has been successfully posting to for the last three weeks — https://a1b2c3d4.ngrok.io/webhooks/stripe — no longer exists. The new tunnel is at https://e5f6g7h8.ngrok.io/webhooks/stripe. Stripe has been retrying for twelve hours.
The fix takes four minutes. The cleanup — identifying which events failed, which customers need retried, which state transitions were missed — takes half a day.
This is the ephemeral URL problem. ngrok custom domains were built to solve it. See the ngrok documentation for the current setup process and G2 reviews for ngrok for real-user experiences. For a broader evaluation framework, read the webhook vendor evaluation checklist.
What ngrok Custom Domains Actually Do
The ephemeral URL problem is structural: a randomly-assigned subdomain that changes on every restart is fine for a one-off test, but a liability when you've configured multiple providers against it.
ngrok custom domains let you attach a domain you own — webhooks.yourdomain.com — to your tunnel. Now the URL your tunnel listens on is yours, persistent, and doesn't change when the tunnel restarts. Configure Stripe, Shopify, and your GitHub Actions webhooks once, against a real domain you control, and they keep working.
The tunnel itself may restart. The domain doesn't change. Stripe doesn't need to know.
This is available on ngrok's paid plans. The free tier gives you a randomly-assigned static subdomain on ngrok.io (also stable across restarts, genuinely useful), but custom domains — your own domain — require a paid subscription.
The mechanics: you add a CNAME from your custom domain to ngrok's routing infrastructure, set the custom domain in your ngrok configuration or dashboard, and ngrok handles the TLS termination and routing. From there, your tunnel starts with ngrok start --domain webhooks.yourdomain.com and the URL is your domain, every time.
For teams that run ngrok in CI/CD — automated test environments that spin up tunnels for integration testing — custom domains make the webhook configuration in test providers static, not per-run. One webhook URL configured in your Stripe test account, reused across every CI build.
It's a clean solution to a real problem, and ngrok deserves credit for identifying that the ephemeral URL was the main thing holding developers back from treating ngrok as a serious part of their webhook workflow.
Webhook Relay: A Different Approach to the Same Problem
Webhook Relay takes a more routing-centric approach to webhook infrastructure than ngrok does.
Where ngrok is fundamentally a tunneling tool with inspection features layered on top, Webhook Relay is more explicitly focused on webhook routing as its core job. The product surface includes buckets (webhook collection endpoints), destinations (where events get forwarded), and bidirectional tunnels — making it more of a routing mesh than a pure tunnel.
The free tier is concrete: 150 webhooks per month. That's not bandwidth — it's events. If you're testing a low-volume integration, 150 events per month covers a lot of development iteration. The paid plans unlock higher volumes, more destinations, and additional features.
Webhook Relay's relaying model is conceptually different from ngrok's tunnel model. With ngrok, you're creating an encrypted tunnel from a public endpoint to your local process. With Webhook Relay, you're configuring a routing topology: events come into a bucket, get routed to one or more destinations, and Webhook Relay handles the HTTP forwarding. This makes it more natural for "send the same webhook to multiple destinations" scenarios — fan-out delivery, routing the same event to both a dev environment and a logging system simultaneously.
If your use case involves routing webhook events to multiple places, or if you want a more explicit routing configuration rather than a per-process tunnel, Webhook Relay's model fits that thinking better. It's less "expose my localhost" and more "configure the routing layer for my webhooks."
The 150/month free tier is worth knowing: if you're doing light-volume testing and don't need persistent history or replay, it's a real option with no credit card required.
HookTunnel: Stable URLs Without Custom Domain Configuration
HookTunnel made permanent URLs the default for every user from day one — no DNS configuration, no CNAME, no paid plan required. See HookTunnel features and pricing for details. For incident recovery patterns, read webhook revenue leakage to see how replay prevents lost business from missed events.
Every hook URL in HookTunnel (hooks.hooktunnel.com/h/your-unique-id) is permanent by default. Not stable-for-this-plan. Not stable-if-you-configure-a-custom-domain. Permanent, with no configuration required, on the free tier.
The URL you generate in thirty seconds today is the same URL that works next month, after a laptop wipe, after you switch to a new development machine. There's no tunnel to restart. There's no ngrok process to keep running. The capture URL exists as a first-class object on HookTunnel's infrastructure — it's an endpoint, not a tunnel session.
This distinction matters for the Tuesday morning problem. ngrok custom domains solve the ephemeral URL issue for teams running active tunnels. HookTunnel's permanent URLs solve it even when you're not running anything — because the URL doesn't depend on a process you're running locally.
The practical workflow: generate a free hook URL, configure it in Stripe's dashboard, and walk away. Events that arrive while you're offline are captured and stored. When you come back, open the dashboard and see everything that arrived — full payload, headers, timing. No tunnel, no client, no process to babysit.
The paid tier ($19/month Pro) adds 30-day history retention and replay — the ability to take any captured event and replay it to any target URL, including localhost:
hooktunnel replay --hook abc123xyz --event evt_1Nkl --to http://localhost:3000/webhooks/stripe
Replay to staging. Replay to a new handler you're testing. Replay the entire batch of events from a time window when your service was down. This is the production incident recovery workflow that custom domains alone can't provide — stable URL plus persistent history plus directed replay.
The Pricing Comparison
| | ngrok | Webhook Relay | HookTunnel | |---|---|---|---| | Stable URL on free tier | ngrok.io assigned subdomain | Yes (buckets are stable) | Yes (permanent by default) | | Custom domain | Paid plans only | Paid plans | N/A — hooktunnel.com domain included | | Free tier events | Unlimited (tunnel-based) | 150 webhooks/month | Unlimited capture | | History persistence | Current session only | Not a core feature | 24h free, 30d Pro | | Replay | One-click in Traffic Inspector | Manual forward | Any target URL (Pro) | | Routing to multiple targets | Not built-in | Yes (buckets → destinations) | Not a core feature | | Local process required | Yes (tunnel must run) | Yes (relay agent) | No | | Pro pricing | Paid plans (custom domains) | Paid plans | $19/month |
Which Tool Solves Your Actual Problem
If your problem is "I need a stable URL for local development and I want to use my own domain": ngrok custom domains are the right answer. You're already running ngrok, you want professional-grade tunnel configuration, and you want to own the domain. The custom domain feature on paid plans delivers that exactly.
If your problem is "I need to route the same webhook to multiple destinations, or configure a routing topology for my webhooks": Webhook Relay's bucket-and-destination model fits this thinking better than ngrok's process-per-tunnel model. The 150 events/month free tier is genuinely usable for light testing.
If your problem is "I want a stable capture URL I configure once and never think about again, with history of everything that arrived and the ability to replay events when something goes wrong": that's HookTunnel's native design. No tunnel. No custom domain configuration. No process to keep running. Permanent URL, captured history, Pro replay.
The Bottom Line
ngrok's custom domains are clever engineering for a real pain point. The ephemeral URL problem was genuinely hurting developer workflows, and fixed, stable tunnel URLs — whether on ngrok.io or your own domain — make webhook integrations dramatically more maintainable.
Webhook Relay approached the same space with a routing-layer mindset, and the result is a product that does fan-out delivery and explicit routing topology better than a pure tunnel tool.
HookTunnel started from the premise that "stable URL" should be the floor, not a paid feature — and then built history and replay on top of that foundation. If you want stable URLs with 30-day history and replay built in from day one, that's three things for $19.
ngrok's custom domains are excellent engineering for a real problem. But a newer option gives you the stable URL, persistent capture, and replay without any DNS configuration. Worth a look before you commit to the tunnel workflow.
Generate a permanent hook URL → Free. No install. Configure it in Stripe and never think about it again.
Stop guessing. Start proving.
Generate a webhook URL in one click. No signup required.
Get started free →