Exposed .env files and debug endpoints
A public .env, a /api/debug route, or a client bundle that prints config hands your secrets to anyone who asks.
Last updated
Shows up in: Next.js, Vercel, Supabase
What it is
Three different mistakes get lumped together because the result is the same.
A file named .env is fetched from the website because it was copied into public/ or deployed as a static file. The browser can request / .env or /env.
A debug route an agent added, such as /api/debug or /api/health?verbose=1, returns process.env so the author could see why a key was undefined. That route is a directory of secrets.
A log line prints the config object on startup, and the host's log viewer is shared more widely than production.
Next.js exposes variables whose names start with NEXT_PUBLIC_ to the browser. That is documented framework behavior, not a leak by itself. Putting a secret behind that prefix is the leak. See secret keys in the frontend.
Source maps make your client code easier to read. They do not, by themselves, reveal server environment variables. They do reveal a secret you inlined into client code.
How to check in 5 minutes
Request
/.env,/.env.local,/.env.production, and/envon your deployed site. You want a 404, not a file.Search routes for
process.env,JSON.stringify(process.env), and path namesdebug,test, andhealth. A health check may return{ ok: true }. It should not return config.Confirm
public/does not contain.envor a service-account JSON file. Everything inpublic/is a URL.Search client components for
console.logof session objects, keys, or full user records. Browser logs are visible to anyone using that browser, and to extensions.
How to fix
Next.js
Delete debug routes or guard them so they cannot run in production. Prefer deleting them. A health check returns status only.
Do not put .env in public/. Keep real env files gitignored. See keys committed to git.
If a secret was reachable, rotate it. Removing the route does not un-copy the value.
Vercel
Set environment variables in the project settings, scoped to the environments that need them. Do not commit them for "convenience." If the host offers a way to mark a variable sensitive so the dashboard stops showing the value, use it. Confirm the current control in Vercel's environment variable docs rather than assuming a button label.
Turn off verbose logging of request bodies on auth and webhook routes.
Supabase
The project URL and publishable key are not secret. The secret key, database password, and service role JWT are. The database password in a committed .env is a direct Postgres login. Rotate it in the dashboard if it leaked, not only the API key.
Firebase
A downloaded service account JSON in the repo or in public/ is a server credential. Rotate it in Google Cloud credentials if it was exposed. The Firebase web config object is different and is expected in client apps.
Copy-paste prompt
Paste this into Cursor, Claude Code, or whichever agent is editing the repo. Read the diff before you merge it.
You are removing debug exposure. Do not add features.
1. Find routes and pages that print process.env, config objects, or secrets. Delete them or make them return only { ok: true } with no secrets.
2. Make sure public/ and static export folders do not contain .env files or service account JSON.
3. Remove console.log calls that print tokens, keys, passwords, or full session objects.
4. Do not rename a NEXT_PUBLIC_ secret and leave the old value in git. Tell me which values I must rotate.
Do not print any secret in your summary. List files you deleted or changed.Questions
Our /api/health returns the git SHA. Is that a secret?
A SHA is not a password. It does tell an attacker which commit you run, which matters if that commit has a known bug. Returning { ok: true } is enough for an uptime check.
The .env file 404s. Are we done?
Check git history and old deployments too. A file that 404s today may have been public yesterday. If it ever contained a live key, rotate the key.