A webhook signature verification checklist should prove that a delivery was created with the expected secret and that its signed bytes were not altered before verification. Capture the raw request body, use the provider's documented algorithm and header, compare safely, reject failures before business logic, and test both valid and invalid requests.

Record the provider contract

For each webhook endpoint, document the provider, public path, event types, signing header, algorithm, secret owner, secret store, rotation method, delivery identifier, retry behavior, and expected response time. Do not assume that two providers use the same header format or canonicalization rules.

GitHub recommends setting a high-entropy webhook secret, storing it securely, and validating the `X-Hub-Signature-256` HMAC-SHA256 value before processing the delivery (GitHub Docs). Other providers may use timestamps, multiple signatures, or different encodings, so follow the contract for that endpoint.

Preserve the signed bytes

Read and retain the raw request body before JSON parsing, form decoding, whitespace changes, character conversion, or middleware rewriting. Stripe's troubleshooting guidance says its verification uses the raw request body, the `Stripe-Signature` header, and the endpoint secret; changes such as reordering JSON fields or altering whitespace can cause verification to fail (Stripe Docs).

Make the raw-body requirement explicit in framework configuration and tests. A verifier that works in a unit test can still fail in production when a proxy, gateway, or body parser changes the payload first.

Validate before doing work

Retrieve the endpoint secret from an approved secret manager or protected runtime setting. Recalculate the expected signature with the provider's specified algorithm and the exact signed input. Compare the received and expected values with a constant-time comparison function where the provider recommends it. GitHub specifically warns against a plain equality operator and points to constant-time comparison methods.

If the signature header is missing, malformed, or does not match, return the documented rejection response and stop. Do not create orders, update records, send messages, enqueue trusted work, or log the full payload as a fallback. Record bounded diagnostic data such as provider, endpoint, delivery identifier, time, and failure category.

Check freshness and replay controls

When the provider signs a timestamp, verify it using the provider's stated tolerance and a reasonably synchronized system clock. Track delivery identifiers or idempotency keys so an authentic retry does not repeat a non-idempotent action. Signature validity proves origin and integrity for the signed request; it does not by itself make repeated processing safe.

Keep replay and duplicate handling separate from signature failure. A valid duplicate can be acknowledged without repeating the side effect, while an invalid request should never enter the trusted processing path.

Test known-good and failure cases

Use provider test deliveries or documented test vectors. Test a valid payload, a one-byte body change, a wrong secret, a missing header, a malformed header, an expired timestamp when applicable, Unicode content, and a legitimate redelivery. Verify that only the valid cases reach business logic and that logs do not expose the secret or sensitive payload data.

Include the reverse proxy, API gateway, serverless adapter, and production body parser in at least one end-to-end test. Confirm that an operational alert distinguishes provider delivery failure from receiver signature rejection.

Rotate without losing deliveries

Document whether the provider supports overlapping old and new secrets. If it does, accept both only for a bounded transition and remove the old one after evidence shows the new signature path works. If it does not, coordinate the provider and receiver changes, preserve rollback, and monitor delivery logs closely.

Review ownership after staff changes, vendor migrations, endpoint moves, and security incidents. Retire unused endpoints and revoke their secrets rather than leaving dormant public routes indefinitely.

Quick completion test

Verification is ready when the raw body reaches the verifier unchanged, the provider's documented signature validates before processing, failures stop safely, comparison and secret storage follow secure practices, duplicate handling is idempotent, end-to-end negative tests pass, and rotation has an owner and rollback plan.

Sources

  1. Validating webhook deliveries — GitHub Docs
  2. Resolve webhook signature verification errors — Stripe Docs