How to Test Webhooks Locally – Developer Guide
Webhooks are simple in principle and awkward in development. Providers need a public endpoint, while your application usually runs on localhost behind a firewall. This guide covers the practical setup: exposing a local endpoint safely, replaying payloads, and verifying signatures before the integration reaches production.
1. The Problem: Webhooks Need a Public URL
Webhook providers send HTTP POST requests to a URL you configure. If your app runs on http://localhost:3000, external services cannot reach it. You need a tunnel or a hosted endpoint that forwards traffic to your machine.
2. Option A: ngrok Tunnel
ngrok creates a public URL that forwards to your local server:
# Install ngrok, then run: ngrok http 3000 # Output: https://abc123.ngrok.io -> http://localhost:3000 # Use https://abc123.ngrok.io/webhooks/stripe as your Stripe webhook URL
Configure your webhook provider with the ngrok URL. Each request will hit your local app. Free tier gives you a random subdomain that changes on restart.
3. Option B: Online Webhook Tester
For quick inspection without running a server, use an online webhook tester. These tools give you a unique URL that logs incoming requests. You can:
- See headers, body, and query params
- Verify HMAC signatures (e.g., Stripe
X-Stripe-Signature) - Generate curl commands to replay requests
Use the Webhook Tester — receive webhooks, verify HMAC-SHA256, and export curl for local debugging. All processing happens in your browser; no data is stored.
4. Implementation: Verify Webhook Signatures
Always verify the signature before processing. Use the raw request body (not parsed JSON), as parsing can alter bytes:
// Node.js: Stripe webhook verification
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}5. Conclusion
Use ngrok for end-to-end testing against your local server, or an online webhook tester for quick inspection and HMAC verification. Always verify signatures in production. Use the Webhook Tester for instant, privacy-safe webhook debugging.
6. Replay workflow for faster debugging
The fastest way to debug webhook handling is to replay the exact same payload multiple times while changing only one piece of server logic. This removes provider-side noise and makes failures reproducible.
- Capture a real webhook payload and headers.
- Store it as a fixture in your test directory.
- Replay with
curlagainst your local endpoint after each code change. - Compare status codes, logs, and side effects between runs.
7. Idempotency and retries
Most providers retry webhook deliveries automatically. Your handler should be idempotent so processing the same event twice does not create duplicate records or repeated billing operations.
// Pseudocode
if (hasProcessed(event.id)) {
return 200; // already handled
}
processEvent(event);
markProcessed(event.id);8. Security checklist before production
- Verify signatures: never trust an incoming webhook without HMAC verification.
- Enforce timestamp tolerance: reject very old signed requests to reduce replay risk.
- Log safely: redact tokens and personal data in request logs.
- Use least privilege keys: avoid using broad API keys in webhook processing workers.