Brainfile › Guides › Keep API Keys Out of Claude Code
Claude Code · Secrets

Can Claude Code Read Your .env?
Yes. Here's How to Keep Keys Out

We tested it on 2026-10-11: a deny rule stops Claude's Read tool and cat, but not a one-line Python script. The fix that holds is keeping the real keys in a password manager. Every step is below.

Free by email: a practical Claude Code tip every Tuesday and Thursday, plus a Sunday recap.

Start here → Free 30-day trial
In this guide

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 triedNo ruleWith the deny rule
Its Read toolPrinted the tokenBlocked (“denied by your permission settings”)
cat .env in the shellPrinted the tokenBlocked
python3 -c "print(open('.env').read())"Printed the tokenPrinted the token
A deny rule is a seatbelt, not a vault

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:

.claude/settings.json
{
  "permissions": {
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)"
    ]
  }
}

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.

.env — references only (safe to read)
STRIPE_API_KEY="op://dev/stripe/restricted_key"
DATABASE_URL="op://dev/postgres/url"
Terminal
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:

Terminal — your own window, not inside Claude
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
Use a separate vault for project keys

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.

SetupWhat Claude can doSettings
Claude never touches keysEdits code; you start anything that needs a key yourselfDeny Bash(op *) and Bash(bw *)
Claude runs named commandsRuns your tests or dev server with keys injected, never sees the values in a fileAllow the exact wrapped command, such as Bash(op run --env-file=.env -- npm test)
.claude/settings.json — Claude never touches keys
{
  "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.

Where Brainfile fits

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.

Start the free 30-day trial →

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.

Not ready to start? Stay in the loop.

A practical Claude Code tip every Tuesday and Thursday, plus a Sunday recap of the week. Free.

Or start the 30-day free trial — no card required.