# Webhook Tester

> Self-hosted webhook tester in one Docker container: a unique URL, a live request feed, full headers and body, custom replies. MIT licensed, no account.

Source: https://webhooker.eu/tools/webhook-tester
Last updated: 2026-09-18

[Open-source tools](https://webhooker.eu/tools) / 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.

- Python · HTML
- MIT licensed
- Docker image
- No account

[View on GitHub](https://github.com/webhooker-eu/webhook-tester) Quick start

## 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`.

```bash
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:

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

Prefer to build the image yourself:

```bash
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](https://webhooker.eu/) 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](https://webhooker.eu/docs/quickstart) 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

- [Webhook.site and RequestBin alternatives compared](https://webhooker.eu/blog/webhook-site-alternatives)
- [How webhooks work: the request, header by header](https://webhooker.eu/blog/how-do-webhooks-work)
- [ngrok alternatives for getting webhooks onto your laptop](https://webhooker.eu/blog/ngrok-alternatives-for-webhooks)
- [The Webhooker ingest URL: limits and response codes](https://webhooker.eu/docs/ingest)

## More open-source tools

[Discord Webhook Tester  →   Send a test message to a Discord webhook, and build the embed visually.](https://webhooker.eu/tools/discord-webhook-tester)[Webhook Mock Sender  →   Send signed Stripe, GitHub and Shopify events to your own endpoint.](https://webhooker.eu/tools/webhook-mock-sender)

Everything we publish lives in the [webhooker-eu GitHub organisation](https://github.com/webhooker-eu), and the full list with a short description of each is on the [open-source tools page](https://webhooker.eu/tools).

## 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.

[Start for free](https://app.webhooker.eu/register) [Read the docs](https://webhooker.eu/docs)
