Webhook not arriving: the debugging checklist
A webhook that "doesn't arrive" is almost always one of these: the provider doesn't send it, the request never reaches your server, or your server receives it and rejects it. The fastest method is to split the problem in two with a capture URL.
1. Split the problem in two
Temporarily replace your endpoint URL with a Tracehook capture URL, then trigger the event. If the request shows up in the inspector, the provider does send it: the problem is on your side (network, server, code). If nothing shows up, the problem is in the provider's configuration.
That single step saves hours of reading logs in the wrong place. It also shows the exact request, raw headers and body, as your code should receive it.
2. Nothing arrives: check the provider
When even the capture URL receives nothing, look at the configuration:
- Subscribed events: is the event you trigger in the list selected on the endpoint?
- Environment: at Stripe, test-mode and live-mode endpoints are separate. A test event is never sent to a live endpoint.
- Endpoint state: some providers disable an endpoint after a run of failures.
- Delivery log: Stripe lists attempts on the endpoint page, GitHub in the webhook's "Recent Deliveries" tab. A delivery missing from the log was never attempted.
3. The capture receives it, your server doesn't: the network
The provider calls your URL from the Internet, so it must be reachable from outside:
- No localhost or private address: 127.0.0.1, 192.168.x.x or an internal hostname can't be reached by the provider.
- DNS: does the domain resolve to the right machine from outside?
- TLS: an expired or self-signed certificate, or an incomplete chain, fails the call before it reaches your code.
- Firewall, WAF or bot protection: a rule that blocks non-browser requests blocks webhooks too.
curl -i -X POST https://api.example.com/webhooks/stripe \
-H 'Content-Type: application/json' \
-d '{"ping":true}'4. Your server receives it but the provider sees a failure
For the provider, only a fast 2xx response counts as a successful delivery. GitHub expects a response within 10 seconds; Stripe recommends returning 2xx before any long processing. Respond first, process later (message queue, background job).
Watch out for redirects: an http URL that redirects to https, or a trailing slash added by the framework, returns a 301 or 308 that most providers don't follow. Configure the exact final URL.
5. The request arrives but the body is empty or unreadable
Compare the Content-Type header received in Tracehook with what your code expects. GitHub, for example, can send application/json or application/x-www-form-urlencoded (the JSON then sits in a payload field) depending on the webhook setting.
Check the size too: a proxy or framework that caps bodies at a few hundred kilobytes truncates or rejects large events.
6. The request is rejected by signature verification
This is the most common cause once everything else works: the body was parsed and re-serialized before computing the HMAC, or the secret isn't this endpoint's. The dedicated guide covers verification and its pitfalls.