Can Cursor read your .env file? Yes, and how to stop it
Out of the box, Cursor's agent can read your .env file. Cursor's docs list .env* among the files it ignores by default, but its staff explained on the forum that this default list only applies to indexing, not to the agent's file tools. Adding .env to .cursorignore closes that gap for the agent's own tools, Tab and @ mentions. It does not cover terminal commands that run outside the sandbox, MCP tools, or keys exported in your shell.
So the fix has two parts: tell Cursor what to ignore, and keep the real keys somewhere the agent can't reach in the first place. I build Keyfold, a vault for API keys, so I care about the second part. I read Cursor's docs and forum on 20 October 2026 for everything below; Cursor changes quickly, so check the linked pages if you read this much later.
What does Cursor send to the model?
Anything the agent reads can end up in a model request, and every request goes through Cursor's servers. Cursor's data use page says so plainly: "Even if you use your API key, your requests will still go through our backend! That's where we do our final prompt building."
Code search is local. Cursor's search docs say Instant Grep "builds and queries its index on your machine" and that Cursor doesn't store embeddings of your codebase for search. But the same page adds: "When Agent opens a match, that file content can still be included in the model request." A key in a file the agent opens travels with the request.
Privacy Mode is about what happens after that. With it on, Cursor says your data isn't used for training and its model providers have zero data retention agreements. That's useful, but it doesn't stop a file from being sent in the first place.
Does Cursor ignore .env files by default?
Not for the agent. Cursor's ignore file docs say "Cursor automatically ignores files in .gitignore and the default ignore list below", and .env* is on that list. In February 2026 a user reported that Cursor 2.5 read their .env file anyway. A Cursor staff member replied: ".env* is in the default ignore list, but that list only applies to indexing. It does not affect Agent tool calls (Read file) or tab completion." The same thread links the cause to a change in 2.5: "The global ignore list is now empty by default to fix sandboxing issues."
Another staff reply on Cursor's forum adds that files in .gitignore "are excluded from indexing but can still be seen and edited by the agent." So a .env file that git ignores is still one tool call away.
How do I block .env files with .cursorignore?
Create .cursorignore in the project root. It uses .gitignore syntax:
# .cursorignore
.env
.env.*
!.env.example
*.pem
*.keyThe !.env.example line keeps the example file readable, which is useful: it tells the agent which variables exist without giving it values. To cover every project at once, Cursor's docs suggest adding **/.env and **/.env.* to the global ignore list in your user settings. That list is empty until you fill it.
According to the docs, .cursorignore blocks the files from "Code accessible by Agent, Tab, and Inline Edit" and from "@ mention references". The same page then says what it doesn't block: "The terminal and MCP server tools used by Agent cannot block access to code governed by .cursorignore." Its own security note adds that "complete protection isn't guaranteed due to LLM unpredictability."
Can Cursor's agent still run cat .env?
Yes, in some modes. Whether a terminal command waits for you depends on your Run Mode (Settings > Agents > Approvals & Execution). Cursor's Run Modes docs describe three:
| Run Mode | What runs without asking | Can it read an ignored .env? |
|---|---|---|
| Auto-review (Cursor's recommended default) | Allowlisted calls; other shell commands inside the sandbox; a classifier reviews the rest | Not from inside the sandbox (see below); yes if a command runs outside it |
| Allowlist | Only what you allowlisted, optionally sandboxed | Only if you approve or allowlist the command |
| Run Everything | Every tool call | Yes |
The sandbox is the part that matters for .env files. In its write-up of the sandbox, Cursor says the sandbox policy is generated "along with the user's .cursorignore", and on Linux ignored files are made "completely inaccessible to the sandboxed process". That's why the table above gives the sandbox a partial mark for terminal commands: it should stop a sandboxed cat .env, but the ignore file docs still say terminal tools can't be blocked, and not every command runs inside it.
Commands can leave the sandbox. The Run Modes page says a command that needs more access goes to the classifier instead, and that "If a sandboxed command fails on a sandbox restriction, such as a permission error, the agent can rerun it outside the sandbox." It also says, in a heading of its own, that "Auto-review is not a security boundary".
In practice: never use Run Everything in a project that has keys in it, and read each approval prompt for a command that runs outside the sandbox. Look for cat, head, grep -r, printenv, env, and scripts you don't recognise.
Cursor also has hooks: a beforeShellExecution hook can refuse a command before it runs, and a beforeReadFile hook can refuse a file read. A hook that refuses commands mentioning .env is a speed bump, not a wall, because cat .e* or a small Node script gets past it.
Can Cursor see keys exported in my shell?
Yes. Commands the agent runs start from your shell, and Cursor's own terminal docs tell you to check for the CURSOR_AGENT variable in ~/.zshrc to change your prompt for agent sessions. That only works because the agent's terminal loads your shell profile. Anything you export there, such as export OPENAI_API_KEY=..., is in the environment of every command the agent runs, and printenv prints it. No ignore file helps, because there's no file to ignore.
There's a second gap outside the project. Under Read Access, Cursor's default is "System": "The agent can read files outside the workspace without approval." That includes ~/.zshrc, ~/.aws/credentials and any .env file in another project. Cursor 3.23 (1 October 2026) added a "Workspace" mode that asks before any read outside the project. Turn it on in Settings > Agents > Approvals & Execution > Read Access.
What about MCP servers?
Cursor's security page says every MCP connection needs your approval, and by default "each tool call still needs individual approval before running". An MCP allowlist or your Run Mode can change that. But .cursorignore doesn't apply to MCP tools, and whatever a tool returns goes into the conversation. A secrets server with a tool that returns a value puts that value in the chat. I wrote about what makes an MCP server safe for secrets in more detail.
The MCP config file is a leak of its own. Cursor's MCP docs show keys in an env block of mcp.json, and also support ${env:NAME} references and an envFile setting. A key pasted straight into .cursor/mcp.json is a file in your project that isn't on any ignore list, and it's easy to commit. GitGuardian's State of Secrets Sprawl 2026 counted 24,008 unique secrets in MCP configuration files on public GitHub.
How do I keep my keys out of Cursor's context?
Ignore rules reduce what the agent reads. They can't make a key that's sitting in your project safe. The reliable fix is to keep real keys out of the project folder and out of your shell, and hand them to one command at a time. This is the setup I recommend:
- Move real keys out of the project. Keep them in an encrypted vault by project and environment, and keep only
.env.examplein the repo. The guide to .env leaks covers moving them. - Add
.cursorignorewith the patterns above, and the same patterns in the global ignore list, for any.envfile that's still around. - Use Auto-review or Allowlist with the sandbox on. Not Run Everything.
- Set Read Access to Workspace, so files outside the project need your click.
- Delete
export SOME_KEY=...lines from~/.zshrcand~/.bashrc. - Give keys to commands, not to the shell. In Cursor's terminal, run the command through a tool that injects keys for that one process:
# Keyfold, from the local vault
$ keyfold run -p acme-api -e development -- npm test
# 1Password CLI, with secret references in an env file
$ op run --env-file=./dev.env -- npm testOnly npm test and the processes it starts get the keys. If you'd like the agent to run tests with keys on its own, give it an MCP server that hands keys to a process and never returns them. Keyfold's server works in Cursor: your Run Mode decides whether Cursor asks before the tool call, and Keyfold shows its own approval sheet before it hands any values to the process. No Keyfold tool returns a secret value; a command you let run can still print one.
Open the approval sheet in the live demoOpens the live demo, with made-up keys
- Rotate any key you see in the chat. Once it's been in a request, assume it's out of your hands. The leaked key guide has rotation links for 20 providers.
Keyfold's features include .env import, so step 1 is one import per project. The comparison table shows how other tools handle agents.
FAQ
Does .gitignore stop Cursor from reading my .env file?
No. Cursor's staff say files in .gitignore are kept out of the index but "can still be seen and edited by the agent." Use .cursorignore or the global ignore list as well.
Does Privacy Mode keep my .env file private?
It limits what happens to your data after a request: no training, and zero retention at the model providers. It doesn't stop the agent from reading a file and sending it with a request.
Is .cursorignore enough on its own?
For the agent's file tools, Tab and @ mentions, mostly yes. Cursor's docs say it can't block terminal or MCP tools, and that "complete protection isn't guaranteed". Pair it with the sandbox, Workspace read access, and keys that aren't in the project.
Can I still let Cursor's agent run tests that need API keys?
Yes. Run the tests through a command that injects keys for that process, or give the agent an MCP server that runs commands with keys injected and returns only the output. The key reaches the test process, not the chat.
What should I do if Cursor already read a key?
Rotate it. The key was part of a request that went through Cursor's servers to a model provider, and it may sit in your local chat history. Create a new key, update where it's used, then revoke the old one.