Skip to content
Huggehub Global Digital StudioParis --:--London --:--New York --:--Now accepting new projects →AI / Commerce / Technology / GrowthEurope • United Kingdom • United States

Engineering · 7 min read ·

How to Verify Shopify Webhooks (HMAC Signature, Headers and Retries)

An unverified webhook endpoint is an open door. Here's exactly how Shopify signs webhooks and how to check the signature in Python and Node.

By Hatim El Badaoui

A software core connected by API pipelines to order, product and shipping modules

Shopify webhooks tell your app when something happens — an order is created, a product changes, an app is uninstalled. Your endpoint is a public URL, so anyone can send it a request that looks like a webhook. Verifying the signature is how you know it really came from Shopify.

How Shopify signs a webhook

Every webhook request includes a header, X-Shopify-Hmac-Sha256. Its value is the base64-encoded HMAC-SHA256 of the raw request body, computed with your webhook secret:

  • For apps, the secret is the app's client secret (API secret key) from the Partner Dashboard or Dev Dashboard.
  • For webhooks created in the Shopify admin (Settings → Notifications), use the signing key shown there.

Other Shopify webhook headers you should use

HeaderUse
X-Shopify-TopicWhich event it is, e.g. orders/create
X-Shopify-Shop-DomainWhich store sent it (multi-store apps)
X-Shopify-Webhook-IdUnique delivery ID — use it to ignore duplicates
X-Shopify-Triggered-AtWhen the event happened — use it to handle out-of-order deliveries
X-Shopify-API-VersionThe API version of the payload

The three rules of verification

  1. Use the raw body. Compute the HMAC on the exact bytes received, before any JSON parsing. Re-serialising parsed JSON changes whitespace and breaks the signature — the most common bug.
  2. Compare in constant time. Use hmac.compare_digest (Python) or crypto.timingSafeEqual (Node) so the comparison can't be timing-attacked.
  3. Reject before doing anything. Return 401 and stop if the signature doesn't match.

Python example

import base64, hashlib, hmac

def verify_shopify_webhook(raw_body: bytes, hmac_header: str, secret: str) -> bool:
    digest = hmac.new(secret.encode(), raw_body, hashlib.sha256).digest()
    expected = base64.b64encode(digest).decode()
    return hmac.compare_digest(expected, hmac_header or "")

This is the logic packaged in our dependency-free shopify-webhook-validator, which you can drop into Flask, FastAPI or a serverless function.

Node.js example

import crypto from "node:crypto";

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

In Express, read the body with express.raw({ type: "application/json" }) on the webhook route so you still have the raw bytes.

Respond fast, process later

Shopify expects a 2xx response within a few seconds. If your endpoint is slow or errors, Shopify retries the delivery several times over the following hours and can eventually remove a subscription that keeps failing. The robust pattern:

  1. Verify the HMAC.
  2. Check X-Shopify-Webhook-Id against recently processed IDs; skip duplicates.
  3. Put the payload on a queue and return 200 immediately.
  4. Process from the queue with retries and logging.

Don't rely on webhooks alone

Webhooks are "at least once" and occasionally late or missing. For critical data such as orders, run a periodic reconciliation job through the GraphQL Admin API to catch anything missed.

Building a custom Shopify app or integration? See our Shopify app development work and Shopify development service.

Frequently asked questions

Where do I find my Shopify webhook secret?

For apps, it is the app's client secret (API secret key). For webhooks created under Settings → Notifications in the admin, Shopify shows a signing key on that page.

Why does my Shopify HMAC verification always fail?

Usually because the HMAC is computed on parsed and re-serialised JSON instead of the raw request body, or with the wrong secret.

Can Shopify send the same webhook twice?

Yes. Deliveries are at least once. Use the X-Shopify-Webhook-Id header to detect and skip duplicates.

Let's build what's next

Let's build what's next.

Have a project, product or ambitious idea? Tell us where you want to go.