Your Stripe live key is in your client bundle. Read this in the next 10 minutes.

2026-05-08 · vibecheck team · 6 min read · Incident response

Quick answer If sk_live_ appears in your JavaScript bundle, every visitor to your site can issue refunds, create charges, pull customer data, list saved payment methods, and disable your webhook endpoints — all using your Stripe account. The fix is five steps in roughly this order: roll the key, verify the new key works server-side, audit recent API logs for unfamiliar requests, file with Stripe if abuse is found, and prevent recurrence with restricted keys plus a CI gate. Don't read the rest of this article. Do step 1 first.

Stop reading. Do this:

curl -sL https://your-app.com/ | grep -oE 'sk_live_[A-Za-z0-9]{24,}'

If anything matched, your Stripe secret key is in your client bundle right now. Open dashboard.stripe.com/apikeys in another tab. Click "Roll key" next to your secret key. Confirm. Now read the rest.

What an attacker can do with a leaked sk_live_

The publishable key (pk_live_) is meant to be public — it's just a tag identifying your account. The secret key (sk_live_) is roughly equivalent to a logged-in admin session on dashboard.stripe.com:

Every minute the leaked key is valid increases the risk. The first thing automated scrapers do when they find a Stripe live key is enumerate Connect accounts and try a test transfer. The second thing is open a refund loop.

Step 1 — Roll the key (90 seconds)

Stripe Dashboard → Developers → API keys → "Roll key" next to your live secret key.

The old key dies immediately. Every server, function, cron, or third-party service that was using it returns 401 until updated. This is correct. The breakage is how you find every place the old key lived.

Step 2 — Verify the new key works on your server (2 minutes)

Stripe gives you the new key once on screen. Copy it to your secret manager:

Verify it works by hitting your own server-side endpoint that uses Stripe — a checkout creation or webhook receiver. If that returns 200, you're back in business.

Step 3 — Audit Stripe API logs (3 minutes)

Dashboard → Developers → Logs. Filter by date range covering the time the key was exposed. Look for:

If you see anything alarming, take a screenshot of each suspicious entry. You'll need it for step 4.

Step 4 — File with Stripe if abuse is found (5 minutes)

If your audit shows unauthorised activity:

  1. Email [email protected] with subject "Compromised live secret key — abuse detected"
  2. Include: the key (rolled, no longer valid), the date range you're concerned about, screenshots from the API logs
  3. Stripe will reverse fraudulent charges and freeze the offending Connect account if it was used to launder the funds

You also have notification obligations under GDPR / CCPA / state breach laws if customer payment data was accessed. The right time to notify is when you discover it, not when you've fully scoped it.

Step 5 — Prevent recurrence

Three controls:

Use restricted keys, not the root secret key

Stripe supports restricted API keys with format rk_live_*. Each restricted key has a custom set of permissions. For most server-side use cases — creating customers, charges, subscriptions — a restricted key with read+write only on those resources is enough. If a restricted key leaks, the blast radius is bounded.

Generate a new rk_live_ with the minimum permissions your code needs. Use that for production. Reserve the root sk_live_ for one-off CLI work only.

Stripe's automatic key revocation

Stripe scans GitHub for leaked keys and rolls them automatically when found. This catches you only if the leak was a public commit. Bundle leaks (the kind we're discussing here) don't trigger Stripe's scan because the bundle isn't on GitHub. So this isn't enough on its own.

Add a CI gate

Run a check on every deploy. The simplest:

# In your CI:
DEPLOY_URL="${VERCEL_URL:-${DEPLOY_URL}}"
if curl -sL "$DEPLOY_URL" | grep -qE 'sk_live_[A-Za-z0-9]{24,}'; then
  echo "FAIL: Stripe live secret key in deployed bundle"
  exit 1
fi

Or use the vibecheck CLI to gate on every critical finding:

npx @vibecheck/cli "$DEPLOY_URL" --exit-on critical

Frequently asked

How did the key get into my bundle?

Almost always: an env-var with a NEXT_PUBLIC_, VITE_, or PUBLIC_ prefix. Or an AI builder that pasted the secret key into a React component because you said "wire up Stripe." Or a copy-paste from a tutorial that had the key inline as an example. Find the env var name; that's the smoking gun.

How long was it valid?

From the time you committed/deployed it. Stripe doesn't tell you when a key was first used — you have to reconstruct from API logs. Assume the worst case: the entire window from first-deploy to now.

Can I just use the publishable key (pk_live_) instead?

Yes — for any operation that goes through Stripe's checkout flow, Elements, or Payment Element. The pk_live_ key is designed for client use and only allows the operations Stripe meant to be client-safe (create payment methods, complete payment intents). Use it everywhere your client code touches Stripe. Move every other Stripe call to your server.

Should I rotate even if I haven't seen unauthorised activity?

Yes. Absence of evidence is not evidence of absence. Automated scrapers harvest GitHub and indexed pages on a 24-hour cycle; the key may already be compromised even if the abuse hasn't started yet. Rotate now.

Inspect your app for leaked Stripe keys