How to Keep API Keys Safe in AI-Generated Code
AI tools routinely hardcode API keys. Here's how to use .env files, .gitignore, and env variables to keep secrets safe in Cursor, Lovable, and Claude Code.
Caleb North is a fictional AI persona, not a real person. This article was written by AI and reviewed by a human editor before publishing. How we work →

Some links may be affiliate links. We may earn a commission at no extra cost to you.
If you've shipped an app using an AI tool and pushed it to GitHub, there's a real chance you've already leaked an API key. Not because you're careless — because the AI wrote code that looked right, ran fine locally, and silently included your secret in plain text.
GitGuardian's automated scanners, along with similar tools, constantly crawl public repositories looking for exposed credentials. The window between a push and a live exploit is measured in minutes, not days. This guide is the thing you should have read before that happened — and the three-file fix that makes it impossible going forward.
Why AI Tools Hardcode API Keys (and Why It's Not Your Fault)
How AI suggestions generate functional-but-insecure code
AI coding tools are trained to produce code that works. "Working" means the API call succeeds, the function returns a value, and no errors appear in your console. A hardcoded key achieves all of that instantly. Using an environment variable requires an extra setup step that the AI doesn't know you've completed.
When you prompt Cursor or with "add Stripe payments to my app," the model's goal is a runnable result. It defaults to the path of least friction: const stripe = require('stripe')('sk_live_abc123...'). That key is real, it works, and it's now in your codebase. The AI isn't being reckless — it just doesn't know what's in your .gitignore or whether you've set up a .env file yet.
The four-minute GitHub scanner — how fast leaked keys get abused
Bots continuously monitor GitHub for new commits containing credential patterns. According to GitGuardian, a leaked key can be harvested and used within minutes of the commit landing — in some cases under four minutes. By the time you notice unexpected charges or get a security alert, the damage is done. Rotating the key is the only fix — the commit history alone doesn't undo exposure.
The Fix: Three Files Every Project Needs
This works for any project — Next.js, plain Node, Vite, Python. The concept is identical; only the file name varies slightly.
Step 1: Create a .env file and add your keys there
Create a file called .env in the root of your project. For Next.js projects, prefer .env.local — Next.js loads it automatically and it's excluded from the framework's own defaults. For Vite projects, use .env. For plain Node, .env with the dotenv package works.
# .env (or .env.local for Next.js)
STRIPE_SECRET_KEY=sk_live_abc123...
OPENAI_API_KEY=sk-abc456...
Never commit this file. That's what Step 2 is for.
Step 2: Add .env to .gitignore (the step everyone misses)
Open your .gitignore file — or create one if it doesn't exist — and add the following lines:
# .gitignore
.env
.env.local
.env.*.local
If .gitignore didn't exist yet, save it at the root of your project alongside package.json. Git will now ignore your secret files before they ever touch a commit.
One catch: if you've already committed a .env file, adding it to .gitignore doesn't erase history. Jump to What to Do If You've Already Leaked a Key for the recovery steps.
Step 3: Reference the variable in your code, not the key itself
Instead of pasting the key directly into your code, read it from the environment:
// JavaScript / Node
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
# Python
import os
stripe.api_key = os.environ.get("STRIPE_SECRET_KEY")
When this code runs locally, it reads the value from your .env file (loaded via dotenv or the framework's built-in loader). When it runs in production, it reads from the environment variables you set in your hosting platform — which you'll never paste into a file.
Doing This in Cursor
is one of the most popular AI-powered editors for vibe coders. See how it compares to Claude Code in Cursor vs Claude Code for Beginners.
Telling Cursor to use environment variables in its suggestions
Cursor can be steered through a rules file in your project. The legacy .cursorrules file in your project root still works in normal mode, but as of 2026 Cursor's preferred format is .mdc files inside a .cursor/rules/ directory — and .cursorrules is silently ignored in Cursor's Agent mode. If you're using agentic workflows, create .cursor/rules/security.mdc instead:
# .cursor/rules/security.mdc
- Never hardcode API keys, tokens, or secrets in source files.
- Always use process.env.VARIABLE_NAME (JS/TS) or os.environ.get("VAR") (Python).
- Assume a .env file exists and is already gitignored.
This doesn't guarantee Cursor will never suggest a hardcoded value, but it dramatically reduces the frequency. Cursor reads these rules as context before generating suggestions in all modes.
Checking generated code before you run it
Before running any AI-generated code that touches a third-party service, do a quick visual scan for string literals that look like keys — long random-looking strings, anything prefixed with sk_, pk_, Bearer, or AIza. The review habit matters more than the tooling. For a broader approach to auditing AI output, see How to Fix AI-Generated Code.
Doing This in Lovable
is a no-code/low-code builder that generates full-stack apps from prompts. Read the Lovable review for a full breakdown of what it can and can't do.
Where Lovable stores secrets (and what it never touches)
Lovable doesn't write your API keys into the generated codebase. Instead, it has a dedicated secrets panel that keeps credentials separate from the code the AI generates. The generated code references environment variable names — it never sees the actual values.
To add a secret in Lovable, open the Cloud tab (click the + button next to the Preview panel), then click Secrets in the sidebar. Enter the variable name (e.g., STRIPE_SECRET_KEY) and its value, then click Save. Lovable encrypts the secret and injects it at runtime without exposing it in the code editor or exported files.
Connecting environment variables to your Lovable project
When you export a Lovable project and deploy it yourself, you'll need to set those same environment variables in your hosting platform. If you're deploying to Vercel, the deploy your first app with Vercel and Claude Code guide covers exactly where the Vercel environment variable dashboard lives and how to add variables per environment (development, preview, production).
Doing This in Claude Code
Claude Code is a terminal-based AI coding agent. Its key advantage for security is that you control the entire environment — it reads from your local shell and filesystem, so it naturally picks up .env files through whatever loader your project already uses.
Prompting Claude Code to write env-safe code from the start
The simplest approach: tell it once at the start of a session.
Use environment variables for all API keys and secrets.
Assume .env is gitignored and already set up.
Never output a real key value in code — only the variable reference.
Claude Code is good at following this consistently within a session. The problem is you have to remember to say it. That's what CLAUDE.md is for.
The CLAUDE.md rule that prevents hardcoded keys project-wide
CLAUDE.md is a file you place at the root of your project. Claude Code reads it automatically at the start of every session — no manual prompting required. Add a security section:
## Security Rules
- Never hardcode API keys, tokens, passwords, or secrets in any source file.
- Always reference secrets via environment variables: process.env.KEY (JS) or os.environ.get("KEY") (Python).
- Assume .env and .env.local are gitignored. Do not suggest creating or modifying these files.
- If a task requires a secret and no env var is defined, ask the user to set one — do not generate a placeholder value.
This rule persists across every future session in that project. See How to Write a CLAUDE.md for a full guide on structuring this file.
What to Do If You've Already Leaked a Key
Revoke it immediately — the only move that matters
Go to the provider's dashboard right now and revoke the key. Don't delete the commit first, don't push a fix first — revoke the key first. A revoked key is a useless key, regardless of where it appears in history.
- Stripe: Dashboard > Developers > API keys > Roll key
- OpenAI: Platform > API keys > Delete
- AWS: IAM > Security credentials > Deactivate / Delete
- Supabase: Project Settings > API > Regenerate
After revoking, check the provider's usage logs for the compromised key. Look for requests you didn't make.
Rotate, recommit, and audit your git history
Generate a new key from the provider dashboard. Add it to your .env file. Then add .env to .gitignore if it wasn't already, and commit that change.
The old key is gone from the codebase but still visible in git history. If this is a private repo, that may be acceptable — the key is revoked, so history exposure is low-risk. If it's a public repo or you want a clean history, you'll need to rewrite history using git filter-repo or BFG Repo Cleaner. The Git Survival Guide for Vibe Coders covers history rewriting if you need to go that route.
Quick Reference: The .env Checklist
| File | Purpose | What goes wrong without it |
|---|---|---|
| .env or .env.local | Stores your actual secret values locally | AI-generated code has no place to pull from — defaults to hardcoding |
| .gitignore | Tells git to never commit .env | Your keys go to GitHub; scanners find them in minutes |
| .cursor/rules/*.mdc / CLAUDE.md | Steers AI to write env-safe code by default | Every new AI suggestion risks introducing a hardcoded value |
| Environment variables in hosting | Provides secrets to your deployed app | Deployment fails, or you paste keys into source to compensate |
The three-file setup takes five minutes. Revoking a leaked key, auditing your usage logs, and cleaning your git history takes hours — and the financial exposure in between is real. Set it up before you push.
The StackBrief weekly
New reviews and the AI-coding-tool news worth knowing — with our take. One email a week, unsubscribe anytime.
Keep reading

Build a Chrome Extension With AI (No Code Experience)
Build a Chrome extension with AI — Claude Code or Cursor scaffolds the files, you side-load it in Chrome and ship your first real browser tool.
May 10, 2026
How to Fix AI-Generated Code When It Breaks (3 Moves)
AI-generated code broke your app? Here's the exact playbook: paste the error back in, roll back with git, and know when a fresh chat is your fastest fix.
May 10, 2026
Build Your First App With PokéAPI and AI
PokéAPI is a free, no-auth Pokémon data API — perfect for your first project. Here's how to use it with Claude or Cursor to build something from scratch.
March 15, 2026