The Problem with Local Webhook Development
You're building a Stripe integration. You write your webhook handler, start your local server on localhost:3000, and… now what? Stripe can't reach your machine. You need a public URL.
The traditional solutions all have drawbacks:
ngrok / Tunnels
- + Quick to set up
- + Works immediately
- - URL changes every restart (free tier)
- - Must update provider webhook URL each time
- - Doesn't capture when tunnel is down
- - No replay capability
Webhook Relay (HookRelay)
- + Permanent URL — set once, never change
- + Captures webhooks even when offline
- + One-click replay
- + Real-time payload inspection
- + Signature verification built in
- + Free tier available
The Webhook Relay Approach
Instead of tunneling traffic directly to your machine, a webhook relay captures webhooks in the cloud and lets you forward them to localhost on demand. Here's the workflow:
Step 1: Create an Endpoint
Sign up for HookRelay and create a new endpoint. You get a unique, permanent URL like:
https://hookrelay.fly.dev/h/abc123xyz
Step 2: Configure Your Provider
Point your webhook provider (Stripe, GitHub, Shopify, etc.) to your HookRelay endpoint URL. This is a one-time setup — the URL never changes.
Step 3: Webhooks Are Captured Automatically
Every webhook sent to your endpoint is captured with full details: headers, body, query parameters, timestamp, and source IP. You can inspect them in the HookRelay dashboard in real time via WebSocket.
Step 4: Forward to Localhost
When you're actively developing, enable forwarding to send captured webhooks to your local server:
# Webhooks sent to your HookRelay endpoint
# are automatically forwarded to localhost:3000/webhook
Step 5: Replay as Needed
Found a bug? Fixed it? Replay the same webhook with one click. No need to trigger a new event from the provider. You can replay the same webhook dozens of times as you iterate on your handler code.
Real-World Example: Stripe Checkout
Let's walk through a complete example of developing a Stripe Checkout integration using HookRelay.
Set Up Your Handler
// server.js
const express = require('express');
const app = express();
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
const event = JSON.parse(req.body);
switch (event.type) {
case 'checkout.session.completed':
const session = event.data.object;
console.log('Payment successful for:', session.customer_email);
// Fulfill the order
break;
case 'customer.subscription.updated':
console.log('Subscription updated:', event.data.object.id);
break;
default:
console.log('Unhandled event:', event.type);
}
res.json({ received: true });
});
app.listen(3000, () => console.log('Listening on :3000'));
Configure Stripe Webhook
In the Stripe Dashboard, set your webhook endpoint to your HookRelay URL. Select the events you care about (e.g., checkout.session.completed).
Trigger a Test Event
Complete a test checkout in Stripe. The webhook is captured by HookRelay. You can see the full payload in the dashboard immediately:
{
"id": "evt_1abc123",
"type": "checkout.session.completed",
"data": {
"object": {
"id": "cs_test_xyz",
"customer_email": "buyer@example.com",
"amount_total": 2900,
"currency": "usd",
"payment_status": "paid"
}
}
}
Debug and Iterate
Your handler has a bug? Fix it, restart your server, and replay the captured webhook. The same payload, the same headers — but this time it hits your updated code. Repeat until it works perfectly.
No more "trigger another test payment." Every webhook you receive is saved and replayable. This alone saves hours during development.
Why Not Just Use ngrok?
ngrok is great for quick demos, but it has real limitations for webhook development:
- URL instability: Free tier URLs change every time you restart ngrok. You have to update your webhook URL in every provider dashboard.
- No capture when offline: Close your laptop and ngrok goes down. Webhooks sent during that time are lost forever.
- No replay: ngrok forwards requests in real time. If your handler crashes, you have to trigger a new event from the provider.
- Rate limits: Free ngrok has connection limits that can cause webhook delivery failures during development.
- Cost: ngrok's paid plans that solve these problems start at $10/mo — and still don't include replay.
Other Services and Alternatives
If you're evaluating webhook testing tools, here's a quick comparison:
- webhook.site / RequestBin: Good for one-off inspection, but no forwarding to localhost and no replay. URLs are temporary.
- Stripe CLI: Stripe-specific. Great if you only use Stripe, but doesn't work with GitHub, Shopify, Twilio, or any other provider.
- LocalTunnel: Open source ngrok alternative, but even less reliable. URLs are random and the service goes down frequently.
- HookRelay: Purpose-built for webhook development. Permanent URLs, capture when offline, one-click replay, real-time inspection. Works with any provider.
Start testing webhooks the easy way
Get a permanent webhook URL in 30 seconds. Free tier included.
Create Free Account