Blog · October 23, 2026 · Updated October 24, 2026 · 8 min read

Secrets in a Cursor app: .env, .cursorignore and git history

Cursor's agent can read more than you think, and AI code puts keys where browsers see them. A checklist for .env, .cursorignore and git history.

Cursor is different from Lovable or Bolt. It does not host your app for you. It edits real files on your own computer, in your own git repository, and you deploy them wherever you like. That is great for control, but it also means every place a secret can leak is now your job: the .env file, the code that reaches the browser, your git history, your deploy settings, and the AI agent itself.

This guide walks through those five places in order. Each step takes a few minutes, and none of them needs security experience.

What counts as a secret

A secret is anything that lets someone act as you or as your app. In a typical Cursor-built app, that means:

Some keys are designed to be public. The Supabase anon or publishable key, the Stripe publishable key and a Firebase web config are meant to live in the browser. They are safe only if the rules behind them are right (Row Level Security, Firebase rules), which is a different check covered in our Supabase guide.

1. Know what the Cursor agent can read

Most people assume that because .env is in .gitignore, Cursor's AI does not look at it. That is only partly true.

Cursor's ignore file documentation says files in .gitignore, plus a default list that includes .env*, are ignored. But Cursor staff have explained on the official forum that this default list applies to indexing, not to what the agent can open when it decides to read a file. The file that actually blocks the agent, Tab and Inline Edit from reading something is .cursorignore.

Create a .cursorignore file in the root of your project:

.env
.env.*
**/credentials.json
**/*.pem
**/*.key

Or add the same patterns once for every project under Cursor's global ignore list in settings (patterns such as **/.env and **/.env.*). The docs say the global list is empty by default.

Two honest limits, both from Cursor's own docs:

The practical rule: keep development keys in your local .env, and keep production keys only in your hosting provider's settings. If the agent ever sees a dev key, the damage is small. If it never touches a production key, the damage is zero.

2. Check the MCP config file

If you connected Cursor to tools like Supabase, GitHub or Stripe through MCP, there is a config file at .cursor/mcp.json in your project (or ~/.cursor/mcp.json for all projects). It is easy to paste a token straight into it:

{
  "mcpServers": {
    "my-server": {
      "command": "npx",
      "args": ["-y", "some-mcp-server"],
      "env": { "API_KEY": "paste-real-key-here" }
    }
  }
}

The problem is .cursor/mcp.json lives inside your repository, so the next git add . commits the token. Cursor's MCP docs support reading values from your environment instead:

"env": { "API_KEY": "${env:MY_SERVICE_KEY}" }

For stdio servers you can also point envFile at a local file that is in .gitignore. Cursor's docs also recommend restricted API keys with only the permissions the server needs. A read-only database token for an MCP server is much safer than an admin key.

Quick check:

git ls-files .cursor
git log --all --oneline -- .cursor/mcp.json

If mcp.json is tracked and contains a real token, move the token out and rotate it (step 5).

3. Make sure no secret reaches the browser

This is the most common leak in AI-built apps, and it is the one strangers find first, because they only need to open your site.

Frameworks decide what goes to the browser by the variable name:

When Cursor hits an "undefined" error because client code cannot see a variable, a common quick fix is to rename OPENAI_API_KEY to NEXT_PUBLIC_OPENAI_API_KEY. The error disappears and the key is now public.

Search your project:

grep -rnE "NEXT_PUBLIC_|VITE_" --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" --include="*.env*" . | grep -v node_modules

For every match, ask: would I be fine posting this value on social media? The Supabase URL and anon key: yes. Anything with "secret", "service", "private" or an AI provider name: no.

Then check the built output, which is what visitors actually download:

npm run build
grep -rnoE "(sk-|sk_live_|rk_live_|AKIA)[A-Za-z0-9_-]{10,}" .next/static dist 2>/dev/null

No output is good. Any output means a real key is shipped to every visitor. Our earlier post, your API key is probably sitting in your JavaScript, explains how to move that call to a server route.

You can paste this into Cursor:

Find every environment variable starting with NEXT_PUBLIC_ or VITE_. List any that hold a secret (AI keys, payment secret keys, database admin keys, email keys). For each one, remove the public prefix and move the code that uses it into a server-only route or server action. The browser should call that route and never see the key.

4. Search your git history, not just your files

Deleting a key from a file does not delete it from git. Every old commit is still in the repository, and if the repository is on GitHub, every old commit is pushed there too.

Check whether an env file was ever committed:

git log --all --oneline -- .env .env.local .env.production

If this prints anything, the contents of that file exist in your history, even if the file is gone today.

For a broader search, run Gitleaks, a free open-source scanner that reads your whole git history:

gitleaks git -v --redact

The --redact flag hides the secret values in the output, which matters if you are sharing your screen. Gitleaks can also run as a pre-commit hook, so a key gets caught before the commit even happens.

On GitHub, two features help:

Push protection only stops new pushes. It does not clean up what is already in your history.

5. If you find a leaked key: rotate first, clean up second

The instinct is to rewrite git history to make the key disappear. Do that later, if at all. A key that was pushed or shipped to browsers must be treated as known to strangers, so the only real fix is to make it useless:

  1. Create a new key in the provider's dashboard (OpenAI, Stripe, Supabase and so on).
  2. Put the new key in your hosting provider's environment settings (Vercel, Netlify, Railway, Fly), not in a committed file.
  3. Redeploy, so the running app uses the new key.
  4. Revoke or delete the old key. Until you do this, the leak is still live.
  5. Check usage and billing for the old key's lifetime. Unexpected AI usage or new Stripe objects are the usual signs.

For Supabase, rotating the secret or service role key matters most, because that key ignores Row Level Security entirely. For Stripe, roll the secret key and the webhook signing secret if either leaked.

After rotation, you can remove the file from history with a tool such as git filter-repo if you want a clean repository, but the rotation is what actually protects you.

6. Keep production keys out of your laptop

The safest setup for a small team or solo builder looks like this:

The five-minute version

If you only do five things today:

  1. Add .env and .env.* to .cursorignore.
  2. Move any token in .cursor/mcp.json to ${env:...}.
  3. Search for NEXT_PUBLIC_ and VITE_ variables that hold secrets.
  4. Run git log --all -- .env and gitleaks git -v --redact.
  5. Rotate anything that shows up.

For the wider Cursor picture (API routes, CORS, headers), see our short guide on how to secure an app built with Cursor. To check a config file or a built bundle without uploading it anywhere, paste it into the free secret checker, which runs in your browser.

Check it for free

Greenlight Scan looks at your live app the way a stranger would: it reads the JavaScript your site ships, looks for keys that should never be there, and checks whether your database answers questions it should not. You get a traffic-light verdict in about a minute. Run the free scan to see whether anything is exposed. If you want every finding explained with a fix prompt written for Cursor, the $19 Full Scan goes through it all in detail.

Newsletter

The Friday leak report

One short email a week: the newest ways AI-built apps leak data, and the one-line fix for each. No spam, unsubscribe any time.