# How to securely store Stripe secret keys

- Tool: Stripe
- Difficulty: Beginner
- Time required: 10 minutes
- Compatibility: All platforms, Node.js 18+
- Last updated: March 2026

## TL;DR

Stripe secret keys (sk_live_ and sk_test_) grant full access to your Stripe account and must never appear in source code, frontend files, or version control. Store them in environment variables, use .gitignore to exclude .env files, rotate keys periodically, and use restricted keys for services that only need specific permissions. This guide covers all best practices with practical examples.

## Protecting Your Stripe API Keys

Your Stripe secret key grants complete access to your account — charges, refunds, customer data, everything. A leaked key can result in fraudulent charges, data theft, and account compromise. The solution is simple: never hardcode keys in source code. Use environment variables, restrict access with scoped keys, and rotate keys if you suspect exposure.

## Before you start

- A Stripe account with API keys (Dashboard → Developers → API keys)
- Node.js 18 or later installed
- Basic understanding of environment variables

## Step-by-step guide

### 1. Store keys in environment variables

Create a .env file in your project root to hold your Stripe keys. Your application reads them at runtime without exposing them in source code.

```
# .env file (DO NOT commit this file)
STRIPE_SECRET_KEY=sk_test_51ABC123...
STRIPE_PUBLISHABLE_KEY=pk_test_51ABC123...
STRIPE_WEBHOOK_SECRET=whsec_ABC123...
```

**Expected result:** Your keys are stored in a file that is excluded from version control.

### 2. Add .env to .gitignore

Ensure your .env file is never committed to Git. Add it to .gitignore before your first commit.

```
# .gitignore
.env
.env.local
.env.production
.env.*.local
node_modules/
```

**Expected result:** Git ignores .env files, preventing accidental commits of your secret keys.

### 3. Load environment variables in Node.js

Use the dotenv package to load your .env file in development. In production, set environment variables directly on your hosting platform.

```
// Install dotenv: npm install dotenv
require('dotenv').config();

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

// Verify key is loaded (never log the actual key)
if (!process.env.STRIPE_SECRET_KEY) {
  console.error('ERROR: STRIPE_SECRET_KEY not set');
  process.exit(1);
}

console.log('Stripe initialized with key ending in:', 
  process.env.STRIPE_SECRET_KEY.slice(-6));
```

**Expected result:** The Stripe SDK initializes with the key from the environment variable. The full key is never logged.

### 4. Create restricted API keys for limited access

If a service only needs to create charges, create a restricted key with only that permission. Go to Dashboard → Developers → API keys → Create restricted key.

```
// Using a restricted key that can only create charges
const stripeChargesOnly = require('stripe')(process.env.STRIPE_RESTRICTED_KEY);

// This works — charges permission is granted
async function createCharge() {
  const pi = await stripeChargesOnly.paymentIntents.create({
    amount: 5000,
    currency: 'usd',
    payment_method_types: ['card'],
  });
  return pi;
}

// This would fail — customer permission not granted
// const customers = await stripeChargesOnly.customers.list(); // Error!
```

**Expected result:** The restricted key can only perform authorized actions. Unauthorized operations throw a permissions error.

### 5. Rotate keys when compromised

If you suspect a key has been exposed, roll (rotate) it immediately in the Stripe Dashboard. Stripe lets you roll keys without downtime by keeping the old key active for a grace period.

```
// After rotating your key in the Dashboard:
// 1. Update the key in your environment variables / hosting platform
// 2. Restart your application to pick up the new key
// 3. Test that payments still work

// Verify the new key is active
async function verifyKey() {
  try {
    const balance = await stripe.balance.retrieve();
    console.log('Key is valid. Balance available:', balance.available);
  } catch (err) {
    console.error('Key verification failed:', err.message);
  }
}

verifyKey();
```

**Expected result:** The new key works, and the old key is invalidated after the grace period.

## Complete code example

File: `secure-stripe-config.js`

```javascript
require('dotenv').config();

// Validate required environment variables at startup
const REQUIRED_VARS = [
  'STRIPE_SECRET_KEY',
  'STRIPE_PUBLISHABLE_KEY',
  'STRIPE_WEBHOOK_SECRET',
];

const missing = REQUIRED_VARS.filter(v => !process.env[v]);
if (missing.length > 0) {
  console.error('Missing required environment variables:', missing.join(', '));
  console.error('Create a .env file or set them in your hosting platform.');
  process.exit(1);
}

// Warn if using live keys in development
if (process.env.NODE_ENV !== 'production' &&
    process.env.STRIPE_SECRET_KEY.startsWith('sk_live_')) {
  console.warn('WARNING: Using live Stripe keys in development!');
  console.warn('Switch to test keys (sk_test_) for development.');
}

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY, {
  apiVersion: '2024-12-18.acacia',
  maxNetworkRetries: 2,
});

module.exports = {
  stripe,
  PUBLISHABLE_KEY: process.env.STRIPE_PUBLISHABLE_KEY,
  WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET,
};
```

## Common mistakes

- **Hardcoding sk_live_ keys directly in source code** — undefined Fix: Always use environment variables. If keys are in code, rotate them immediately and move to env vars.
- **Committing .env files to Git** — undefined Fix: Add .env to .gitignore before the first commit. If already committed, remove from history with git filter-branch or BFG Repo-Cleaner, then rotate keys.
- **Using the same key for all services and environments** — undefined Fix: Use test keys (sk_test_) in development and restricted keys for services that do not need full access.
- **Logging the full API key in error messages or server logs** — undefined Fix: Only log the last 4-6 characters for identification. Never log the full key.

## Best practices

- Store all Stripe keys in environment variables, never in source code
- Add .env to .gitignore before your first commit
- Use restricted API keys for services that only need specific permissions
- Use test keys (sk_test_) in development and staging environments
- Rotate keys immediately if you suspect exposure
- Validate that required environment variables are set at application startup
- Warn loudly if live keys are detected in a development environment
- Use a secrets manager (AWS Secrets Manager, Google Secret Manager, Vault) in production

## Frequently asked questions

### What happens if my Stripe secret key is leaked?

Anyone with your secret key can create charges, issue refunds, access customer data, and more. Rotate the key immediately in Dashboard → Developers → API keys → Roll key.

### Can I use the same key for test and live mode?

No. Test keys (sk_test_) and live keys (sk_live_) are separate. Use test keys in development and live keys only in production.

### Is it safe to commit my publishable key (pk_test_)?

Publishable keys are designed to be used in client-side code and are not secret. However, it is cleaner to use environment variables for all keys to maintain consistency.

### How do restricted keys differ from regular secret keys?

Restricted keys have limited permissions that you define (e.g., only create charges, only read customers). Regular secret keys have full access to everything in your Stripe account.

### Should I use a secrets manager instead of .env files?

In production, yes. AWS Secrets Manager, Google Secret Manager, or HashiCorp Vault provide encryption, access control, and audit logging. For local development, .env files with dotenv are sufficient.

---

Source: https://www.rapidevelopers.com/stripe-guide/how-to-securely-store-stripe-secret-keys
© RapidDev — https://www.rapidevelopers.com/stripe-guide/how-to-securely-store-stripe-secret-keys
