Local webhook testing tool. Like webhook.site, but on your machine.
Send webhooks to localhost instead of a third-party service. Inspect request details, headers, and body in a real-time dashboard. Optionally forward the incoming webhook to your application.
- Public HTTPS Tunnel -- built-in support for HTTPS tunnel via Tailscale or Cloudflare
- Real-time -- requests appear instantly via Server-Sent Events, no refresh needed
- All HTTP methods -- GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
- Request inspection -- method, URL, headers, query parameters, body
- Request forwarding -- optionally forward incoming requests to your own application
- JSON formatting -- auto-detects and pretty-prints JSON with syntax highlighting
- Zero config -- no database, no build step, no accounts
- Terminal logging -- see requests in your terminal without opening the dashboard
npx @cmer/localhookThen send requests to http://localhost:3000/any-path (or the public URL) and watch them appear in the dashboard.
# Default port 3000
npx @cmer/localhook
# Custom port
npx @cmer/localhook --port 8080
# Expose via Tailscale Funnel
npx @cmer/localhook --tailscale
# Expose via Cloudflare Quick Tunnel (no account required)
npx @cmer/localhook --cloudflare
# Custom port with Tailscale
npx @cmer/localhook --port 8080 --tailscale
# Allow dashboard access from the public URL (password-protected)
npx @cmer/localhook --tailscale --allow-remote-access --password mysecret
# Forward webhooks to your local app
npx @cmer/localhook --forward-to http://localhost:4444
# Forward with a base path
npx @cmer/localhook --forward-to http://localhost:4444/api/webhooks| Flag | Short | Description |
|---|---|---|
--port <port> |
-p |
Port to listen on (default: 3000) |
--tailscale |
Start Tailscale Funnel for a public HTTPS URL | |
--cloudflare |
Start Cloudflare Quick Tunnel for a public HTTPS URL | |
--allow-remote-access |
Allow dashboard/API access from non-localhost (e.g. via tunnel) | |
--password <value> |
Require HTTP Basic Auth for remote dashboard/API access (localhost is never challenged) | |
--data-file <path> |
Path to data file (default: ~/.localhook/data.json) |
|
--forward-to <url> |
Forward incoming webhooks to a local app (preserves method, path, headers, body) | |
--help |
-h |
Show help |
Open http://localhost:3000 in your browser to see the dashboard.
Any HTTP request to any path (except /) gets captured:
curl -X POST http://localhost:3000/webhook \
-H "Content-Type: application/json" \
-d '{"event": "user.created", "user_id": "123"}'Use --forward-to to forward incoming webhooks to your local application while still capturing them in the dashboard:
npx @cmer/localhook --forward-to http://localhost:4444Every captured webhook is forwarded synchronously — your app receives the original HTTP method, path, query string, headers (including signature headers like x-stripe-signature), and body. The caller (e.g. Stripe) receives your app's actual response, so retries work correctly on 5xx errors.
You can also specify a base path that gets prepended to the webhook path:
# Webhook to /events → forwarded to http://localhost:4444/api/webhooks/events
npx @cmer/localhook --forward-to http://localhost:4444/api/webhooksThe dashboard shows forwarding results: status code, duration, and response body for each request. If the target is unreachable, the caller receives a 502 and the error is displayed in the dashboard.
Combine with a tunnel for end-to-end webhook testing:
npx @cmer/localhook --cloudflare --forward-to http://localhost:4444If you need to receive webhooks from external services like Stripe, GitHub, or Shopify, they need a public URL to send requests to.
My favorite. Use the --tailscale flag to automatically start Tailscale Funnel alongside LocalHook:
npx @cmer/localhook --tailscaleThis gives you a public HTTPS URL like https://myhost.tail1234.ts.net. Use that as your webhook URL in Stripe, GitHub, etc.
Requires Tailscale to be installed with Funnel enabled.
Use the --cloudflare flag to start a Cloudflare Quick Tunnel — no account or login required:
npx @cmer/localhook --cloudflareThis gives you a public HTTPS URL like https://random-words.trycloudflare.com. Use that as your webhook URL in Stripe, GitHub, etc.
Requires cloudflared to be installed. The URL changes each time you restart.
Note:
--tailscaleand--cloudflareare mutually exclusive — use one or the other.
Note: The dashboard is always restricted to localhost by default. Requests through reverse proxies (Tailscale Funnel, ngrok, Cloudflare Tunnel, etc.) are automatically detected and blocked from accessing the dashboard. Use
--allow-remote-accessto override this, and--passwordto require authentication for remote access.
You can also use any other tunneling service manually, such as ngrok.
LocalHook has a REST API for programmatic access to captured webhooks. See API.md for full documentation with example requests.
LocalHook runs a single Express server. GET / serves the dashboard. Every other request is captured as a webhook and broadcast to the dashboard via SSE.
Data is stored in ~/.localhook/data.json by default (max 500 entries). Use --data-file to override.
MIT
