Leaked a Stripe secret key: how to rotate it without breaking webhooks
Rotate the key on the API keys page (the ⋯ menu, Rotate key) and pick the shortest expiry you can deploy within, or Now if the key is somewhere public. Put the new key in your host's server-side environment, redeploy, and confirm the old key returns 401. Rotating an API key does not change your webhook signing secret: Stripe's docs say signing secrets aren't API keys but per-endpoint whsec_ secrets, so signature verification keeps working.
What does break is any code still holding the old key when it expires, including a webhook handler that calls the API (retrieving a Checkout Session, a customer, a subscription). That is a deploy-order problem, not a webhook one. Then read the old key's request logs for anything you didn't send.
Pick the expiry deliberately
When you rotate in the Dashboard, the old and new keys can both work for up to 7 days. Stripe offers this so you can migrate without downtime. For a leaked key, every hour of overlap is an hour the leaked value also works.
Choose Now when the key is in a public repo, a client bundle or anywhere you can't take back. Your API calls fail until the redeploy lands. Webhook events that fail in the meantime are retried: Stripe retries live-mode deliveries for up to three days with exponential backoff.
Choose a short expiry (enough for one deploy) when the exposure is narrower, such as a log file or a former teammate's laptop. Stripe's guidance is to treat any exposure as a potential compromise and rotate immediately, even if you aren't sure anyone saw it.
Stripe sometimes detects exposed keys itself, asks you to rotate, and in some cases deactivates the key proactively. If that already happened, the outage has started and step 2 below is the fix.
The rotation, in order
Find every place the key lives before you rotate: production and preview env vars, CI secrets, local .env files, background workers, cron jobs, any third-party tool you pasted it into.
Rotate the key (⋯ > Rotate key > Expiration). Copy the new value straight into your host's secret settings under a server-only name (no NEXT_PUBLIC_, VITE_ or similar prefix). Stripe shows the new value once; you can't retrieve it later.
Redeploy every service that reads it. Most hosts read env vars only at startup.
Confirm the old key is dead once its expiry has passed. Expect 401:
Confirm webhooks still work end to end: in Workbench, open the endpoint's Event deliveries tab and check that recent events show Delivered. Failed ones show when Stripe will retry them.
Check what the leaked key was used for
On the API keys page, the ⋯ menu on the key has View request logs, which opens that key's API request logs in Workbench. Look for requests from IPs, user agents or endpoints that aren't your servers, especially refunds, payouts, payout destination changes, and listing customers.
If you see activity you don't recognize, contact Stripe support. Stripe's published notice is that the account holder carries losses from unauthorized activity on a compromised key.
Only if the webhook secret leaked too
If the same .env file held your whsec_ signing secret, roll it as well: in Workbench's Webhooks tab, open the endpoint, ⋯ > Roll secret. You can expire the old secret immediately or keep it valid for up to 24 hours; while both are active, Stripe sends one signature per secret, so a handler with either secret verifies. Update the handler, deploy, and let the old one expire. Each endpoint, and test and live mode, has its own secret.
So the next leak costs less
Replace the secret key with restricted keys, one per service, each with only the permissions that service needs. A leaked restricted key is limited to those calls. A read-only tool, such as Stripe Leak Check, needs a read-only restricted key and nothing more; it refuses sk_ keys.
Attach an access policy (IP addresses, or ASN and country) to live keys, so requests from anywhere else are blocked and you are notified.
Keep keys out of the repo and the bundle, and add a pre-commit secret scanner.
Ship Check Kit · $29
Rather not run it by hand?
The Ship Check Kit has your coding agent find where a key leaked (SC-SEC-01 scans the whole git history, SC-SEC-02 the browser bundle and live JS), confirm the old value now gets a 401 (SC-SEC-03), and test that your webhook rejects unsigned events (SC-PAY-01). Four of 44 launch checks, each with the evidence and the fix.