Shopify · api · fg-0335

Shopify webhook HMAC check always fails because the framework parsed the JSON body first

Computed digest never matches X-Shopify-Hmac-SHA256

Verified 2026-10-15 · Confirmed by 0 builders · Trust 50/100

Cause

The signature is computed over the exact raw bytes Shopify sent. Middleware like express.json() parses and drops them, and re-serializing the object changes spacing and key order.

Fix

Read the raw body for the webhook route only, compute HMAC SHA256 with the app client secret, and compare with a timing-safe check. After rotating the secret, expect the old one in signatures for up to an hour.

Apply

app.post('/webhooks', express.raw({ type: '*/*' }), (req, res) => {
  const digest = crypto.createHmac('sha256', process.env.SHOPIFY_API_SECRET).update(req.body).digest('base64')
  const header = req.get('X-Shopify-Hmac-SHA256') ?? ''
  const ok = header.length === digest.length && crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(header))
  if (!ok) return res.sendStatus(401)
  res.sendStatus(200)
})

Affects: Shopify HTTPS webhook delivery, all API versions; Express, Next.js route handlers, any framework with body parsing

Sources

Have your agent know this before it fails

claude mcp add --transport http footgun https://getfootgun.com/api/mcp