Stripe webhook · server log

Wrong webhook secret (CLI vs Dashboard, test vs live)

what it looks like
No signatures found matching the expected signature for payload  (works locally with stripe listen, fails in production)

What it means

Every endpoint has its own signing secret. The secret printed by stripe listen only verifies events forwarded by the CLI. A Dashboard endpoint has a different one, and test-mode and live-mode endpoints have different ones too.

Common causes

  • The stripe listen whsec_ is set in production
  • A test-mode secret is used with a live-mode endpoint (or the reverse)
  • You rolled the secret in the Dashboard and didn't redeploy
  • Extra quotes or a trailing space in the env var

How to fix it

  1. Stripe Dashboard → Developers → Webhooks → your endpoint → reveal the signing secret
  2. Set it as STRIPE_WEBHOOK_SECRET in the production environment (the matching mode: live for live keys)
  3. Redeploy so the running build sees the new value
  4. Keep the stripe listen secret only in .env.local
.env.local
# local only: printed by `stripe listen`
STRIPE_WEBHOOK_SECRET=whsec_from_stripe_listen

# production (set on your host, not in git):
# STRIPE_WEBHOOK_SECRET=whsec_from_dashboard_endpoint
verify the fix
stripe listen --forward-to localhost:3000/api/stripe/webhook
# put the whsec_... it prints in .env.local, then restart npm run dev
stripe trigger checkout.session.completed
stripe events resend evt_... --webhook-endpoint=we_...
# then the deployed endpoint; add --live for a live-mode event

pass if Local events return [200], and the resent event shows a 200 delivery on the deployed endpoint.

works with stripe-node 23.0.0 · Next.js 16.4.3 App Routerlast verified CLI steps: Stripe CLI 1.55.2, Oct 27, 2026

prevent it · stripe-webhooks.mdc (free Cursor Rules Pack): One whsec_ secret per endpoint and environment, and test keys never paired with live secrets.

Prompt for Cursor or Claude Code

Stripe webhook signature checks pass locally but fail in production. List every place STRIPE_WEBHOOK_SECRET is read, confirm the code trims nothing weird, and give me a checklist to confirm production uses the Dashboard endpoint's signing secret for the same mode (test/live) as STRIPE_SECRET_KEY. Do not hardcode any secret.

Not quite your error? Paste yours into the free VibeBoot Doctor or browse the Stripe webhook fixer.

next: skip the next one

Skip the debugging. The VibeBoot SaaS Starter ships with this already handled: a signature-verified webhook (raw body + constructEvent) that re-fetches the subscription from Stripe before saving it, with Vitest tests. Plus auth and a database on Next.js 16.4.3. $49, one-time.

14-day money-back guarantee · lifetime updates

$ fixed it? get the next one first

New fixes for what AI agents break, by email.

When we add a fix or a Cursor rule for a new Next.js, Auth.js, Prisma or Stripe break, it goes out in the VibeBoot newsletter. Free, occasional, unsubscribe in one click.

Subscribe free