Clerk webhooks: Svix verifies the RAW body, so never parse JSON before verifying
The single biggest webhook failure across every framework: the body parser consumes the JSON first, and the Svix signature no longer matches. Verify against the raw bytes, in every runtime.
Raw body rule for Clerk webhooks
Clerk signs webhooks with Svix (Standard Webhooks). The signature is computed over the exact raw request bytes. If any middleware parses the body into JSON first, even a whitespace or key-order change breaks the signature and verification fails.
The rule, per framework
- Next.js App Router: pass the
Requeststraight toverifyWebhook(req). Do not callawait req.json()first; the body can only be consumed once and parsing it first breaks verification. - Next.js Pages Router: API routes parse JSON by default. Disable it with
export const config = { api: { bodyParser: false } }and buffer the raw body yourself before verifying. - Express: do not mount
express.json()globally ahead of the webhook route. Useexpress.raw({ type: 'application/json' })on the webhook route so the handler gets a Buffer. - Fastify: the default JSON parser consumes the body. Add a content-type parser with
parseAs: 'buffer'forapplication/jsonon the webhook route, then verify the buffer.
Verify, then branch on event type
import { verifyWebhook } from '@clerk/backend/webhooks'
const evt = await verifyWebhook(req) // req carries the raw body
if (evt.type === 'user.created') {
// evt.data is now typed for user.created; handle it
}Narrow on evt.type for type-safe access to evt.data. The WebhookEvent union covers every event; un-narrowed access is untyped.
Checklist
- The Svix headers (
svix-id,svix-timestamp,svix-signature) must reach your handler untouched. Proxies that strip headers break verification silently. - Signing secret lives in
CLERK_WEBHOOK_SIGNING_SECRET, per endpoint. The wrong endpoint's secret is a common cause of "signature invalid" in apps with dev and prod endpoints. - Return 2xx only after handling. 4xx/5xx (or no response) triggers Clerk retries; a 200 before your DB write risks losing the event if the write then fails.
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.