Webhooks power real-time integrations across payment processors, CI/CD pipelines, communication platforms, and more. These best practices are distilled from building and operating webhook systems at scale.
1. Always Verify Signatures
Never trust a webhook payload without verifying its HMAC signature. Anyone can POST to your endpoint — signature verification proves the request came from the legitimate provider.
const sig = crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
const valid = crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(received))
if (!valid) return res.status(401).end()Never use string equality — it's vulnerable to timing attacks.
2. Return 200 Before Processing
Acknowledge immediately, process asynchronously. Any non-2xx response or timeout triggers a retry. If your processing takes 10 seconds, you'll receive the event again even if you eventually succeeded.
3. Make Handlers Idempotent
Store the event ID and check before processing. Every provider retries failed deliveries — you will receive duplicates.
INSERT INTO processed_events (event_id, processed_at)
VALUES ($1, now())
ON CONFLICT (event_id) DO NOTHING
RETURNING event_id4. Verify Timestamps (Where Available)
Stripe and some providers include a timestamp in the signature header. Reject events older than 5 minutes to prevent replay attacks.
5. Log Everything
Structured logs on every webhook — success and failure — are invaluable for debugging and auditing.
logger.info({
event: 'webhook_received',
provider: 'stripe',
event_id: event.id,
latency_ms: Date.now() - startTime,
status: 'ok'
})6. Use a Dead-Letter Queue for Critical Events
Payment events, subscription changes, and order fulfillments should never silently disappear. When processing fails after all retries, write to a DLQ and alert your team.
7. Monitor Delivery Health
Track per provider: events received per minute, signature verification failure rate, processing error rate, and P99 latency. Alert when error rate exceeds 1%.
8. Test Failure Paths
Use WebhookWhisper to replay events to your staging environment. Test: signature mismatch (should 401), duplicate delivery (should 200 but skip), slow handler, and malformed JSON.
9. Rotate Secrets Carefully
When rotating a webhook secret, use the grace period where both old and new secrets are valid — rotating without a grace period drops in-flight events.
Quick Checklist
- Signature verification with timing-safe comparison
- Return 200 in under 5 seconds
- Idempotent processing with event ID deduplication
- Structured logging per event
- Dead-letter queue for critical events
- Monitoring and alerting on delivery health
- HTTPS-only endpoints