Skip to content
apikey.tools
Guide

Where to Store API Keys Securely — Frontend, .env and Secrets Managers

Why API keys can never be hidden in frontend code, how to use .env files without leaking them, and when to move to a secrets manager — with concrete patterns for React, Next.js, Vite, Docker, Kubernetes and CI.

By Abdessamad Bettal · Updated · 5 min read

The most common API key question has an uncomfortable answer: there is no way to hide a secret in code that runs on someone else's computer. Everything else in this guide follows from that.

The frontend cannot keep a secret

Anything you ship to a browser or a mobile app can be read by the person using it. Environment variables do not change this — build tools copy their values into the JavaScript bundle as plain strings, where anyone can find them in the browser's developer tools.

Every framework marks this boundary with a prefix:

FrameworkExposed to the browserServer only
Next.jsNEXT_PUBLIC_ variableseverything else
ViteVITE_ variableseverything else
Create React AppREACT_APP_ variablesnot available — no server
Expo / React NativeEXPO_PUBLIC_ variablesnot available — no server

If a variable has a public prefix, treat its value as published. Obfuscating it, splitting it into pieces or Base64-encoding it slows nobody down — the string deobfuscator unwraps most of those tricks in one click.

What to do instead: a backend proxy

Keep the secret on a server you control and let the browser call that server:

typescript
// app/api/summarise/route.ts — runs on the server, never in the browser
export async function POST(request: Request) {
  const { text } = await request.json();

  const response = await fetch("https://api.example-ai.com/v1/complete", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.AI_API_KEY}`, // no NEXT_PUBLIC_ prefix
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ prompt: text }),
  });

  return new Response(response.body, { status: response.status });
}

The browser calls /api/summarise; only the server knows the key. Add your own authentication and rate limiting to the route, or you have built a free public proxy for your API quota.

Keys that are meant to be public

Some providers issue keys designed for the browser: Stripe publishable keys (pk_live_), Firebase web config, Google Maps keys and Algolia search-only keys. They are safe in the frontend because their power is limited — restrict them by domain or bundle ID and by API in the provider's console. Never confuse them with the secret key from the same provider. The test vs live checker and the API key detector identify which kind you have.

.env files: fine, with three rules

For server code, environment variables are the standard way to keep secrets out of source. A .env file is a convenient way to set them in development.

  1. Never commit it. Add .env, .env.local and .env.*.local to .gitignore before the file exists. The gitignore generator produces a complete set of rules.
  2. Commit a template instead. A .env.example with every variable name and a placeholder value documents what is needed without exposing anything.
  3. Use different secrets per environment. Development, staging and production should never share keys, so a leaked laptop does not mean a leaked production.
bash
# .env.example — committed
DATABASE_URL=postgres://user:password@localhost:5432/app
STRIPE_SECRET_KEY=sk_test_replace_me
SESSION_SECRET=generate-a-32-byte-random-value

The .env file generator fills a template with freshly generated random secrets of the right length. When you need to share a .env for debugging, run it through the environment variable redactor first.

When to move to a secrets manager

.env files stop scaling when several people and services need the same secrets, when you need to rotate them, or when an auditor asks who accessed what. A secrets manager — AWS Secrets Manager, Google Secret Manager, Azure Key Vault, HashiCorp Vault, Doppler or Infisical — adds:

  • Access control per service and per person.
  • An audit log of every read.
  • Rotation, sometimes automatic, without redeploying code.
  • Encryption at rest with keys you do not have to manage.

Your application fetches secrets at startup or receives them as environment variables injected by the platform. The secrets manager formatter converts a set of variables into the JSON payloads AWS Secrets Manager and Vault expect.

Containers and Kubernetes

  • Docker: pass secrets at runtime with --env-file or Docker secrets. Never use ENV or ARG for secrets in a Dockerfile — both are recorded in the image history, which anyone who pulls the image can read.
  • Kubernetes: Secret objects are Base64-*encoded*, not encrypted. Enable encryption at rest for etcd, restrict who can read Secrets with RBAC, and consider the External Secrets Operator to sync from a real secrets manager. The Kubernetes secret generator produces a correctly encoded manifest without a stray trailing newline.

CI/CD pipelines

Store secrets in the CI platform's encrypted secret store (GitHub Actions secrets, GitLab CI variables), never in the workflow file. Better still, let CI authenticate to your cloud with OIDC federation, which issues short-lived credentials per job, so there is no long-lived cloud key to steal at all. Make sure secrets are masked in logs — and never echo them for debugging.

Mobile and desktop apps

Native apps have the same problem as the browser: the binary can be decompiled. Use a backend proxy for third-party secrets. For your own API, authenticate the *user* (OAuth with PKCE) rather than embedding an app-wide key. Store the resulting user tokens in the iOS Keychain or Android Keystore, never in plain preferences.

Frequently asked questions

How do I hide an API key in React?

You cannot hide it in React code — anything in the bundle is visible to users. Move the call that needs the key to a server: a Next.js route handler, a serverless function or your existing backend. The React app calls that endpoint, and only the server holds the key.

Are environment variables secure?

They are a reasonable way to deliver secrets to server processes, and much better than hardcoding. They are not a security boundary on their own: anything with access to the process can read them, they can leak into crash reports and debug logs, and a .env file on disk is plain text. For production at scale, a secrets manager that injects them is stronger.

Is it safe to commit an encrypted .env file?

It can be, with tools like SOPS or git-crypt, as long as the decryption key is managed separately and not committed. It moves the problem rather than removing it: now the decryption key must be protected, rotated and shared carefully.

How often should I rotate API keys?

Rotate immediately after any suspected exposure and whenever someone with access leaves. On a schedule, 90 days is a common baseline for long-lived keys, but making rotation easy matters more than the exact interval. The key rotation planner builds a schedule with overlap windows.

Try it in your browser

Every tool referenced above runs locally — nothing you paste is uploaded.

More guides