Skip to Content
FluxStore is currently invite-only. Some sections of this documentation are still being written and expanded.
APIWebhooksSecurity & verification

Security & verification

Every webhook request includes an X-Webhook-Signature header containing an HMAC-SHA256 signature of the request body, signed with your webhook signing secret.

Important: It is your responsibility to verify the signature on every incoming webhook request. Without verification, any third party could send forged requests to your endpoint. Always validate the signature before processing webhook data.

To verify the signature:

  1. Read the raw request body as a string
  2. Compute HMAC-SHA256 of the body using your signing secret
  3. Compare the computed signature with the X-Webhook-Signature header value

The signature format is: sha256=<hex-encoded-hash>

Example (Node.js)

const crypto = require('crypto'); function verifyWebhookSignature(body, signature, secret) { const computed = 'sha256=' + crypto .createHmac('sha256', secret) .update(body, 'utf8') .digest('hex'); return crypto.timingSafeEqual( Buffer.from(computed), Buffer.from(signature) ); }

Example (Python)

import hmac import hashlib def verify_webhook_signature(body: str, signature: str, secret: str) -> bool: computed = 'sha256=' + hmac.new( secret.encode('utf-8'), body.encode('utf-8'), hashlib.sha256 ).hexdigest() return hmac.compare_digest(computed, signature)

Example (PHP)

function verifyWebhookSignature(string $body, string $signature, string $secret): bool { $computed = 'sha256=' . hash_hmac('sha256', $body, $secret); return hash_equals($computed, $signature); }

Example (Java)

import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.security.MessageDigest; public boolean verifyWebhookSignature(String body, String signature, String secret) { try { Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256")); byte[] hash = mac.doFinal(body.getBytes("UTF-8")); StringBuilder hex = new StringBuilder(); for (byte b : hash) { hex.append(String.format("%02x", b)); } String computed = "sha256=" + hex.toString(); return MessageDigest.isEqual( computed.getBytes("UTF-8"), signature.getBytes("UTF-8") ); } catch (Exception e) { return false; } }

Source IP addresses

Webhook requests are delivered through Cloudflare’s global network, meaning requests may originate from any Cloudflare IP address. We do not recommend IP allowlisting as the sole method of verifying webhook authenticity. Use signature verification (above) instead.

However, if you want an additional layer of security, you can restrict incoming requests to Cloudflare’s published IP ranges: https://www.cloudflare.com/ips/