— Infrastructure

Webhook signing-key rotation: verify exact bytes across two providers

Design webhook signing-key rotation around provider-specific raw-byte verification, documented overlap windows and separate replay and deduplication tests.

4 min readUpdated 2026-11-05By AptixLabs studio
Raw delivery: Preserve exact bytes; Provider verifier: Exact signed format; Key window: Current or retiring by policy; Durable processing: Replay and effect rulesAptixLabs · 2026-11-05

Original explanatory figure of a synthetic or proposed design; not a deployed product screenshot. View full-size diagram

Webhook key rotation starts with the provider's exact signed-message definition. Preserve raw request bytes, use the provider's supported verifier, accept retiring keys only within the documented overlap policy and separate signature acceptance from event deduplication. Two providers can both use HMAC-SHA256 while signing different byte sequences and carrying different replay metadata.

Raw bytes are part of the contract

Read the body once at the framework boundary in the form required by the verifier before parsing JSON. Equivalent JSON objects can have different whitespace, escaping and key order, so parsing and reserializing changes the signed bytes. Keep verification before business processing and use a constant-time comparison when implementing the documented low-level scheme. Prefer an official library when available rather than reconstructing a provider's envelope from memory.

GitHub documents its X-Hub-Signature-256 HMAC over the payload with a sha256= prefix. Stripe's webhook guidance uses its own signature header and timestamped verification contract. These are distinct provider protocols; do not add a generic timestamp/body concatenation rule to GitHub or omit Stripe's documented timestamp handling.

GitHub: exact webhook payload signature verification

Stripe: signature errors and endpoint operations

Compare two harmless synthetic providers

For a fixture, provider A signs the exact body bytes. Provider B signs an explicitly documented timestamp delimiter and body. Use local disposable secrets and a body such as a fictional record update. Verify the untouched bytes, then change one harmless whitespace character. Both schemes should reject an altered signed message unless the fixture re-signs it. This demonstrates byte sensitivity without delivering any external request.

# Illustrative synthetic schemes, not drop-in provider verifiers.
message_a = raw_body
message_b = timestamp.encode('ascii') + b'.' + raw_body
# Compute each HMAC with its disposable fixture key.
# Use the real provider SDK for real endpoint verification.

These local schemes have not been executed here. They are intentionally labeled synthetic so the example cannot be mistaken for exact vendor integration code. A production fixture must use the selected provider's actual header parsing, supported algorithms, encoding and clock tolerance. Record SDK version and endpoint mode; test-mode and live-mode signing secrets are separate configurations.

Proposed harmless verification cases
CaseExpected boundarySeparate assertion
Current key, unchanged bodyAccept by provider schemeSchema validation
Retiring key in overlapProvider-policy outcomeRecord key version
Body whitespace alteredSignature refusalNo business processing
Old key after retirementRejectNo new acceptance window

Rotation has a timeline and an owner

Inventory endpoints and consumers, provision the replacement in approved secret storage and establish when the provider will use it. If the provider supports overlapping active and retiring keys, verify both only during that documented interval. If it does not, plan the cutover and expected retry behavior explicitly. Do not manufacture an overlap promise by keeping old secrets accepted forever.

Record which key version verified a delivery without logging the secret, raw signature or private body. A provider-issued version identifier is preferable to an improvised secret fingerprint. Retire the old verifier after the documented window and confirm a fixture signed only by that key is refused. The rotation record should identify who can reverse a mistaken configuration and what restoring the old key would mean for exposure and delivery.

Replay and duplicate effects remain separate

A valid signature establishes integrity and the relevant secret possession under the provider scheme. It does not automatically establish freshness or make a repeated event harmless. Apply authenticated timestamp checks where the protocol provides them, and deduplicate stable event or business identities according to the provider contract. A header not covered by the signature should not be treated as authenticated replay metadata.

OWASP's webhook guidance describes these controls as related but distinct. After verification, validate the event schema and durably accept work before acknowledging under the chosen delivery policy. No provider rotation or live webhook fixture was executed for this guide. The release artifact should include current-key, retiring-key, altered-body, expired-window and duplicate-event outcomes using synthetic records, with no reusable credentials in logs.

OWASP: webhook authenticity, replay and idempotent processing

Stripe webhook idempotency for SaaS subscriptions

Observability for small teams: start with the failed customer task

Bring one API or data workflow and a sanitized failure example for a bounded implementation assessment.

AptixLabs saas development

How do you rotate webhook signing keys safely?

Follow each provider’s exact signed-byte and overlap contract, verify before processing and test key retirement separately from replay and business deduplication.

Can all HMAC webhook providers use the same message format?

No. Signed bytes, headers, timestamps and rotation overlap differ by provider.

Does a valid signature prevent duplicate processing?

No. Authenticated repeated events still need durable deduplication and business idempotency.

Need help with b2b saas development for connected customer workflows?

Tell us about your product, existing stack and the problem you need to solve. We can discuss integration scope, implementation tradeoffs and a practical first milestone.

Start a conversation