Stripe secret key leaked?
Rotate it now. This one is about money.

A live Stripe secret key (the one that starts sk_live_) can charge cards, issue refunds, and redirect your payouts. This is the highest-stakes key you can leak. Here is the calm, in-order runbook to shut it down.

Go to the rotation steps → Check if it is in your bundle (free)

Why this leak is worse than most

Most leaked keys cost you an API bill. A live Stripe secret key touches real money, so it sits in its own category.

The secret key can do almost anything your own backend can do. In plain terms, that means someone who has it can create charges, issue refunds to a card they control (which quietly drains your balance), read your customer and payment records, and change settings that decide where your payouts go. That last one is the nightmare: money leaving your account to theirs.

So the order is simple. Kill the key at Stripe first. Everything else (checking logs, cleaning your code, rolling the webhook secret) comes after the key is dead. Rotating is the only step that actually stops the bleeding.

Two keys, two very different risks. Stripe gives you a publishable key (pk_live_) and a secret key (sk_live_). The publishable key is meant to live in your frontend and is safe there. The secret key is not, ever. If the leaked value starts sk_live_ (or rk_live_, a restricted key), keep reading. If it starts pk_live_, that is expected to be public and there is nothing to rotate.

The runbook, in order

Six steps. The first one is the only urgent one. Do it, then work down the list.

1

Rotate the key at Stripe

Open the API keys page at dashboard.stripe.com/apikeys (make sure you are in live mode, not test mode). Find the leaked secret key, then:

  1. Click the overflow menu (the three dots, ···) next to the key and choose Rotate key.
  2. In the Expiration dropdown, choose Now. That deletes the old key immediately. Use Now for a real leak. Only pick a later time if an automated system needs a short window to pick up the new key first (Stripe allows a grace period of up to 7 days for that).
  3. Click Rotate API key, then click the new key value to copy it. Stripe shows the new secret key once, so copy it now.
  4. Paste the new key into your server environment (for example STRIPE_SECRET_KEY), your CI secrets, and your host's environment settings (Vercel, Netlify, Render, and so on). Redeploy.

Confirm the old key is dead. A quick test call with the old key should be rejected with a 401 (that is the HTTP code for "not authorized"):

curl https://api.stripe.com/v1/charges -u sk_live_OLDKEY:

If that returns a 401, the leaked key is off. Good.

2

Check for fraud in payments and logs

The key may have been used before you rotated it. Two places to look:

  1. Open your payments list and look for charges or, more importantly, refunds you did not issue since the leak. A thief usually refunds real charges to a card they control, so scan for refunds first.
  2. Open the API request logs. Every request shows the IP address behind it. Requests from IPs you do not recognize, after the leak date, are the ones to worry about.

Also glance at your payout settings and connected bank account to make sure nothing was changed. If you see money moving where it should not, contact Stripe Support right away with a short factual timeline: when the key leaked, when you rotated it, and which activity is not yours.

3

Find how it leaked (and never put the secret key in your frontend)

The number one way this happens: the secret key ends up in the browser. A publishable key (pk_live_) belongs in the frontend. A secret key (sk_live_) must stay on your server, always.

In tools like Next.js and Vite, any environment variable prefixed NEXT_PUBLIC_ or VITE_ gets baked into the JavaScript that ships to the browser. If your secret key was named something like NEXT_PUBLIC_STRIPE_SECRET_KEY, it is in your public bundle right now. To check:

  1. Open your live site, then open the browser's developer tools and view the built JavaScript files (the Sources or Network tab).
  2. Search those files for sk_live_. If it appears, anyone who has visited your site could have copied it.
  3. Also search your git history, since old commits keep secrets even after you delete them: git log -S "sk_live_" --all --oneline. If any .env file ever appears in history, treat every key that lived in it as leaked too.

The fix: move all Stripe secret-key calls to server-side code (an API route, a serverless function, your backend), and rename the variable so it has no public prefix. The browser should only ever see the pk_live_ publishable key.

4

Switch to restricted keys going forward

Once you are stable, reduce the blast radius of the next mistake. Stripe offers restricted API keys (they start rk_live_). A restricted key can do only what you grant it, instead of everything.

The idea is least privilege: give each part of your app a key with just the permissions it needs. A key that only creates payment intents cannot also issue refunds or read your full customer list. So if it leaks, the damage is capped. Stripe recommends restricted keys over the all-powerful secret key for most integrations. Create one at dashboard.stripe.com/apikeys, grant only the resources your code touches, and swap your integration over to it.

5

Roll your webhook signing secret too

This step gets skipped, and it matters. Your webhook signing secret (it starts whsec_) is what your server uses to prove that incoming webhook events really came from Stripe and not a forger. It is not the same as your API key, but if your .env file leaked, this secret was likely sitting right next to the key.

