How do you keep API keys out of a vibe-coded app?
Keep secret API keys on a server and out of anything the browser downloads. Store them in environment variables or a secrets vault, list .env files in .gitignore, tell the assistant in your rules file never to hard-code keys, scan every commit for secrets, and rotate any key that has ever been pushed or shipped.
Cite
CodeLifter Forum, "How do you keep API keys out of a vibe-coded app?", https://codelifter.com/forum/posts/how-do-you-keep-api-keys-out-of-a-vibe-coded-app/, accessed 2026-11-12.- CodeLifter editorsStaffStaff, CodeLifter ·
Why is a key in front-end code public?
Front-end code is the JavaScript your site sends to every visitor's browser, and anyone can open it. Build tools copy some environment variables straight into that code, so "it was in an environment variable" does not mean it stayed private.
The Vite documentation (v8.3.4, read 16 October 2026) says variables prefixed with
VITE_"will be exposed in client-side source code after Vite bundling" and that "VITE_*variables should not contain sensitive information such as API keys. The values of these variables are bundled into your source code at build time." Next.js (page last updated 25 August 2026) does the same with theNEXT_PUBLIC_prefix: the value is inlined "into any JavaScript sent to the browser."This is the trap in vibe-coded apps. You ask for a page that calls an AI or payment API, the assistant makes the call from the browser because that is the shortest route, and the key goes out with the page. Simon Willison's post on vibe coding puts it plainly: if your code involves secrets, you need to understand how it works to avoid exposing them.
Which keys are safe to expose?
Some services issue two kinds of key on purpose: a public one for the browser and a secret one for the server. Stripe's API keys page (read 16 October 2026) is a clear example:
Key or variable Safe in the browser? Source Stripe publishable key pk_...Yes; it "can't perform sensitive operations such as creating charges" Stripe docs Stripe secret key sk_...No; "unrestricted permissions on all Stripe APIs" Stripe docs Stripe restricted key rk_...No; permissions you control, still server only Stripe docs Vite VITE_NAMEOnly if the value is not secret Vite docs Vite NAMEwithout the prefixNot exposed to client code Vite docs Next.js NEXT_PUBLIC_NAMEOnly if the value is not secret Next.js docs Next.js NAMEwithout the prefixServer only by default Next.js docs Most AI model APIs issue only secret keys. If the provider's docs do not say a key is safe to expose, treat it as secret.
How do you move the key to the server?
- Find every key. Search the project:
grep -rnE "sk_|api[_-]?key|secret|token|password" --exclude-dir=node_modules . - Create a server route. A small function on your host (a serverless function, which is code the host runs on request without you managing a server) receives the browser's request, adds the key, calls the API and returns only the result. In Next.js that is a Route Handler such as
app/api/route.js, which can readprocess.env.MY_KEY. - Put the key in the host's secret settings, or in a local
.envfile for development. Stripe's advice is to use a secrets vault where your platform offers one, and environment variables if not, and not to put keys "in source code or configuration files checked into version control." - Ignore the env files. Add
.envand.env*.localto.gitignore. Vite's docs say.env.*.localfiles "can contain sensitive variables" and should be ignored, and Next.js notes thatcreate-next-appadds.envfiles to.gitignorefor you. - Rebuild and check the bundle. Build the site, then search the output folder (
dist/or.next/) for the first characters of your key. No match is the result you want. - Limit the key. Give it the fewest permissions the job needs and a spending limit where the provider offers one, so a leak costs less.
What should you tell the assistant?
Put the rule where every session reads it, such as
AGENTS.md(see what goes in a rules file):## Secrets - Never write an API key, token or password in code, config or tests. - Call paid or secret-key APIs only from server code; the browser calls our own route. - Never use the VITE_ or NEXT_PUBLIC_ prefix for a secret. - Read secrets from environment variables and name the variable you need.Also keep secrets out of the folders an agent can read. An assistant connected through the Filesystem MCP server can open an
.envfile like any other; the Filesystem sandbox thread covers that.How do you catch a key before it is pushed?
- Push protection. GitHub's push protection docs say the feature "blocks pushes that contain secrets before they reach your repository." Push protection for users is on by default and "Stops you from pushing secrets to public repositories on GitHub." Repository-level push protection is off by default and needs GitHub Secret Protection.
- A local scanner. Gitleaks scans a repository's history with
gitleaks git -vand can run as a pre-commit hook. Its README says the project is now feature complete and will get security patches only. - Your own eyes. GitHub suggests
git diff --cachedto review exactly what a commit will contain, and avoidinggit add .so stray files are not swept in.
The CodeLifter desk article on vibe coding security has the wider routine.
Short version
- Anything in the browser bundle is public, including
VITE_andNEXT_PUBLIC_variables. - Call secret-key APIs from a server route; the browser calls your route.
- Keep keys in a vault or environment variables, and
.envfiles in.gitignore. - Put a no-secrets rule in AGENTS.md and turn on push protection or a scanner.
- If a key was ever pushed or shipped, rotate it.
- Find every key. Search the project:
Write something first.
Give people something to work with: at least 30 words on what happened and what you tried.
That is too long. Keep it under 6,000 characters.
Write the question as the title, 15 to 140 characters, no links.
Pick a category.
Add a name (2 to 40 characters, no links).
That email address does not look right.
That was quick. Read the thread, then try again.
The form expired. Reload the page and post again.
Something went wrong with the form. Reload and try again.
Please complete the check and post again.
Limit reached for now. Try again later.
This thread is closed to new replies.
This thread no longer accepts replies.
Something went wrong with the form. Reload and try again.
Post a reply or question first (name and email), then this browser can vote, edit and accept answers.
You cannot vote on your own post.
Only the author (within 30 days) or the forum team can do that.
That email already has an account. Use your sign-in link instead.
New accounts are paused for the moment. Try again later.
That was already posted.
2 answers
- CodeLifter editorsStaffStaff, CodeLifter ·
What should you do if you pushed an API key to GitHub? Revoke or rotate the key first, before anything else. GitHub's page on removing sensitive data says that once a secret is revoked or rotated "it can no longer be used for access, and that may be sufficient to solve your problem." Deleting the file in a new commit does not help, because the key stays in the history. Rewriting history is possible but costly. GitHub lists the side effects: changed commit hashes, broken diffs on closed pull requests, lost commit signatures, and a high risk that someone's old clone pushes the secret back. Its procedure uses
git-filter-repoversion 2.47 or later with the--sensitive-data-removalflag, followed bygit push --force --mirror origin. Copies in forks and other people's clones stay where they are, and GitHub Support will only purge cached views when rotation cannot fix the risk. Some providers make rotation painless: Stripe's docs say that when you rotate a key in the Dashboard, the old and new keys both work for up to 7 days while you switch over. Then check the provider's logs for calls you did not make. - CodeLifter editorsStaffStaff, CodeLifter ·
Is it safe to keep API keys in a .env file? It is the normal place for development keys, with three conditions. First, the file must not be committed. Add it to
.gitignorebefore you create it, and check withgit statusthat it does not appear. Vite's docs mark.env.localand.env.[mode].localas "ignored by git" in their file list, describe the.localfiles as able to "contain sensitive variables", and recommend adding*.localto.gitignore. Second, the variable must not carry a public prefix. In Vite, onlyVITE_variables reach client code;DB_PASSWORDin the docs' example comes backundefinedin the browser. Third, remember the file is plain text on your disk, readable by any program or agent with access to the folder. For production, Stripe's key guidance prefers a secrets vault from your hosting platform and treats environment variables as the fallback. Next.js also documents a load order worth knowing:process.envfirst, then.env.$(NODE_ENV).local,.env.local,.env.$(NODE_ENV)and.env, stopping at the first match. So a value set on your host overrides the one in your local file, which is what you want in production.