Back to Blog
guides6 min readApril 11, 2026

How to Forward Webhooks to Localhost Without ngrok

Every webhook developer hits the same wall: your handler is on localhost, but the provider needs a public HTTPS URL. ngrok is the usual answer — but it's a CLI to install, manage, and keep running. Here's how to forward webhooks to localhost without any of that.

A
Abinash B
April 11, 2026

The Problem: Your Handler Is Local, the Provider Isn't

You're building a Stripe integration. Your Express server runs at localhost:3000. Stripe needs to POST payment events to a public HTTPS URL. Your laptop is not publicly reachable from Stripe's servers.

The standard solution for years has been ngrok — a tunnel that exposes a port on your local machine to the internet. It works, but it has real costs:

  • CLI binary to install and update — one more tool in the chain
  • Persistent terminal process — the tunnel dies when you close the terminal
  • Rotating URLs on free plan — every restart gives you a new URL, meaning you have to update Stripe, GitHub, Shopify, etc. every time
  • Exposes your whole local server — ngrok tunnels the entire port, not just the webhook path
  • No event persistence — if your server was down when an event arrived, it's gone

There's an alternative that doesn't have any of these problems.


How Cloud-Side Forwarding Works

Instead of exposing your local machine to the internet, a cloud-side relay works like this:

  1. A permanent public HTTPS URL lives in the cloud (e.g. https://webhookwhisper.com/hook/abc123)
  2. You register this URL with your provider — Stripe, GitHub, Shopify — once, permanently
  3. You configure a forwarding rule: when an event arrives, relay it to http://localhost:3000/webhooks/stripe
  4. The cloud server makes the outbound request to your localhost — this works because the cloud is making a request to you, not the other way around

Your machine receives the incoming HTTP POST just like it would from the provider directly. All original headers are preserved — including signature headers like Stripe-Signature (see Stripe webhook testing guide) and X-Hub-Signature-256 (see GitHub webhook testing guide) — so signature verification works identically.

The cloud URL is permanent. You register it once and never touch it again. No tunnel to keep running, no URL rotation, no persistent CLI process.


Step-by-Step: Forward Webhooks to Localhost With WebhookWhisper

Step 1 — Create a free public endpoint

Go to webhookwhisper.com and click Get my free webhook URL. You get a unique HTTPS URL like https://webhookwhisper.com/hook/abc123xyz instantly — no account, no email, no CLI.

If you want a permanent URL that doesn't expire (recommended for any integration you're actively developing), sign up for a free account and create a named endpoint under the Dashboard.

Step 2 — Register the URL with your provider

Paste your WebhookWhisper URL wherever your provider expects a webhook endpoint — see the full provider directory for 35+ providers:

  • Stripe: Developers → Webhooks → Add endpoint
  • GitHub: Settings → Webhooks → Add webhook
  • Shopify: Settings → Notifications → Webhooks → Create webhook
  • Any other provider: wherever you'd normally paste a webhook URL

This is the only time you touch the provider settings. The URL never changes.

Step 3 — Set a forwarding rule

  1. In your WebhookWhisper endpoint, open the Forwarding tab
  2. Click Add rule
  3. Enter your local handler URL as the target: http://localhost:3000/webhooks/stripe
  4. Click Save

That's it. Now every event that arrives at your WebhookWhisper URL is automatically relayed to your local server.

Step 4 — Run your local server and test

Start your local server as usual (npm run dev, python manage.py runserver, whatever your stack uses). Fire a test event from your provider's dashboard. Watch it arrive in the WebhookWhisper inspector and simultaneously hit your local handler.

The delivery log shows the HTTP status your handler returned, the response body, and round-trip time — so you can see instantly whether your handler is processing the event correctly.


What Happens When Your Local Server Is Down

This is where the approach beats ngrok significantly. With ngrok, if your tunnel is down when an event arrives, the event is lost. The provider's retry logic eventually retries, but that might be minutes or hours later.

With WebhookWhisper:

  • The event is received and stored regardless of whether your local server is running
  • The forwarding attempt is logged as failed with the error details
  • WebhookWhisper retries automatically using exponential backoff
  • You can also manually replay any past event from the dashboard once your server is back up

This means restarting your local server mid-development doesn't cause missed events. You close the terminal, fix a bug, restart the server, and replay the last event — all from the browser.


Signature Verification Still Works

A common concern: if the request is being proxied through WebhookWhisper, will the signature header still be valid?

Yes — because WebhookWhisper forwards all original request headers intact. The Stripe-Signature header that Stripe included when the event was sent to your WebhookWhisper URL is passed through verbatim to your local server. The raw body is also preserved byte-for-byte. So stripe.webhooks.constructEvent(rawBody, sig, secret) works exactly as it does in production.

Same for GitHub's X-Hub-Signature-256 and Shopify's X-Shopify-Hmac-Sha256 — they're all forwarded intact.


ngrok vs WebhookWhisper — Side-by-Side

Featurengrok (free)WebhookWhisper
Installation requiredYes — binary + auth tokenNo — browser only
Persistent terminal processYesNo
Static URLNo (rotates each restart)Yes
Event stored if server is downNoYes
Automatic retry on failureNoYes
Event replayNoYes
Delivery log with HTTP statusNoYes
Exposes entire local portYesNo — only the target URL
Multiple forwarding targetsNoYes
Free tier forwardingYes (rotating URL)Yes (50 events, static URL on paid)

Common Configurations

Forward to a specific path

Set the forwarding target to the exact handler path your app expects:

http://localhost:3000/webhooks/stripe
http://localhost:8080/api/github/events
http://localhost:5000/shopify/orders

Forward to multiple targets simultaneously

Add multiple forwarding rules on the same endpoint. Useful for testing locally while also hitting a staging environment:

Target 1: http://localhost:3000/webhooks/stripe    (your local dev server)
Target 2: https://staging.yourapp.com/webhooks/stripe  (staging)

Docker or WSL networks

If your handler runs in Docker or WSL2, localhost may not resolve correctly from the forwarding server. Use the host machine's IP or Docker's host gateway instead:

http://host.docker.internal:3000/webhooks/stripe  (Docker on Mac/Windows)
http://172.17.0.1:3000/webhooks/stripe            (Docker on Linux)
http://192.168.1.5:3000/webhooks/stripe           (your machine's LAN IP)


Related Guides

Summary

You don't need ngrok to forward webhooks to localhost. A cloud-side relay like WebhookWhisper gives you a permanent public HTTPS URL, relays events to your local handler with all headers intact, retries on failure, and logs every delivery attempt — with no binary to install and no terminal to keep running.

The forwarding-to-localhost workflow with WebhookWhisper:

  1. Create a free endpoint at webhookwhisper.com
  2. Register it with your provider (Stripe, GitHub, Shopify, etc.) — once
  3. Add a forwarding rule: target = http://localhost:3000/your-path
  4. Run your local server and receive events in real time

Create your free account and have your first webhook forwarding to localhost in under two minutes.

#webhooks#localhost#forwarding#ngrok#tutorial

Ready to test your webhooks?

Get a free HTTPS endpoint in under 5 seconds — no signup required.

Create Free Account
Forward Webhooks to Localhost Without ngrok (2026) | WebhookWhisper