If someone has your signing secret, they can forge webhook events that look genuine (for example, a fake "payment succeeded" that tricks your app into shipping an order). So roll it:

  1. Go to the Webhooks section of your Stripe Dashboard (under Developers or Workbench) and open the endpoint.
  2. Open its overflow menu (···) and choose Roll secret. You can expire the old secret immediately or keep it live for up to 24 hours while you update your server code, whichever fits.
  3. Update the whsec_ value in your server environment and redeploy.
6

Confirm the bundle is clean

Once the key is rotated and the code is fixed, verify it actually worked. Our free scan looks at your live site passively, with no signup, and flags a secret sitting in your public bundle or a config file your server should not be handing out. It is the fastest way to be sure the sk_live_ is really gone from where the public can reach it.

Run the free scan →

Does Stripe catch leaked keys on its own?

Partly, and it is worth understanding the limits so you do not rely on it.

Stripe takes part in GitHub secret scanning. So a secret key pushed to a public GitHub repo can be reported straight to Stripe, and Stripe may notify you or deactivate the key. That is a real safety net, and it is why some people find out from a Stripe email before they notice the push themselves.

But it is not a guarantee, and it does not cover every leak. It will not catch a key baked into your frontend JavaScript, a key in a private repo a contractor copied, or a key posted in a chat or paste site. Assume the key was captured the moment it went public, and rotate it yourself. A Stripe email is a helpful confirmation, not the plan.

Rotate other providers too

A leaked Stripe key rarely travels alone. The same .env file usually holds a database URL, an AI-provider key, and more. If one leaked, check the neighbors.

We keep dedicated, step-by-step pages for the keys we see leak most: the general leaked API key runbook, plus OpenAI, Anthropic, Supabase service role keys, and the catch-all exposed .env file guide. Building on an AI app builder? See the Lovable security checklist, the Bolt leaked-key guide, and our Claude Code security notes.

This same rotation walkthrough ships as a guided command (/lictor-rotate) inside our free, open-source Claude Code plugin. Install it with:

/plugin marketplace add Raffa-jarrl/Lictor-AI
/plugin install lictor-security-suite@lictor-ai

The same plugin runs a 48-check audit catalog over your codebase, in plain English, covering leaked keys, exposed configs, and the rest of what gets apps breached.

And because "trust us" is not evidence: we publish a reproducible benchmark. In a blind run on a 30-case stratified sample of OWASP Benchmark 1.2, the audit scored an F1 of 93.8%. That is a 30-case sample, not the full benchmark, and we say so right in the results.

Next time, catch it in hours, not on the invoice

Today you found out the hard way: a fraudulent refund, a Stripe email, or a strange charge. That is the worst alarm system there is, and with payments the cost lands fast.

Lictor's continuous monitoring watches your exposed surface for you, from $49/mo, so the next time a secret slips into your bundle or a config file goes public, it is a notification while it is still small, not a surprise on your balance. Sign up at app.lictor-ai.com/billing. It is run by the same person who wrote this runbook: a security engineer with 20 years in the field who does real responsible-disclosure work.

See continuous monitoring → Start with the free scan

Questions people ask at this point

What can someone do with a leaked Stripe secret key?

A live secret key (sk_live_) can do almost anything your own code can do: create charges, issue refunds to a card the thief controls, read your customer and payment records, and change settings that decide where payouts go. This is the highest-stakes leak because it touches real money, not just an API bill. Rotate it at the Stripe Dashboard first, then check payments and logs for anything you did not do.

How do I roll a Stripe key?

Open dashboard.stripe.com/apikeys in live mode. Click the overflow menu (three dots) next to the leaked secret key and choose Rotate key. In the Expiration dropdown pick Now to kill the old key immediately (the right choice for a real leak), or a later time if an automated system needs a short grace window. Click Rotate API key, copy the new key (shown once), put it in your server environment and CI secrets, and redeploy. Confirm the old key returns a 401 error.

Does Stripe deactivate leaked keys automatically?

Sometimes, but never count on it. Stripe takes part in GitHub secret scanning, so a key pushed to a public repo can be reported to Stripe, and Stripe may notify you or deactivate it. That does not cover every case, though (a key in your frontend bundle or a private repo a contractor copied, for example). Assume the key was captured the moment it went public and rotate it yourself.

Is the publishable key (pk_live_) a problem too?

No. The publishable key is designed to live in your frontend and be visible to anyone. Only the secret key (sk_live_) and restricted keys (rk_live_) need rotating. If the value you found starts with pk_live_, that is expected and there is nothing to do.