Verify Before You Process: Securing WhatsApp Cloud API Webhooks
Learn how to verify WhatsApp webhook signatures, preserve raw request bodies, reject replays, and keep message processing secure.
A webhook endpoint is a public door into your application. If it accepts every payload that looks like a WhatsApp event, anyone who discovers the URL can manufacture messages, trigger automations, or fill your queues with junk. Reliable processing starts with a stricter rule: authenticate the request before trusting its JSON.
Understand the two verification paths
WhatsApp Cloud API uses verification at different moments, and they solve different problems.
During webhook setup, Meta sends a GET request containing a mode, challenge, and verify token. Your endpoint compares the supplied token with a secret value you configured, then returns the challenge. This proves that you control the endpoint, but it does not authenticate later notifications.
For event delivery, validate the X-Hub-Signature-256 header. It contains an HMAC-SHA256 signature created from the request body and your app secret. Your server calculates the same HMAC and compares the results. If they differ, reject the request.
The setup verify token and the app secret are separate credentials with separate jobs. Do not treat the initial challenge as ongoing request security.
Preserve the exact request bytes
The most common implementation mistake is calculating the HMAC from parsed JSON. Parsing and serializing can change whitespace, escaping, or key order, producing a different byte sequence even when the data is logically identical.
Capture the raw request body before the framework's JSON middleware transforms it. Then:
- Read the signature header and require the
sha256=prefix. - Compute HMAC-SHA256 over the raw bytes using the app secret.
- Compare signatures with a constant-time function.
- Return
401or403immediately when validation fails. - Parse JSON only after successful verification.
Constant-time comparison matters because a normal string comparison may stop at the first mismatch. A timing-safe function avoids leaking how much of a signature was correct. Also validate equal buffer lengths before comparing; many standard crypto APIs reject unequal inputs.
Keep secrets and logs disciplined
Store the app secret in a secret manager or protected environment variable, not in source control. Limit access to the runtime that verifies webhooks, and establish a rotation procedure before an incident forces you to invent one under pressure.
Logging should show enough to investigate failures without exposing credentials or customer content. Record the timestamp, request ID, event type when available, and a reason such as missing_signature or signature_mismatch. Never log the app secret, full signature, access token, or complete message body by default.
Apply ordinary edge protections too: HTTPS, request-size limits, sensible timeouts, and rate limiting. These controls complement signature verification; they do not replace it.
Separate acceptance from processing
Once authenticated, acknowledge valid notifications quickly and hand work to a queue. Sending replies, calling internal services, or updating analytics inside the webhook request increases timeout risk and encourages duplicate delivery.
Consumers should still be idempotent. Track Meta's message or event identifiers where appropriate, because a correctly signed webhook can be delivered more than once. Signature verification answers who sent this request; idempotency answers whether this event was already handled.
The practical takeaway is simple: preserve raw bytes, verify with the app secret, compare safely, and only then parse or enqueue. That small boundary turns a public URL from a trust assumption into a controlled entry point.
Build with Abati Technology
We build software that ships — WhatsApp API, developer tools, POS, and mobile apps. Let's talk about your project.
Get in Touch →