Open inspector →
developer guide

Forward Webhooks to Localhost with the OpenWebhook CLI

Install the public openwebhook CLI and forward an anonymous or permanent webhook URL to 127.0.0.1 without a tunnel or signup.

A provider needs a public HTTPS URL. Your handler is often still on localhost. A tunnel can expose the whole machine. The OpenWebhook CLI does something narrower: it watches one URL and replays each request only to 127.0.0.1.

The first run does not need an account. A Pro token is only required for a permanent slug, 1-to-many HTTPS forwarding, or a custom HTTP response.

Install and listen

npm install --global openwebhook
openwebhook listen --port 3001

The CLI mints an anonymous device, prints a temporary URL on hooks.openwebhook.co, and forwards each webhook to 127.0.0.1:3001. That URL expires after 24 hours. The device token in ~/.openwebhook/config.json does not.

Keep a permanent slug

Create a custom slug such as billing so the public URL stays stable:

https://hooks.openwebhook.co/billing

Generate a token once in the developer dashboard. It is shown a single time. You can revoke it later. OPENWEBHOOK_TOKEN can supply the token without writing it to disk.

openwebhook auth owh_live_YOUR_TOKEN
openwebhook listen billing --port 3001

auth associates the current device with the Pro account. listen opens an authenticated SSE stream for that slug. Each inbound webhook is forwarded with the original method, path, query and body. Host and Content-Length are recalculated. Hop-by-hop, proxy and X-Forwarded-* headers are dropped. The destination host is always 127.0.0.1; the port comes only from --port.

Start a local receiver, then the CLI:

# terminal 1 — your app, or any HTTP echo on 127.0.0.1
npx --yes http-echo-server 3001

# terminal 2
openwebhook listen --port 3001

Send a test to the URL the CLI printed, or to a Pro slug:

curl -X POST \
  'https://hooks.openwebhook.co/billing/stripe' \
  -H 'content-type: application/json' \
  --data '{"type":"checkout.session.completed"}'

The CLI should print POST /stripe -> 200 (or whatever your local process returned). While listen is connected, that same status and body are what the public URL returns to the provider. A custom slug response applies only when no CLI forwarder is connected. If localhost does not answer in 30 seconds the public URL returns 504.

Why this is safer than a public tunnel

  • Only one URL is observed, not your whole laptop.
  • The local target cannot be chosen by the webhook payload.
  • Absolute URLs, protocol-relative paths and .. tricks in the request path are rejected before forwarding.
  • Anonymous pull goes from the CLI to OpenWebhook. The server never calls a URL you did not register with a Pro account.

Use a tunnel when the provider must hit your TLS terminator or a path that is not 127.0.0.1. Use the CLI when you want the public URL to stay on OpenWebhook and the handler to stay private.

MCP without a token

openwebhook mcp starts the same way: no account for url.inspect and an anonymous watch.start. Permanent slugs still need Pro. Listed on Smithery. See Test webhooks in Cursor and Claude.

Related guides