An API key is a bearer credential: whoever holds it can use it, and there is no second factor on an API call. On APIVerve, holding your key means spending your credits. That makes key handling most of your security posture, and the failures are boringly predictable. Keys end up in three places they should not: in source control, in browser code, and in logs.
This lesson closes each of those, in that order, and then covers the part people skip until it is too late: what to do when one gets out anyway.
Keys live in the environment
You have been doing this since lesson one: the code reads APIVERVE_API_KEY from the environment and fails at startup if it is missing. The point of an environment variable is that the value is supplied by wherever the code runs (your shell, a CI system, a hosting platform's settings page), not by the code itself. The same file runs in development and production with different keys and contains neither.
const KEY = process.env.APIVERVE_API_KEY;
if (!KEY) throw new Error('APIVERVE_API_KEY is not set');import os
KEY = os.environ["APIVERVE_API_KEY"] # KeyError at startup if unsetTyping export in every new terminal gets old, so most projects keep development values in a .env file and load it at startup.
APIVERVE_API_KEY=YOUR_API_KEY// npm install dotenv
import 'dotenv/config'; // loads .env into process.env
console.log(Boolean(process.env.APIVERVE_API_KEY));
// Node 20.6+ can skip the package: node --env-file=.env app.mjs# python -m pip install python-dotenv
from dotenv import load_dotenv
import os
load_dotenv() # loads .env into os.environ
print(bool(os.environ.get("APIVERVE_API_KEY"))).gitignore before the first commit
A .env file is only safe if it never enters version control. Add it to .gitignore before your first commit, not after you notice it in git status:
.env
.env.*
!.env.example
.dev.varsCommit a .env.example with the variable names and placeholder values instead, so the next person knows what to set.
Order matters because git history is permanent. If .env was committed once and deleted in the next commit, the key is still in the repository, in every clone, and in every fork. Rewriting history is possible but does not reach copies that already exist. Treat a committed key as a leaked key, even in a private repository: private repositories get shared, made public, cloned onto laptops, and read by every integration you have connected.
You committed .env with your key, noticed, and deleted it in the next commit. The repository is private. What is the situation?
Answer the check above to continue.
The proxy is now the thing to protect
Moving the key server-side does not make the endpoint private. Anyone can call https://yoursite.com/api/dictionary?word=x from a script. The allow-list caps what they can do; it does not stop them doing it a hundred thousand times. Checking the Origin header helps against other websites embedding your endpoint, but scripts can send any header they like, so it is not access control. Depending on what is behind the proxy, add one or more of: a per-IP or per-user rate limit, a requirement that the user is signed in to your app, and a cache so repeated questions cost nothing. APIVerve also lets you put a lower rate cap and endpoint scoping on the key the proxy uses, so a leak of that key is worth very little.
When a key leaks anyway
It will happen to someone on your team eventually: a screenshot in a bug report, a key pasted into a support chat, a .env in a public gist. What you do in the first ten minutes matters more than what you do afterwards.
- Rotate immediately. Rotation on APIVerve issues a new key and invalidates the old one instantly, with no grace period. For a planned rotation you roll the new key out first and rotate after. For a leak, invert that: rotate first and accept a few minutes of downtime. A leaked key is spending your credits for as long as it works.
- Update every place the key lives. Hosting settings, CI secrets,
wrangler secret put, teammates'.envfiles. This is where one key per environment pays off: you only touch the places that used the leaked one. - Look at usage. The per-API breakdown in the dashboard shows whether anything you do not recognise was called. An endpoint you never use appearing in analytics is the clearest sign someone else had the key.
- Remove the exposure. Delete the gist, edit the ticket, scrub the commit if the repository is public. This is cleanup, not containment: the rotation in step one is what actually stopped the damage.
The key rotation guide covers the zero-downtime pattern with overlapping sub-keys. Practise it once on a sub-key before you need it under pressure.
What you have now
Keys in environment variables that fail loudly when missing, a .gitignore that was right from the first commit, a proxy in Express or a Worker that forwards only what you allow, and a rotation plan you can run in minutes. The proxy also left you with a natural place to stop paying for the same answer twice, which is where the course goes next: Caching and credit budgets.