Practical guides2 min read
How to store API keys securely in a web application
Where API keys should and should not live in a web application: server-side storage, browser limits, build-time pitfalls, and what to do when a key is exposed.
By the Keel team
The rule that covers most cases: an API key that must stay secret belongs on a server you control, loaded from outside your source code, and never sent to the browser.
Decide whether the key can be public
Some keys are designed to be public, such as a Stripe publishable key or a map tile token restricted to your domain. Others grant full account access. Read the provider's documentation. If a key is public by design, restrict it by domain or scope. If it is not, it never goes in client code.
Keep secret keys on the server
Call third-party APIs from your backend, and have the browser call your backend. In a framework with server routes, that looks like this:
// app/api/send/route.ts (runs on the server only)
export async function POST(req: Request) {
const apiKey = process.env.EMAIL_API_KEY;
if (!apiKey) return new Response("Not configured", { status: 500 });
// ...call the provider with apiKey, never return it
return Response.json({ ok: true });
}Load keys from the environment, not the code
- Never hard-code a key in source files, even in a private repository.
- Do not commit
.envfiles. Commit a.env.examplewith empty values instead. - Use a different key per environment so a leaked development key cannot touch production.
- Avoid baking keys into container images through
ENVor build arguments. They remain readable in the image layers.
Watch the public-prefix trap
Frameworks inline variables with certain prefixes, such as NEXT_PUBLIC_ or VITE_, into the JavaScript sent to users. If you put a secret behind one of those prefixes to make an error go away, you have published it. Check the built output when in doubt.
Store the real values somewhere with access control
At some point the value has to live somewhere other than your own head. A shared file or chat thread has no access control or history. A dedicated store gives you encryption at rest, role-based access, and an audit trail. That is what Keel's secrets management provides.
Limit what each key can do
Create keys with the narrowest scope the provider allows: read-only where possible, restricted to the resources and IP ranges that need it. A leaked narrow key is an incident. A leaked broad key is a breach.
If a key is exposed
- Revoke or rotate it at the provider first.
- Update the new value wherever it is used.
- Review the provider's usage logs for activity you do not recognise.
- Then clean the source of the leak. See how to keep secrets out of Git repositories.
Related reading
- How to keep secrets out of Git repositoriesPrevent API keys and passwords from reaching Git with ignore rules, scanners, and push protection, and learn the correct cleanup order if one gets committed.
- Environment variables vs. secrets: what is the difference?Environment variables are a way to deliver configuration. Secrets are a kind of value that needs protection. Here is how the two overlap and why it matters.
- Common secrets management mistakes in software developmentThe mistakes that most often lead to leaked credentials, from committed .env files to shared production keys, and what to do instead.
- Secrets managementOne encrypted place for API keys, tokens, and credentials, organised by project and environment.
- Version historyNumbered versions for every change, with restore to any earlier value.