All checks
Securitycriticalexposed-stripe-webhook-secretchecked on every page we scan

Stripe Webhook Signing Secret Exposed

Your Stripe webhook signing secret is public.

Why it matters

Your Stripe webhook signing secret is public. This secret is the only thing that proves a webhook actually came from Stripe: your server verifies the signature on each event against it. Anyone who reads it from your JavaScript can sign their own events and POST them to your webhook endpoint, and your application will accept them as genuine — letting them mark orders as paid, activate subscriptions, or grant paid access without any money moving. It is also a signal worth acting on by itself: this secret has no reason to exist in frontend code at all, so its presence means webhook-handling code was bundled into the client.

How ShipReady detects it

Detects credentials that reached the browser. Scans the page HTML and every linked JavaScript bundle for the literal shapes real provider keys take — `sk_live_`, `AKIA`, `ghp_`, a JWT with a service_role claim — rather than guessing from entropy. High-entropy string detection finds build hashes, CSS class names and base64 images on almost every site; provider prefixes are unambiguous, which is why this check reports only what it can name. A match is evidence, not inference: the value was served to anyone who loaded the page. Evidence records the variable NAME and a ten-character prefix, never the key, because a report that quoted the secret would republish it. Bundles are fetched once per scan and shared across pages, so a key present in a chunk loaded on ten routes is reported once.

Detection is deterministic. ShipReady reports this only when it observes the condition directly, and prefers to miss a real problem over inventing one. Rule version 1.7.0.

How to fix it

This is the prompt ShipReady puts in your report — written to be pasted straight into Cursor, Claude Code, or whichever assistant built the app.

Your Stripe webhook signing secret (whsec_) is in client-side code, so anyone can forge webhook events your server will accept as genuine — marking orders paid or granting paid access with no money moving. Roll it immediately: Stripe Dashboard -> Developers -> Webhooks -> select the endpoint -> roll the signing secret, then set the new value as a server-only environment variable (never NEXT_PUBLIC_/VITE_ prefixed). Then find why it was bundled at all: webhook handling belongs in a server route (a Next.js route handler, server action, or your backend), so if that code is reachable from the browser, move it. Verify your handler calls stripe.webhooks.constructEvent with the raw request body and rejects anything that fails verification. Finally, review recent events and orders for fulfillments that were never actually paid for.

Frequently asked questions

What does "Stripe Webhook Signing Secret Exposed" mean?
Your Stripe webhook signing secret is public.
How serious is it?
ShipReady rates this critical. Fix before launch. On its own, this is enough to compromise the application.
How do I fix it?
Paste the fix prompt on this page into Cursor, Claude Code or your AI editor. It is the same prompt ShipReady puts in your report.
Can I check my own site?
Yes — ShipReady scans up to ten pages of any public site for free and reports this alongside every other check. The free report lists every issue it finds and shows full evidence and a fix prompt for the critical and high-severity ones; medium and low findings are counted and unlock on Pro.

Related checks

Run this check on your site

ShipReady checks this and 193 other things across up to ten pages of your site, with an AI-ready fix for each. Free, no signup.