Clerk webhooks on Next.js Pages Router: bodyParser false and buffer the raw body
Pages Router API routes parse JSON by default, which breaks Svix verification. Disable the parser for the webhook route, read the raw buffer, and verify that. Not the full reference manual.
TL;DR: Pages Router API routes parse JSON by default, which breaks Svix verification. Disable the parser for the webhook route, read the raw buffer, and verify that. Pages Router webhook handler Disable the body parser Without this, Next parses the JSON body before your handler runs and the Svix signature will never match. Checklist - The route must be public: no getAuth(req) gate, no middleware protection on /api/webhooks.
Pages Router webhook handler
Disable the body parser
export const config = {
api: { bodyParser: false },
}Without this, Next parses the JSON body before your handler runs and the Svix signature will never match.
Buffer the raw body, then verify
import { verifyWebhook } from '@clerk/backend/webhooks'
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).end()
}
try {
const evt = await verifyWebhook(req)
// handle evt by evt.type, write to DB
return res.status(200).json({ received: true })
} catch (err) {
console.error('webhook verification failed', err)
return res.status(400).json({ error: 'verification failed' })
}
}verifyWebhook from @clerk/backend/webhooks accepts the Node request; with bodyParser: false the raw stream is intact for it to read.
Checklist
- The route must be public: no
getAuth(req)gate, no middleware protection on/api/webhooks. Webhooks carry no session. CLERK_WEBHOOK_SIGNING_SECRETmust be the secret for this exact endpoint. Rotating or recreating the endpoint invalidates the old secret.- Return 200 after your side effects. Non-2xx triggers retries with backoff; design the handler idempotent (key on the event id or
evt.data.id) because retries redeliver. - If verification fails only in production, compare secrets first (dev vs prod endpoint), then check that no proxy or CDN is altering the body or stripping the
svix-*headers.
When to use this
- This covers exactly what the title says: Clerk webhooks on Next.js Pages Router.
- You are setting this up for the first time, or auditing an existing setup.
- You want the key gotchas in one place before you start.
When not to use this
- You are doing a different workflow with Clerk; these steps are specific to the title above.
- You need the full reference docs; this is the short path, not the manual.
Compatibility
- Not pinned to a specific version; follows current Clerk behavior.