Webhook Debugging Checklist for Developers
A systematic checklist for finding missing webhooks, signature failures, timeouts, duplicate events, and payload parsing problems.
Webhook failures cross several boundaries: provider configuration, DNS, TLS, proxies, framework middleware, signature verification, queues, and business logic. Debugging becomes much faster when you isolate those layers instead of changing everything at once.
Use this checklist from the outside in.
1. Prove that the provider sends an event
Start with the provider's delivery dashboard. Find the event ID, destination URL, send time, response status, and retry history.
If the provider offers a “send test event” action, point it temporarily at a free webhook inspector. This answers the first question: can the provider make the expected HTTP request at all?
Compare the configured URL character by character. Common mistakes include:
- an old UUID or environment hostname;
http://instead ofhttps://;- a missing path prefix;
- a trailing slash rule that causes a redirect;
- test-mode events configured against a live-mode endpoint.
2. Inspect the complete request
Do not look only at formatted JSON. Capture:
- HTTP method and full path;
- query parameters, including repeated keys;
Content-TypeandContent-Encoding;- provider event type and event ID headers;
- signature and timestamp headers;
- raw body bytes;
- source IP and payload size.
Send a known request when you need a baseline:
curl -v -X POST \
'https://openwebhook.co/YOUR_UUID/debug?attempt=1' \
-H 'content-type: application/json' \
-H 'x-webhook-id: evt_debug_001' \
--data '{"type":"debug.test","data":{"ready":true}}'
The -v output also shows DNS resolution, TLS negotiation, and response headers from the destination.
3. Check the network boundary
If the request reaches an inspector but not your application, inspect infrastructure before handler code.
DNS and TLS
Verify that DNS resolves to the intended host and the certificate covers the exact hostname:
dig +short api.example.com
curl -Iv https://api.example.com/webhooks
Expired certificates, incomplete certificate chains, and IPv6 records pointing at an unconfigured server can affect webhook providers even when a browser appears to work.
Reverse proxy
Check payload limits, route matching, timeouts, and redirects in nginx, Apache, a load balancer, or an API gateway. A proxy-generated 413 or 502 never reaches your framework.
Do not put a webhook endpoint behind interactive login middleware. Machine-to-machine authentication should use the provider's signature mechanism.
Firewall and allowlists
If you restrict inbound traffic, use the provider's documented IP ranges and keep them updated. Signature verification is still necessary because source IP alone is not proof of authenticity.
4. Verify body parsing and signatures
Signature failures are often byte mismatches. Most providers sign the raw request body. This fails:
const parsed = JSON.parse(rawBody);
const reconstructed = JSON.stringify(parsed);
verifySignature(reconstructed, signature); // bytes may differ
Whitespace, escaped characters, and key order can change. Verify first, then parse:
const rawBody = await readRawBody(request);
verifySignature(rawBody, request.headers["x-signature"]);
const event = JSON.parse(rawBody.toString("utf8"));
Also verify that you use the secret for the correct environment and endpoint. Rotated secrets may require accepting both old and new values briefly.
Check timestamp tolerance using a synchronized server clock. A clock drift of several minutes can make every valid request look like a replay.
5. Respond before doing slow work
Providers usually expect a 2xx within a few seconds. A handler that waits for email, third-party APIs, or a large database transaction may time out even though the work eventually succeeds.
Prefer:
await verifyAndStoreEvent(event);
await queue.publish(event);
response.status(200).send("ok");
Then process the queued event separately. Monitor the HTTP acknowledgement and background job as two different operations.
6. Make retries safe
A timeout or network disconnect can occur after your server processes an event but before the provider receives the response. The provider retries, so delivery is at least once—not exactly once.
Use the provider's event ID as an idempotency key:
INSERT INTO webhook_events (provider_event_id, payload)
VALUES ($1, $2)
ON CONFLICT (provider_event_id) DO NOTHING;
Return success for an event that was already processed. A duplicate is normal delivery behavior, not necessarily an error.
7. Account for ordering
Retries can cause an older event to arrive after a newer one. Avoid assuming receive order is state order. Fetch the latest resource from the provider when correctness depends on current state, or compare event sequence numbers and timestamps.
8. Preserve a safe debugging record
Store event IDs, types, timestamps, response codes, and processing outcomes. Avoid logging full payloads by default because they may contain personal information or credentials.
For temporary inspection, use a tool whose storage behavior you understand. OpenWebhook relays events to the connected browser and keeps history in that browser rather than persisting payloads on the server.
Once you can identify the exact layer where delivery stops, the fix is usually small: a URL, proxy limit, raw-body setting, secret, timeout, or idempotency check.