Shopify webhook verification fails for two reasons more than any other, and both come from habits that work everywhere else. The signature is base64, not the hex string Stripe and GitHub hand you, and Shopify's own quickstart parses the body with express.json() before you get a chance to hash the bytes it actually signed. Get either wrong and your comparison never matches even though your code looks right. This post shows the exact Shopify webhook check, which secret to use, and the traps that account for most of the failures.
The exact scheme
Per Shopify's docs, each webhook request carries an HMAC in the X-Shopify-Hmac-Sha256 header. It is HMAC-SHA256 computed over the raw request body, keyed with your app's client secret, and the result is base64-encoded. Three details decide whether your check works: the raw body, the client secret, and base64.
Verifying the signature
Compute the HMAC over the untouched body, base64-encode it, and compare it to the header in constant time.






