Secrets management — where to actually store your API keys
Not in .env files. Not in localStorage. Here is the 2026 guide to storing and accessing secrets in a small-team Node.js / Python app.
Where you store secrets determines how badly a leak hurts. This guide covers the four tiers of secret storage, with recommendations by team size.
What it is
A secret is any credential whose compromise would lead to abuse — API keys, database passwords, signing secrets, private keys. 'Secrets management' is where you store them, how you access them, and who can rotate them.
Vulnerable example
// Tier 0: in code
const stripeKey = "sk_live_...";
// Tier 1: .env committed
// .env
STRIPE_SECRET_KEY=sk_live_...
// These both leak the moment git history goes public.Fixed example
// Tier 2: environment variables injected at runtime (Vercel, Fly, Railway)
const stripeKey = process.env.STRIPE_SECRET_KEY!;
// .env is in .gitignore; .env.local is in .gitignore; only .env.example is committed.
// Tier 3: secrets manager (1Password, Doppler, Vault)
import { getSecret } from "./secrets";
const stripeKey = await getSecret("STRIPE_SECRET_KEY");
// Tier 4: short-lived credentials via OIDC / Workload Identity
// No secrets checked in anywhere; your app assumes an identity and gets tokens.How Securie catches it
.env.local:12Secrets management
Securie's secrets specialist detects keys at Tier 0 and Tier 1 immediately. It also validates that your production deploy actually uses Tier 2+ by checking the live bundle for any pattern that matches a known secret shape.
// Tier 2: environment variables injected at runtime (Vercel, Fly, Railway)
const stripeKey = process.env.STRIPE_SECRET_KEY!;
// .env is in .gitignore; .env.local is in .gitignore; only .env.example is committed.
// Tier 3: secrets manager (1Password, Doppler, Vault)
import { getSecret } from "./secrets";
const stripeKey = await getSecret("STRIPE_SECRET_KEY");
// Tier 4: short-lived credentials via OIDC / Workload Identity
// No secrets checked in anywhere; your app assumes an identity and gets tokens.Checklist
- No secrets in source (Tier 0) — ever
- No secrets in committed .env files (Tier 1)
- Production uses injected env vars (Tier 2) at minimum
- Small teams on Tier 3 (Doppler / 1Password / Vercel env)
- Regulated / larger teams on Tier 4 (Vault + OIDC + short-lived credentials)
- Rotation automated (not 'we will rotate if someone leaves')
FAQ
Is .env.local safe?
If it is in .gitignore and you have confirmed it has never been committed (check `git log .env.local`). Safer is to never have .env.local at all on shared machines.
Related guides
Every week founders tweet about their OpenAI bill going from $10 to $10,000 overnight. Usually the cause is an API key committed to a public repo. Here is why it happens in Next.js specifically and how to stop it in five minutes.
Rotating an API key without taking your app down requires a specific dual-read single-write sequence. Here is the exact pattern.
The service-role key bypasses every RLS policy you wrote. It exists for a reason; it leaks for many reasons. Here is the rule for when to use it, the patterns that leak it, and the recovery playbook when it does.
Vercel environment variables have three flavors (development, preview, production) and two scopes (server-only and NEXT_PUBLIC_). Mixing them up leaks production secrets. Here is the rule and the canonical bugs.