Webhook development is one of the most painful corners of modern back-end programming. Stripe sends a checkout.session.completed. GitHub fires a pull_request. Slack pings your slash command. None of them can reach localhost:3000 on your laptop, so the standard flow is deploy to staging, trigger the event, tail the logs, find the bug, push again — a 3-5 minute loop per iteration that shreds focus.
TL;DR: install the Nodetonet PC agent, create an HTTPS tunnel to port 3000, paste the stable public URL into Stripe or GitHub once, and from that moment on every webhook hits your local breakpoint within milliseconds — no redeploy, no staging lag.
Why the deploy loop costs you more than time
Every round-trip to staging is a context switch. By the time the logs show up you have already started reading something else. Worse, you cannot set breakpoints in staging; you would have to attach a remote debugger or litter the code with console.log calls. And reproducing intermittent webhook bugs — idempotency failures, out-of-order retries, race conditions between your handler and the provider's retry schedule — is nearly impossible because you cannot replay a specific signed delivery against your local code at will.
A tunnel collapses the entire loop into "save file, fire test event, hit your breakpoint." That is the development experience webhook providers expect you to have, and it is what this page is about.
How Nodetonet fits the webhook development workflow
Nodetonet's HTTP tunnel feature exposes any local port on your dev machine as a persistent public HTTPS URL. You can pick a stable subdomain like stripe-dev.yourname.nodetonet.com, or bring your own domain. The URL does not change when your laptop sleeps, does not change when you restart the tunnel agent, and does not change between coding sessions. Configure Stripe, GitHub and Slack once and never touch the webhook config again.
- Automatic HTTPS. A Let's Encrypt certificate is issued within seconds of creating the tunnel. Stripe and GitHub both require HTTPS for webhook endpoints — this just works without any certificate management on your part. Read more about wildcard SSL certificates and auto-renewal.
- Custom domains. If you want webhooks landing at
hooks.yourcompany.dev, add a CNAME record and Nodetonet issues the certificate automatically. No paywall, no per-domain fee. - Audit log for every request. Every HTTP request that reaches the tunnel is logged — source IP, host, path, byte count, response status, latency — for 30 days. Accessible from the panel and via REST API. This is the practical substitute for ngrok's one-click Replay while we build our own UI equivalent.
- IP allow-listing. Lock the tunnel so only Stripe's published webhook IP ranges can hit it. One allow-list entry and everyone else gets a 403. See our guide on IP allow and deny lists.
- Pay nothing while idle. Stop the tunnel at the end of the day. The URL is reserved to your account and unchanged when you restart tomorrow. Idle tunnels cost nothing under Nodetonet's prepaid credit model.
Step-by-step setup
- Sign up at /auth/register and top up a small amount of prepaid credit.
- Download and install the PC agent from /download. It runs in the system tray on Windows, macOS or Linux and idles at under 30 MB of RAM.
- In the panel under HTTP Tunnels, create a new tunnel. Source machine: your dev PC. Local port: 3000 (or whichever port your dev server uses). Public hostname: choose a free subdomain or your own domain.
- Copy the generated HTTPS URL. Paste it into the Stripe Dashboard under Developers → Webhooks, or into GitHub → Settings → Webhooks, or Slack's slash-command request URL.
- Fire a test event from the provider's UI. Within 200 ms your IDE hits the breakpoint. Iterate freely.
Code example — verifying a Stripe webhook signature
The handler below runs on localhost:3000. Expose it through the tunnel and paste the URL into Stripe. The critical detail is using express.raw on this one route so Stripe's HMAC signature check works against the original bytes:
// server.js
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET);
const app = express();
const SECRET = process.env.STRIPE_WEBHOOK_SECRET;
app.post('/webhook',
express.raw({ type: 'application/json' }),
(req, res) => {
let event;
try {
event = stripe.webhooks.constructEvent(
req.body, req.headers['stripe-signature'], SECRET
);
} catch (err) {
return res.status(400).send('Bad signature');
}
console.log('Event received:', event.type, event.data.object.id);
// Set a breakpoint here and iterate without redeploying
res.json({ received: true });
}
);
app.listen(3000, () => console.log('Listening on :3000'));
In the Stripe Dashboard, set the endpoint to https://stripe-dev.yourname.nodetonet.com/webhook. Click "Send test webhook." Your process logs the event in under 200 ms. Change the handler, save, and the hot-reload means the next test event hits your updated code instantly — without any redeploy.
Nodetonet vs ngrok for webhook development
Both tools tunnel local ports to public HTTPS URLs. The table below covers the differences that matter for a typical webhook development workflow. We have described ngrok's publicly documented features accurately and honestly — this is not a one-sided comparison.
| Feature | Nodetonet | ngrok Free | ngrok Personal ($8/mo) |
|---|---|---|---|
| Stable public URL | Yes — subdomain or custom domain | Random URL, changes every session | Yes — reserved domain |
| Automatic HTTPS / TLS cert | Yes, Let's Encrypt, wildcard | Yes | Yes |
| Custom domain (own CNAME) | Yes, no extra fee | No | Yes |
| Request audit log | 30-day log, panel + REST API | Limited session-only inspector | Extended inspector |
| One-click Replay | Not yet (roadmap) | Yes (web inspector) | Yes (web inspector) |
| IP allow-listing | Yes — built-in, per-tunnel | No | Yes (IP policy) |
| Idle cost | Zero — prepaid credit, no subscription | Free tier (limited) | $8/month regardless of use |
| Multiple local ports at once | Yes — one tunnel per port, billed per use | 1 tunnel on free | Yes |
The honest summary: ngrok's one-click Replay is the feature Nodetonet does not have yet. If replaying specific signed webhook deliveries from a GUI is a daily part of your debugging workflow, ngrok Personal is currently the stronger choice for that specific task. For stable URLs, custom domains, IP restriction and cost efficiency, Nodetonet competes well — especially if you already use Nodetonet for mobile proxies or rotating proxies and want one platform for everything. See our full Nodetonet vs ngrok comparison for more.
Using the audit log as a manual Replay
While the one-click UI is on the roadmap, the 30-day audit log in the panel already gives you the raw material to reproduce any delivery. Every logged request includes the full headers and payload. From there you can re-fire it against your local server with a single curl command:
# Fetch the raw body of a specific tunnel request
curl -s -H "Authorization: Bearer $NTN_KEY" "https://nodetonet.com/api/v1/proxies/prx_abc123/requests/req_xyz789" | jq -r '.data.body' | curl -X POST http://localhost:3000/webhook -H "Content-Type: application/json" -H "Stripe-Signature: $(cat .stripe-sig)" -d @-
It takes a few more keystrokes than a button click, but it works and it means you never lose access to a specific problematic delivery. Check the REST API first call guide and the audit log guide for the full syntax.
Locking down the tunnel to your webhook provider
Leaving a public HTTPS URL wide open is fine during initial development, but before long-running setup you should restrict access to known source IPs. Stripe publishes its webhook delivery IP ranges; GitHub provides them via https://api.github.com/meta. Paste those CIDR blocks into the tunnel's IP allow-list in the panel. Any request from an unknown IP gets a 403 before it ever reaches your dev machine. This mirrors the approach described in our IP allow and deny lists post.
Testing GitHub and Slack webhooks
The same tunnel serves every webhook provider. A GitHub repository webhook pointed at https://ci.yourname.nodetonet.com/github fires events to your local CI runner the instant code is pushed. A Slack slash command URL pointed at https://slack.yourname.nodetonet.com/commands/myapp lets you iterate on the response payload in real time, in your local environment, with full access to your local database and file system — none of which is available in a typical staging environment.
For monitoring tunnel health once set up, see monitoring your tunnel health. For programmatic tunnel management from scripts, programmatic tunnel creation in Python shows a reusable client.
When this is not the right fit
- You need one-click Replay daily. The feature is on the Nodetonet public roadmap but not yet available. ngrok's web inspector handles this better today.
- You debug webhooks once a quarter. ngrok's free tier gives a working URL (random, no custom domain) with no payment required. For occasional one-off debugging sessions, that is perfectly sufficient.
- You need a WebSocket traffic inspector UI. Nodetonet tunnels WebSocket connections correctly, but the visual inspector for WS frames is also on the roadmap and not yet live.
Get started
Ready to stop deploying just to test a webhook? Create a free account, install the PC agent from /download, and have your first tunnel running in under five minutes. Browse HTTP tunnels for the full feature set, or check our proxy checker to verify any tunnel is live before pointing a payment provider at it.