Shopify Webhook HMAC Verifier

Compute and check a webhook's HMAC signature in your browser, with matching code for Node, PHP and Python.

Describe the result you need. AI will set up the tool and ask for any missing details.

Configure with AI

Enter to submit · Shift + Enter for a new line

Sent to AI. Leave out passwords and private customer data.

Your details

Paste the exact bytes Shopify sent, before any JSON parsing. Even reformatting the JSON changes the signature.

Paste it here to check it against the value this computes.

Verification code

javascript
const crypto = require("crypto");

function verifyShopifyWebhook(rawBody, hmacHeader, secret) {
  const digest = crypto.createHmac("sha256", secret).update(rawBody, "utf8").digest("base64");
  const a = Buffer.from(digest);
  const b = Buffer.from(hmacHeader);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// rawBody must be the exact, unparsed request body (a string or Buffer).
// hmacHeader is req.headers["x-shopify-hmac-sha256"].

Computed HMAC-SHA256 (base64): sBDGCgIvRRH0qBMcI830QHFApi/fQq4XKvyrvRwZtfY=

How Shopify signs a webhook

Every webhook Shopify sends carries an X-Shopify-Hmac-SHA256 header: a base64-encoded HMAC-SHA256 digest of the raw request body, keyed with your app's client secret. You recompute the same digest on your end and compare it to the header using a constant-time comparison, never a plain ==, to avoid timing attacks.

Secret "hush", sample order payload

Algorithm
HMAC-SHA256, base64

sBDGCgIvRRH0qBMcI830QHFApi/fQq4XKvyrvRwZtfY=

The most common mistake is verifying against a re-serialized or pretty-printed copy of the body instead of the exact raw bytes Shopify sent. Any difference, even whitespace, changes the digest.

Where to check it

Do this check before your route parses the body as JSON: read the raw body first, verify it, and only then parse. In Express, that usually means reading the body with a raw body parser for the webhook route specifically. In PHP, read it with file_get_contents("php://input") before anything else touches the request.

Questions people ask

Why doesn't my computed HMAC match Shopify's?

Almost always because the body was already parsed, reformatted or had encoding changed before you hashed it. Hash the exact raw bytes Shopify sent, before any JSON.parse or similar.

Which secret do I use?

Your app's client secret from the Dev Dashboard (or Partner Dashboard for older apps). It's the same secret used in the OAuth flow, not a separate webhook-only key.

Is it safe to paste my client secret into this tool?

Everything runs locally in your browser using the Web Crypto-equivalent math built into this page; nothing is sent anywhere. Still, only paste a secret into tools you trust, and rotate it if you're ever unsure.

Do I need to verify webhooks from Shopify Flow or manual triggers?

Yes, every webhook delivery over HTTPS carries this header and should be verified the same way, regardless of what triggered it.

Want this builtinto your store?

I'm Emmanuel, a Shopify and WooCommerce developer. If this tool pointed at something you'd rather have fixed for you (a faster store, cleaner product data, a migration, a custom feature), send me a note.

Emmanuel Asika

Emmanuel Asika

Shopify & WooCommerce developer, Dublin

I build and fix online stores: new builds, migrations, speed, SEO and the custom bits apps can't do.

hello@emmanuelasika.com

Last reviewed 30 September 2026.

Sources: Shopify.dev: verify webhook deliveries