Stripe API Key Security: Best Practices for 2026

Written by the Rafter Team

A leaked Stripe secret key is one of the most expensive mistakes a developer can make. A typical account-level live secret key starts with sk_live_, and Stripe's API accepts it on the first request with no second factor and no approval step. Whoever holds it can create charges, read and export customer data, and trigger payouts where the account allows it, until you rotate it.
This guide walks through the common Stripe key prefixes and behaviors, why secret keys are so dangerous when exposed, the best practices that keep them safe, and what to do the moment one leaks. The prefixes and behaviors below are verified against Stripe's API keys documentation.
Stripe Key Types and Formats
Stripe issues several kinds of API credentials, and the prefix identifies the common key family you are holding. To confirm a key's exact scope and permissions, you still check the Dashboard or the key's metadata, because organization-level keys all share the sk_org prefix regardless of their permission level. Getting these distinctions right is the foundation of Stripe key hygiene.
Publishable keys (pk_test_ / pk_live_)
Publishable keys identify your account in client-side code, such as Stripe.js or the mobile SDKs. They are designed to be exposed in a browser or app bundle, so they can only do safe things like tokenizing a card or confirming a payment that your server already set up. A publishable key starts with pk_test_ in test mode and pk_live_ in live mode.
Secret keys (sk_test_ / sk_live_)
Account-level secret keys are broadly privileged by default, with access across most of the Stripe API. A secret key can create charges, refund payments, read your full customer list, and manage payouts, though account-specific limitations can apply. The test-mode form starts with sk_test_ and the live-mode form starts with sk_live_. Organization-level secret keys use the sk_org prefix instead. These belong only on your server, and Stripe shows a live secret key once at creation, so you save it immediately or rotate to get a new one.
Restricted keys (rk_test_ / rk_live_)
Restricted API keys are secret keys with a permission scope you define yourself. You choose which resources the key can read or write, so a key built for issuing refunds never needs access to your customer list. They start with rk_test_ in test mode and rk_live_ in live mode, and Stripe recommends them over full-access secret keys for most integrations.
Webhook signing secrets (whsec_)
A webhook signing secret is not an API key, but it deserves the same care. Your endpoint uses it to verify that an incoming event genuinely came from Stripe rather than from an attacker forging requests. It starts with whsec_, and it should live in your server environment alongside your secret key.
Test mode versus live mode
Every key is bound to a mode. Test-mode keys (pk_test_, sk_test_, rk_test_) operate against simulated objects: card networks never move real money, and the customers and charges are simulated rather than real. Live-mode keys (pk_live_, sk_live_, rk_live_) process real payments against real customer data. Keeping the two cleanly separated is what stops a debugging session from touching a production account.
Why a Leaked Secret Key Is Dangerous
The danger of a secret key comes from how much it can do with how little friction. A valid sk_live_ key is a highly privileged bearer credential for your Stripe account.
With one exposed secret key, an attacker may be able to attempt unauthorized charges using saved payment methods where Stripe's rules and mandates allow, expose customer records and payment-method metadata, and, depending on account configuration and the key's permissions, attempt unauthorized payouts or transfers. They can also issue unauthorized refunds, though refunds go back to the original payment method rather than to an account the attacker controls, so the damage there is revenue loss and operational disruption rather than direct theft. None of this requires breaking anything; the API simply trusts the key.
This is also why the publishable-key nuance matters. A publishable key is genuinely safe to ship in client code, because it is scoped to operations that cannot move money or read sensitive data on their own. Finding a pk_live_ key in a public repository is not, by itself, a secret-key compromise. Finding an sk_live_ or rk_live_ key is.
Best Practices for Stripe Key Security
Strong Stripe key hygiene is a set of habits more than a single setting. The practices below compound, and most cost nothing but discipline.
Never commit keys to source control
Keep secret keys out of your codebase entirely. Load them from environment variables, store them in a local .env file for development, and add that file to .gitignore so it never reaches a commit. Hardcoding or committing a key, even temporarily, is a common way secret leaks happen, because a "temporary" key tends to survive in git history long after you forget about it.
Use restricted keys with least-privilege scopes
Default to restricted keys (rk_live_) and grant each one only the permissions it needs. A background job that issues refunds should not be able to read your customer list, and a reporting service should be read-only. Scoping keys this way means a single leaked credential exposes a fraction of your account rather than all of it.
Keep test and live strictly separated
Use test-mode keys (sk_test_) everywhere a real charge would be wrong: local development, automated tests, and staging. Reserve live keys for production. Separating the two prevents accidental real charges and makes a leaked key easier to triage, because the prefix tells you immediately whether real money is at risk.
Keep secret keys server-side only
Secret and restricted keys belong on your server, never in client-side JavaScript, mobile binaries, or a single-page app bundle. Anything shipped to a browser or device can be extracted. If your frontend needs to talk to Stripe, it uses the publishable key, and your server holds the secret key behind your own API.
Protect your webhook signing secret
Treat the whsec_ signing secret as a credential. Verify every incoming webhook signature against it before acting on the event, and store it the same way you store your secret key. Skipping verification lets an attacker forge events such as a fake "payment succeeded," and leaking the secret undermines the guarantee entirely.
Rotate keys on a schedule and after personnel changes
Rotate secret and restricted keys periodically, and immediately when a team member with access leaves. Stripe's rotation generates a replacement key and lets you expire the old one, either on a schedule or immediately, so you can roll a credential without downtime.
Monitor activity and turn on alerts
Watch your account for unexpected API activity. The Stripe Dashboard surfaces API request logs and event history, and it offers email notification settings for events like disputes and elevated-risk payments. Enable Stripe notifications where available, and use webhooks for failed-payment and suspicious-activity monitoring. Reviewing these regularly shortens the window between a compromise and your response.
What to Do If a Stripe Key Leaks
Speed is everything once a secret key is exposed. Researchers have observed exposed cloud keys on public repositories being used within minutes of a push, so treat a leaked key as already compromised and assume it is being tried.
First, roll the key in the Stripe Dashboard. Rotation revokes the exposed key and issues a replacement immediately; choosing to expire the old key right away deletes it so no further requests can use it. Update your environment variables and redeploy with the new key.
Next, review your Stripe logs and recent activity for anything you did not initiate: unexpected charges, refunds, payouts, or new restricted keys. If you find fraudulent activity, follow up on disputes and contact Stripe support.
Finally, remove the key from wherever it leaked. Deleting a key from the latest commit is not enough, because it remains in git history; you need to rotate the key regardless, and scrub the history if the repository is shared. For a full walkthrough, see the leaked API key emergency response guide.
Detection: Catching Stripe Keys Before Attackers Do
A common Stripe key leak path runs like this. A developer hardcodes sk_live_... while debugging, commits it, and pushes to a repository that is public or later becomes public. The key sits in git history, and automated scanners run by attackers find it.
Secret scanning closes that gap by matching the distinctive Stripe prefixes. Major secret scanners support Stripe key patterns, including secret, restricted, and webhook-signing secrets, and a stronger workflow catches them in more than one place: a pre-commit hook that blocks the key before it ever enters git history, a CI scan that catches anything a local hook missed, push protection where your host offers it, and periodic history scans. For a comparison of the scanners that handle this, see secret scanning tools compared and the broader API key leak detection tools roundup.
Rafter scans your repositories for exposed secrets, including Stripe keys, as part of a wider code security assessment. Detecting a sk_live_ key before it reaches a public branch is usually less disruptive than rotating it after exposure, and treating credential detection as one layer of a full scan means a leaked Stripe key surfaces alongside the rest of your security posture rather than in isolation.
FAQ
Is it safe to expose a Stripe publishable key?
Yes. Publishable keys (pk_test_ and pk_live_) are designed for client-side use and can only perform safe operations like tokenizing a card. Finding one in a frontend bundle or public repository is not a security incident. Only secret keys (sk_) and restricted keys (rk_) need to stay hidden.
What's the difference between sk_test_ and sk_live_?
Both are full-access secret keys, but sk_test_ operates in test mode against simulated data, where card networks never move real money, while sk_live_ operates in live mode against real payments and real customer data. A leaked sk_live_ key is an emergency; a leaked sk_test_ key cannot charge a real card, though you should still rotate it.
What do I do if my Stripe secret key leaked?
Roll the key in the Stripe Dashboard immediately, which revokes the exposed key and issues a replacement, then update your environment variables and redeploy. Review your Stripe logs for activity you did not initiate, and rotate the key even if you have already deleted the commit, because it survives in git history. The emergency response guide covers each step.
Should I use restricted keys?
For most integrations, yes. Restricted keys (rk_live_) let you grant only the permissions a given service needs, so a single leaked credential exposes a fraction of your account instead of all of it. Stripe recommends restricted keys over full-access secret keys, and they are the safer default for new work.
Related Resources
- Stripe API Keys: Test vs Live and Secure Use
- Secret Scanning in CI/CD: detect-secrets vs Betterleaks vs TruffleHog
- Top Tools for Detecting API Key Leaks
- You Leaked an API Key—Now What? Emergency Response Guide
- API Keys Explained: Secure Usage for Developers
Want to catch exposed Stripe keys before attackers do? Start a scan with Rafter to find leaked secrets across your repositories.