Free and open source · MIT

Webhook Tester

Webhook Tester is a self-hosted webhook tester: a request bin you run yourself. Start one container, create a URL, point Stripe, GitHub, your payment provider or your own service at it, and every request shows up live with its method, path, query, headers, client IP and body. Payloads stay on your machine.

Webhook Tester showing a captured POST request with its headers and JSON body

What Webhook Tester does

It answers one question: what is this service actually sending me? Then it gets out of the way. No account, no build step, no external database.

Feature What it means
Unique URLs Every bin gets an unguessable URL. Any method and any sub-path is captured, so /hook/<id>/orders/created?x=1 lands in the same list.
Live view New requests appear over Server-Sent Events, without a page refresh.
Full request detail Method, path, query parameters, headers, client IP and body. JSON is pretty-printed; binary bodies are shown as base64.
Custom responses Choose the status code, content type and body sent back, so you can watch a provider retry a 500 or disable a webhook on 410.
Persistent Requests live in SQLite on a Docker volume and survive restarts.
Self-cleaning Per-bin request limit, body size limit and automatic expiry of idle URLs.

Quick start

One command. The image is published for linux/amd64 and linux/arm64.

Terminal
docker run -d -p 8080:8080 -v webhook-tester-data:/data \
  ghcr.io/webhooker-eu/webhook-tester

Open http://localhost:8080, click Create webhook URL, and send something to it:

Terminal
curl -X POST 'http://localhost:8080/hook/<your-id>' \
  -H 'Content-Type: application/json' \
  -d '{"hello": "world"}'

Prefer to build the image yourself:

Terminal
git clone https://github.com/webhooker-eu/webhook-tester.git
cd webhook-tester
docker compose up -d --build

Receiving events from a real provider

Stripe, GitHub, Shopify and the rest can only call a public address, so a container on your laptop is not reachable as it stands. Either run Webhook Tester on a small server behind TLS, or open a tunnel (Cloudflare Tunnel, ngrok or similar) and give the provider the public https://…/hook/<id> URL. The tool does not open a tunnel for you; it only captures what arrives.

While you are there, keep an eye on the signature headers. They are stored unchanged, which makes this the fastest way to see whether a provider sends Stripe-Signature, X-Hub-Signature-256 or something of its own, and what the raw body looked like when it was signed.

Configuration

Everything is an environment variable: pass it with docker run -e NAME=value, or put it in a .env file next to docker-compose.yml.

Variable Default What it controls
MAX_BODY_BYTES 1048576 Largest stored body. Bigger ones are truncated and flagged.
MAX_REQUESTS_PER_BIN 200 Requests kept per URL; the oldest are dropped first.
BIN_TTL_HOURS 168 A URL expires after this long without traffic. 0 disables expiry.
FORWARDED_ALLOW_IPS 127.0.0.1 Your reverse proxy's address, so client IPs come from X-Forwarded-For.
DATABASE_PATH /data/webhook-tester.db SQLite file inside the container.

HTTP API

Everything the UI does is available as JSON, with interactive docs at /docs. That makes the tester usable from a test suite: create a bin, trigger your code, read the captured request back.

Method Path Description
POST /api/bins Create a new capture URL.
PUT /api/bins/{id}/response Set the status, content type and body returned to senders.
GET /api/bins/{id}/requests List captured requests, newest first.
GET /api/bins/{id}/stream Server-Sent Events stream of new requests.
ANY /hook/{id} The capture endpoint itself, including any sub-path.

When a request bin is not enough

A request bin is a mirror: it shows you traffic and forgets it. That is the right tool while you are reading payloads and writing a handler. It becomes the wrong one the moment an event has to reach your code reliably. A bin does not verify the provider's signature, does not retry when your service is down, and drops its oldest requests once the per-bin limit is hit.

Webhooker is the other half of that: one EU-hosted ingest URL per source, signatures checked before a payload is accepted, every event stored durably, six delivery attempts over about five hours, a dead-letter queue and one-click replay. The quick start takes four steps, and the free plan covers 10,000 events a month.

Frequently asked questions

How do I see what a webhook actually sends?

Create a URL in Webhook Tester and paste it into the provider's webhook settings. Every request that arrives is listed with its method, path, query parameters, headers, client IP and body; JSON is pretty-printed and the raw bytes are one click away. Nothing is parsed or rewritten on the way in, so what you see is what the provider sent.

Is there an open-source alternative to webhook.site or RequestBin?

Yes, that is what this is. It runs as a single Docker container with a SQLite file, has no accounts and keeps every payload on your own server, which matters when webhooks carry customer data, tokens or signature headers you cannot paste into a public website. RequestBin itself now lives inside Pipedream and asks you to sign up.

Can I receive webhooks on localhost?

Only through something public. Providers can reach a public address, so either run Webhook Tester on a server or expose your local container with a tunnel such as Cloudflare Tunnel or ngrok and give the provider the public /hook/<id> URL. Webhook Tester does not open a tunnel by itself.

Can the URL answer with an error, so I can test retries?

Yes. Open the "Replies to the sender" panel and pick any status from 200 to 599 with a content type and body. Answer 500 or 503 to watch how the provider backs off and retries, or 410 to see whether it disables the webhook after a permanent failure.

Does it verify webhook signatures?

No. It stores the body byte for byte and shows headers such as Stripe-Signature or X-Hub-Signature-256 unchanged, so you can compare them with what your own code computes. To send correctly signed test events to your app, use Webhook Mock Sender; to have signatures checked automatically before a payload is accepted, that is what the Webhooker gateway does.

Is it safe to put on the public internet?

There is no authentication: anyone who can open the instance can create URLs, and anyone who knows a URL can read its requests. The IDs are random and unguessable, but for a public deployment put the UI and /api/* behind your reverse proxy's auth and leave only /hook/* open.

Further reading

More open-source tools

Everything we publish lives in the webhooker-eu GitHub organisation, and the full list with a short description of each is on the open-source tools page.

When the testing is done, the events still have to arrive.

Webhooker is the hosted side of the same problem: one EU-hosted ingest URL for any provider, signatures verified before a payload is accepted, every event stored, retried and replayable. Free for 10,000 events a month.