Webhook Design
Also Known As
webhook
HTTP callback
event notification
webhook security
TL;DR
Best practices for reliable webhooks — HMAC signature verification, idempotency, delivery retry with exponential backoff, and handling slow consumers with queues.
Explanation
Webhooks push events to consumer endpoints via HTTP POST. Design considerations: Signature verification (HMAC-SHA256 of the payload with a shared secret — prevents forged payloads), idempotency (the same event may be delivered multiple times — consumers must be idempotent), delivery guarantees (retry with exponential backoff on non-2xx responses), timeouts (consumer must respond within 5-30 seconds — queue slow processing), event ordering (not guaranteed — include timestamps and sequence numbers), and dead letter handling (failed deliveries after retries need alerting).
Common Misconception
✗ Webhooks are fire-and-forget — providers retry on failure, so consumers must be idempotent; receiving the same event twice must produce the same result, not duplicate side effects.
Why It Matters
A webhook consumer that processes a payment event twice (due to retry) charges a customer twice — idempotency using the event ID prevents duplicate processing.
Common Mistakes
- Not verifying HMAC signature — any HTTP client can forge webhook payloads.
- Non-idempotent consumers — retries cause duplicate charges, emails, or database records.
- Slow processing in webhook handler — slow handlers cause timeouts and trigger retries.
- No dead letter handling — failed webhooks silently lost means missed events.
Code Examples
✗ Vulnerable
// No signature verification, not idempotent:
$payload = file_get_contents('php://input');
$event = json_decode($payload);
// No verification — anyone can POST fake events!
if ($event->type === 'payment.completed') {
$this->processPayment($event->data); // Not idempotent — retries = double charge!
}
✓ Fixed
// Secure webhook handler:
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
// Verify HMAC signature:
$expected = 'sha256=' . hash_hmac('sha256', $payload, WEBHOOK_SECRET);
if (!hash_equals($expected, $signature)) {
http_response_code(401); exit;
}
$event = json_decode($payload);
// Idempotency — skip if already processed:
if ($this->events->isProcessed($event->id)) {
http_response_code(200); exit; // Acknowledge but skip
}
// Respond 200 immediately, process asynchronously:
http_response_code(200);
ProcessWebhookJob::dispatch($event->id)->afterResponse();
$this->events->markProcessed($event->id);
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
16 Mar 2026
Edited
22 Mar 2026
Views
30
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
Amazonbot 9
ChatGPT 9
Perplexity 5
Google 3
Ahrefs 2
Also referenced
How they use it
crawler 26
crawler_json 2
Related categories
⚡
DEV INTEL
Tools & Severity
🟠 High
⚙ Fix effort: Medium
⚡ Quick Fix
Validate webhook signatures (HMAC-SHA256), respond with 200 immediately and process asynchronously via queue, implement idempotency via event ID — these three practices prevent the common webhook failures
📦 Applies To
PHP 5.0+
web
api
🔗 Prerequisites
🔍 Detection Hints
Webhook endpoint with no signature validation; processing webhook inline taking >5 seconds causing retries; no idempotency check for duplicate delivery
Auto-detectable:
✓ Yes
semgrep
postman
⚠ Related Problems
🤖 AI Agent
Confidence: High
False Positives: Medium
✗ Manual fix
Fix: Medium
Context: File
Tests: Update
CWE-345
CWE-20