{"post":{"slug":"webhook-signing-explained-2026","title":"Webhook Signing: Verifying What Arrived at Your Endpoint","excerpt":"Your webhook endpoint is a public URL anyone can post to. Without signature verification, anyone can tell your system that a payment succeeded.","category":"code","date":"January 4, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">A</span> webhook endpoint is a URL on the public internet that accepts POST requests and acts on them. If it does not verify who sent the request, anyone who discovers the URL can tell your system whatever they like — that a payment cleared, that a subscription upgraded, that an account was verified.\n\nSignature verification is what makes the endpoint trustworthy, and it is skipped surprisingly often because the integration works without it.\n\n## What changed in 2026\n\n- **Signing became near-universal among providers.** Sending signed webhooks is now standard practice for services of any size.\n- **Verification helpers spread.** Provider SDKs shipping verification functions removed most of the implementation risk.\n- **Raw-body guidance became prominent.** The framework-middleware failure got documented well enough to become common knowledge.\n- **Asynchronous processing became the recommended default.** Acknowledge fast, process out of band.\n\n## Verify before anything else\n\nThe order matters. Verification must happen before you parse, before you act, and before you log the contents anywhere.\n\nThe mechanics are HMAC signing: the provider computes a signature over the request content using a shared secret, you recompute and compare — see [HMAC request signing](/blog/hmac-request-signing-2026) for the underlying scheme.\n\nThe failure specific to webhooks is the **raw body problem**. Most frameworks parse a JSON body into an object before your handler runs. Signatures cover exact bytes, so re-serialising that object produces different bytes — different key order, different whitespace, different number formatting — and verification fails.\n\nIt fails *intermittently*, depending on payload contents, which makes it maddening to debug. The fix is to capture the raw body before parsing middleware touches it, usually by configuring the framework to preserve it on the webhook route specifically.\n\nUse timing-safe comparison, and reject requests whose timestamp is outside a short window so a captured request cannot be replayed indefinitely.\n\n## Acknowledge fast, process later\n\nThe common architectural mistake: doing the work inside the webhook handler.\n\nProviders expect a prompt response and treat a slow one as a failure. Slow handlers cause timeouts, timeouts cause retries, and retries cause duplicate processing of work that was actually succeeding — a failure mode that gets worse under load, exactly when handlers are slowest.\n\nThe pattern that works: verify the signature, persist the raw event, return 200, and process asynchronously.\n\n| Step | Where |\n| --- | --- |\n| Verify signature | In the handler |\n| Persist the event | In the handler |\n| Return 200 | Immediately |\n| Parse and act | Background worker |\n| Retry on failure | Your own retry logic |\n\nThat decouples your processing reliability from the provider's timeout. A processing failure becomes your retry rather than theirs, which you control.\n\n## Duplicates are normal\n\nWebhook delivery is at-least-once. Providers retry on any response they consider a failure, including timeouts where you actually succeeded. Network conditions produce duplicates independently.\n\nSo duplicate delivery is not an error condition — it is expected behaviour, and handlers must be idempotent by design.\n\nThe standard approach uses the event identifier the provider supplies: record processed identifiers, and skip anything already seen. That turns a duplicate into a no-op — see [idempotency explained](/blog/idempotency-explained-2026).\n\nOrdering is the related trap: webhooks may arrive out of order, so an event describing an older state can arrive after a newer one. Check the event's timestamp or version against what you have recorded rather than applying blindly, or a delayed retry will overwrite fresher data.\n\n## Do not trust the payload as truth\n\nA verified webhook proves the provider sent it. It does not mean the payload is a complete or current picture.\n\nFor anything consequential — a payment amount, a subscription state — the safer pattern is to treat the webhook as a *notification that something changed* and then fetch the current state from the provider's API. That eliminates ordering problems and stale payloads in one step.\n\nIt costs an API call per event, and for financial or access-control decisions that is cheap insurance against acting on an out-of-order or partial payload.\n\n## Common mistakes\n\n- **No verification.** The endpoint accepts input from anyone.\n- **Verifying re-serialised body.** Intermittent failures.\n- **Processing synchronously.** Timeouts and duplicate deliveries.\n- **Assuming exactly-once delivery.** Retries are normal.\n- **Applying events without checking order.** Stale data overwrites fresh.\n- **Returning a non-2xx on a business-logic failure.** Triggers provider retries for something retrying will not fix.\n- **Logging payloads before verification.** Unverified attacker-controlled content in your logs.\n\n## FAQ\n\n**What status should I return on a bad signature?**\nA 4xx — it is a client error and should not be retried. Returning 5xx makes the provider retry something that will never succeed.\n\n**What if processing fails after I returned 200?**\nYour problem to retry, which is the point of persisting the event first. Do not rely on the provider redelivering something you acknowledged.\n\n**Should I allow-list source IP addresses?**\nA useful additional layer where the provider publishes ranges, and not a substitute for signatures — addresses change and can be spoofed at some layers.\n\n**How do I test webhook handling?**\nMost providers offer test events and a way to replay them. Verify that your handler is genuinely idempotent by delivering the same event twice.\n\n## Where to go next\n\nFor the signing scheme, read [HMAC request signing](/blog/hmac-request-signing-2026). For duplicate handling, [idempotency explained](/blog/idempotency-explained-2026), and for the delivery guarantees involved, [exactly-once delivery](/blog/exactly-once-delivery-2026).","hue":308,"metaTitle":"Webhook Signing: Verifying What Arrived at Your Endpoint","metaDescription":"Why webhook endpoints need signature verification, how raw body handling breaks it, and why duplicate deliveries must be handled by design.","keywords":["webhook signing","webhook verification","webhook security","duplicate webhook delivery","raw body verification","async webhook processing","at least once delivery","api integration 2026"],"tldr":["**An unverified webhook endpoint accepts input from anyone** — including instructions your system will act on.","**Verify against the raw body before parsing** — framework middleware that consumes it breaks verification.","**Return 200 quickly and process asynchronously** — slow handlers cause retries and duplicates.","**Handle duplicates by design** — providers retry, and at-least-once delivery means repeats are normal.","**Skip:** trusting the payload's contents over your own records. Treat a webhook as a notification, not as truth."],"readTime":"4 min read","wordCount":897},"related":[{"slug":"webhook-retry-and-idempotency-design-2026","title":"Webhook Retry and Idempotency Design in 2026","excerpt":"Webhooks are delivered at-least-once, which means every consumer must handle retries and duplicates safely. This guide covers event ID dedup, fast acknowledgment, and idempotent handler design for 2026.","category":"code","date":"August 10, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":304},{"slug":"webhooks-deep-dive-explained-2026","title":"Webhooks deep dive in 2026: delivery, security, and reliability","excerpt":"Webhooks are HTTP callbacks that push events to your server instead of making you poll. This 2026 guide covers delivery guarantees, signature verification, and the failure modes teams hit in production.","category":"code","date":"April 21, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":314},{"slug":"what-is-a-message-queue-2026","title":"What Is a Message Queue? A Plain Guide for 2026","excerpt":"A message queue lets one part of your system hand work to another without waiting. Here is what it does, when you need one, and when to skip it.","category":"code","date":"May 2, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":128}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"point-in-time-recovery-2026","title":"Point-in-Time Recovery: Undoing the DELETE You Regret","excerpt":"Replication copies your mistakes instantly. Point-in-time recovery is the only mechanism that lets you go back to just before someone ran a DELETE without a WHERE clause.","category":"code","date":"January 4, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":304},"older":{"slug":"best-ai-image-generators-2026","title":"Best AI image generators in 2026: Midjourney vs Flux vs DALL-E vs Ideogram vs Firefly","excerpt":"We compared the five image generators worth paying for in 2026 on quality, control, pricing, and the commercial-license fine print most reviews skip. Plus the free options that are surprisingly good.","category":"ai","date":"January 3, 2026","author":"ByteLedger Team","readTime":"12 min read","hue":195}}