What is webhook forwarding? How to forward webhooks

Webhook forwarding is receiving webhooks on a public URL and relaying each request on to another destination: a server on localhost, a machine on a private network with no public IP, or several endpoints at once. The provider only sees the public URL. The forwarder delivers each request onward with its body and headers intact, so your endpoint handles it as if the provider had called it directly.

relay forward --bucket my-app http://localhost:3000/webhooks
# prints a permanent public input URL, e.g. https://my.webhookrelay.com/v1/webhooks/2a1b…
# give that URL to the provider; each webhook is delivered to localhost:3000/webhooks

Why forward webhooks

  • Local development. Providers can't reach localhost:3000. Forwarding lets you test real Stripe, GitHub or Shopify webhooks against the code on your laptop.
  • Private and on-prem servers. Jenkins, Home Assistant, internal APIs and Kubernetes services often have no public IP, and opening a firewall port isn't an option.
  • One URL, many consumers. A provider allows one webhook URL, but production, staging and analytics all need the events. See webhook fan-out.
  • Reliability. The forwarder can store each webhook and retry delivery long after the provider would have given up.
  • A stable URL. Move the destination between servers, clouds or environments without touching the provider's settings.

How webhook forwarding works

To a public destination (server-side):

  1. The provider sends a POST to the forwarder's public URL.
  2. The forwarder accepts and stores it, and responds to the provider.
  3. It sends the same request to each configured destination URL, retrying on failure.

To localhost or a private network (agent-based):

  1. A small agent runs inside your network and opens an outbound connection to the forwarding service. No inbound port is opened.
  2. The provider sends a webhook to the public URL.
  3. The service pushes the request down the agent's connection.
  4. The agent makes the HTTP request to your local destination, for example http://localhost:3000/webhooks, and reports the response.
Provider ──HTTPS──▶ public URL ──▶ forwarding service
                                        │  (outbound connection opened by the agent)
                                        ▼
                     your network:  relay agent ──▶ http://localhost:3000/webhooks

Webhook forwarding vs a tunnel

Webhook forwardingTunnel
What's exposedOnly requests sent to the webhook URLThe whole local port
If your machine is offlineRequests are stored and can be retried or replayedRequests fail
Many destinations from one URLYesNo
Logs, retries, transformsBuilt inUsually basic inspection
Best forWebhooksSharing a web app, APIs, browser access

Use a tunnel when people or apps need interactive access to your server. Use forwarding when you're receiving webhooks.

Setting it up with Webhook Relay

Install the relay CLI, run relay login, then:

relay forward --bucket my-app http://localhost:3000/webhooks

The command creates a bucket with a permanent public input URL and starts the agent. Re-running it with the same bucket name keeps the same URL, so you configure the provider once. Several developers can forward the same bucket and every connected agent receives every webhook. The full walkthrough, including Docker and troubleshooting, is in receiving webhooks on localhost.

On top of plain forwarding, each destination can have:

The webhook forwarding overview covers all of these, and forwarding to internal servers covers production use behind a firewall.

Common mistakes

  • Forwarding to the wrong path. The destination URL needs the full route, http://localhost:3000/webhooks, not just the port.
  • A forwarder that modifies the body. Re-encoded JSON breaks signature verification. Forward raw bytes unless you mean to transform.
  • Using a random URL that changes. Every restart then means updating the provider's settings, and events sent to the old URL are lost.
  • Looking in the wrong place for errors. Unless the forwarder is set to pass your endpoint's response back, the provider sees the forwarder's response, not your endpoint's. Check the forwarder's logs to see whether your code failed. Webhook Relay can return a dynamic or static response when a provider needs one.

Frequently asked questions

What is webhook forwarding?

Webhook forwarding is receiving webhooks on one public URL and relaying each request to another destination, such as a server on localhost, a machine on a private network, or several endpoints at once. The provider only ever sees the public URL; the forwarder delivers the request onward with its body and headers intact.

How do I forward webhooks to localhost?

Use a forwarding tool that gives you a public URL and runs a small agent on your machine. With Webhook Relay, install the relay CLI, log in, and run relay forward --bucket my-app http://localhost:3000/webhooks. Give the printed URL to the provider, and each webhook is delivered to your local server over the agent's outbound connection.

What is the difference between webhook forwarding and a tunnel?

A tunnel exposes a whole local port to the internet, so anything can reach your server through the public hostname, and the request fails if the tunnel is down. Webhook forwarding only relays webhook requests sent to a specific URL, can store and retry them while your machine is offline, and can deliver one request to many destinations.

Does webhook forwarding break signature verification?

It shouldn't. A forwarder that passes the raw body and headers through unchanged lets your endpoint verify the provider's signature as usual. Only deliberate transforms that change the body invalidate the original signature.

Do I need to open a firewall port to forward webhooks?

Not with an agent-based forwarder. The agent on your machine opens an outbound connection to the forwarding service, and webhooks are delivered over that connection, so no inbound port, public IP or router change is needed.