Yes, Claude Code can read your .env file
Claude Code reads files in your project without asking, and a .env file is a file. Anything in it, from a Stripe key to a database password, can end up in the conversation. That is not a bug; it is how a coding agent works. The question is what you put in its way.
We tested the common answer, a deny rule, on Claude Code version 2.1.296 on 2026-10-11. Two identical folders each held a dummy .env with a made-up token. One folder had a deny rule; one did not. Then we asked Claude to show the file three ways in each folder:
| What Claude tried | No rule | With the deny rule |
|---|---|---|
| Its Read tool | Printed the token | Blocked (“denied by your permission settings”) |
cat .env in the shell | Printed the token | Blocked |
python3 -c "print(open('.env').read())" | Printed the token | Printed the token |
This matches Anthropic's own permissions documentation: Read and Edit deny rules cover Claude's file tools and shell commands it recognises, such as cat, head and tail, but “They don't apply to ... arbitrary subprocesses that read or write files indirectly, like a Python or Node script that opens files itself.” The only way to be sure Claude cannot read a secret is for the secret not to be in the folder.
Add the deny rules anyway
They stop the common, accidental reads, which is most of the risk. Put this in .claude/settings.json at the root of your project and commit it, so everyone who opens the project gets the same rules:
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
]
}
}.claudeignoredoes nothing. The docs say that if your project has one, “it has no effect, so move its entries intoReaddeny rules.”.gitignoreis not a permission. It keeps the file out of git. It does not stop Claude reading it.- Bare names match at any depth. Per the docs,
Read(.env)andRead(**/.env)are equivalent and cover every.envunder the project folder.
Move the real values into a password manager
The durable fix is a .env that holds references, not values. The real key lives in your password manager and is handed to your app only while it runs. Two common ways to do it:
Option A: 1Password CLI
1Password's CLI documentation describes a .env written as secret references, run through op run, with “the secrets made available as environment variables only for the duration of the process.” If Claude reads this file, it learns where your key lives, not what it is.
STRIPE_API_KEY="op://dev/stripe/restricted_key" DATABASE_URL="op://dev/postgres/url"
op run --env-file=.env -- npm run dev
Option B: Bitwarden CLI (free)
Bitwarden's CLI documentation says unlocking the vault creates a session key, and that “Session keys are valid until invalidated using bw lock or bw logout.” Do the unlock in your own terminal, so your master password is typed into Bitwarden and never into a chat:
bw login # once export BW_SESSION="$(bw unlock --raw)" # asks for your master password; --raw prints only the session key # start your app with the key injected for that one process STRIPE_API_KEY="$(bw get password 'Stripe restricted key')" npm run dev bw lock # when you are done
While a Bitwarden session is unlocked, any program running as you, Claude included, can use it to read any item in that vault. Keep project API keys in a separate free account that holds nothing else, and keep your personal logins somewhere Claude's terminal never unlocks.
Decide whether Claude may run commands that need keys
There are two honest setups. Pick one and write it down in settings, not just in CLAUDE.md, because a sentence in CLAUDE.md is a request and a settings rule is a block.
| Setup | What Claude can do | Settings |
|---|---|---|
| Claude never touches keys | Edits code; you start anything that needs a key yourself | Deny Bash(op *) and Bash(bw *) |
| Claude runs named commands | Runs your tests or dev server with keys injected, never sees the values in a file | Allow the exact wrapped command, such as Bash(op run --env-file=.env -- npm test) |
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Bash(op *)",
"Bash(bw *)"
]
}
}The docs are clear that a shell rule matches the command as written: a deny rule “doesn't match the same program by path or inside sh -c.” For a guarantee that holds against any command, Claude Code's sandbox enforces file and network limits at the operating-system level.
Rotate anything that sat in a plain .env
If a real key has been in a plain .env in a folder you opened with Claude Code, assume it may have been read, and rotate it once the steps above are in place. It takes a few minutes in most dashboards, and it is the only step that fixes the past rather than the future.
- Use restricted keys. Most payment and cloud providers let you create a key that can only do what the project needs. A leaked read-only key is a much smaller problem.
- Use test keys on your laptop. Keep live keys for the server that needs them.
- Check your history. If
.envwas ever committed, the value is in git history even after you delete the file. Rotate it.
This guide is free and complete. Brainfile is the memory around it.
Nothing on this page is part of Brainfile. The settings and commands are yours to copy, and they work without buying anything.
Brainfile is a set of files that gives Claude Code a working memory of your project and your rules, so you stop re-explaining yourself every session. The free 30-day trial pulls the Basic set into your project: the Autopilot protocol, 10 pattern files and the weekly freshness feed. No card to start, and the files you pull stay yours.
Frequently Asked Questions
Can Claude Code read .env files?
Yes. Claude Code reads files in your project, and a .env file is a file. A Read deny rule in .claude/settings.json blocks its file tools and commands like cat, but not a script that opens the file itself. In our test on Claude Code 2.1.296, a one-line Python command still printed the file.
Does .claudeignore stop Claude Code reading a file?
No. Anthropic's permissions documentation says a .claudeignore file has no effect and that its entries should move into Read deny rules in settings.
How do I keep API keys away from Claude Code?
Keep the real values out of the project folder. Store them in a password manager and inject them only when a command runs, for example with 1Password's op run and secret references, or with the Bitwarden CLI. Add Read deny rules for .env files as a second layer.
Will Claude see my password manager's master password?
Not if you unlock the vault in your own terminal window. Type the master password into the password manager's prompt, never into the Claude chat.
Should I rotate keys that were in my .env?
If a real key sat in a plain .env in a folder you used with Claude Code, rotate it after moving to references. Rotation is the only step that covers a read that may already have happened.