Example finding How it works Coverage PENTEST METHODOLOGY DOCS PRICING FAQ MCP CONTACT LOG IN SIGN UP →

How to stop Claude Code from reading your .env file

FIX / CLAUDE CODE SECRETS / 9 MIN READ

To stop Claude Code from reading your .env file, add a Read deny rule for .env files to .claude/settings.json, then turn on the sandbox so that shell commands cannot open the file either. A deny rule alone does not cover every way to read a file. Use both layers, and keep production secrets out of the local file.

A CLAUDE.md instruction such as “never read .env” is not a block. The model can ignore it. A permission rule is a block, because Claude Code enforces it and not the model. The Claude Code permissions documentation says so directly: instructions in your prompt or CLAUDE.md “shape what Claude tries to do, but they don’t change what Claude Code allows.”

Why does Claude Code read my .env file?

Claude Code reads files in your working directory to do its job. File reads inside the working directory need no approval. A .env file sits in the project root, so Claude Code can open it when it looks for configuration, tries to fix a failing connection, or runs a search across the project.

This is a problem because the content of the file becomes part of the conversation. The conversation goes to the model provider. The values can also appear in the terminal output, in session transcripts on your disk, and in any log that records the session. A secret that left your computer in this way is no longer private to you.

The .env file is also an easy target for text in a file that tells the model what to do. A README or a code comment can tell the model to print the file. For this reason, you must block the read with a rule and not with a request. The guide on indirect prompt injection explains how this attack works.

How do I check if Claude Code already read the file?

Check before you change the settings, because the check tells you if you must rotate a secret.

  1. Read the session in the terminal. Scroll up in the Claude Code session. Look for a Read call on a .env file or a cat .env command.
  2. Search the transcripts on your disk. Claude Code keeps session data under ~/.claude/. Search it for the file name:
grep -rl "\.env" ~/.claude/projects 2>/dev/null | head
  1. Look at the permission rules that exist now. Run /permissions in a session. The dialog lists all rules and the settings.json file that each rule comes from.
  2. Check the other .env files. A project can have .env.local, .env.production, and .env.development. A deny rule for one name does not cover the others.
  3. Check the other places that hold secrets. Look at ~/.aws/credentials, ~/.ssh, and any config.json that holds a token. Claude Code can read these files too, unless you block them.

If the session read a file that holds a live key, treat the key as exposed. Go to the fix section.

Warning: Do not paste the content of your .env file into a chat, a ticket, or an issue to ask for help. Show the variable names only.

What do I do first? Fix it now, in order of urgency

Do these steps in this order. Steps 1 and 2 reduce the risk of the keys that were already read. Steps 3 to 6 stop the next read.

  1. Rotate the secrets that Claude Code read. Create a new key in the dashboard of each service. Then delete the old key. Start with the keys that can spend money or change data, such as payment keys, AI provider keys, database passwords, and the Supabase service_role key. The guide on the service_role key shows what to check after a leak of that key.
  2. Replace production secrets in the local file. The local .env file must hold development values only. Use a test payment key, a development database, and a key with low rights.
  3. Add the deny rules. Open or create .claude/settings.json in the project root. Add this block:
{
  "permissions": {
    "deny": [
      "Read(.env)",
      "Read(.env.*)",
      "Read(**/.env)",
      "Read(**/.env.*)",
      "Read(~/.aws/**)",
      "Read(~/.ssh/**)"
    ]
  }
}

The docs state that a bare file name follows gitignore rules and matches at any depth, so Read(.env) and Read(**/.env) are equivalent. The rule Read(.env) blocks any .env file at or under the current directory. It does not block a .env file in a parent directory or in another project. For a rule that covers all projects, add Read(//**/.env) to your user settings in ~/.claude/settings.json. The // prefix anchors the rule at the root of the file system.

  1. Check that the rule loads. Run /permissions and look for the new rules. Then ask Claude Code to show the first line of .env. The read must fail with a message that names the rule.
  2. Delete any .claudeignore file that you rely on. The permissions documentation says that if your project has a .claudeignore file, it has no effect. Move its entries into Read deny rules.
  3. Turn on the sandbox. See the next section.

Rules are checked in this order: deny, then ask, then allow. The first match decides the result. An allow rule cannot make an exception to a deny rule, so a broad allow rule cannot undo your block.

Does a Read deny rule block every way to read the file?

No. You must know where the rule stops.

The docs state that Read and Edit deny rules apply to three things. The first is the built-in file tools. The second is file commands that Claude Code recognizes in Bash, such as cat, head, tail, sed, and tee. The third is the targets of Bash redirections such as < file.

The rules do not apply to a command that reads files without naming them. The docs give grep -r pattern . run from the directory that holds the file as an example. The rules also do not apply to a subprocess that reads a file on its own, such as a Python or Node script that opens the file itself.

This is a real gap. If Claude Code writes python -c "print(open('.env').read())" and you approve it, the deny rule does not stop it. The same holds for a command that you allowed with a broad rule such as Bash(python *).

The docs also say that a Bash deny rule matches the command text that Claude writes. It does not match the same program in a different form, so a deny rule “isn’t a security boundary around the program.” For a block that holds, use the sandbox, which the operating system enforces.

Method Covers Does not cover
Read deny rule The Read tool and recognized file commands such as cat Scripts that open the file themselves
Sandbox denyRead Shell commands and their child processes The built-in Read tool
PreToolUse hook Any tool call that your script checks Reads that your script does not detect
No secret in the file Everything Nothing

The last row is the strongest control. A secret that is not on the disk cannot be read.

How do I block shell commands from reading .env?

Turn on the sandbox and add a deny rule for the file. The sandboxing documentation says that the sandbox is off by default. It uses Seatbelt on macOS and bubblewrap and socat on Linux. It works on WSL2. Native Windows is not supported, and Claude Code runs commands unsandboxed there.

Run /sandbox in a session, or add this to ~/.claude/settings.json:

{
  "sandbox": {
    "enabled": true,
    "failIfUnavailable": true,
    "filesystem": {
      "denyRead": ["~/**/.env"]
    }
  }
}

The docs show denyRead with a ~/**/.env pattern as a way to keep every .env file under your home directory blocked. The setting failIfUnavailable makes Claude Code exit at startup if the sandbox cannot start. Without it, Claude Code runs commands unsandboxed when the sandbox cannot start.

Caution: This rule also blocks a command that Claude Code starts and that needs the file, for example npm run dev with a dotenv loader. Start your dev server in your own terminal. A program that you start yourself is not inside the sandbox of Claude Code.

The sandbox has a limit too. The docs say that it wraps only shell commands and their child processes. The built-in Read, Edit, Write, WebFetch, and WebSearch tools are not covered. A denyRead entry does not stop the Read tool. This is why you need both the Read deny rule and the sandbox rule.

Can a hook block the read?

Yes. A PreToolUse hook runs before a tool call, and it can block the call. A script receives the tool input on stdin as JSON. For a Bash call, the command is in tool_input.command. If the script exits with code 2 and writes a reason to stderr, Claude Code blocks the call and shows the reason to Claude. The hooks guide has an example that blocks edits to protected files such as .env.

A simple hook for Bash looks like this:

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -qE '\.env|printenv|(^|[ ;|])env($|[ ;|])'; then
  echo "Blocked: command may read secrets" >&2
  exit 2
fi
exit 0

Register it in .claude/settings.json under hooks.PreToolUse with the matcher Bash. The docs say that an exit code of 0 does not approve the call. The call still goes through the normal permission flow.

Keep your expectations low. A text match on the command is easy to get around, for example with a variable or an encoded string. The hook catches the normal case and not an attack. It is a third layer and not a replacement for the first two.

How do I prevent secrets from reaching the model in the first place?

The best fix is to not hold the secret in a file in the project. Use these steps:

  1. Use development values locally. The local file holds keys that can do little harm. A leak of a test key costs nothing.
  2. Keep production secrets in the host. Set them in the environment settings of Vercel, Netlify, Supabase, or your host. Do not copy them to your laptop. The guide on environment variables security explains build-time and runtime variables.
  3. Give each key the lowest rights. A key that can only read one table does less harm than an admin key.
  4. Set a spend limit on every paid key. A limit caps the cost if a key leaks.
  5. Commit a .env.example file. It holds the variable names and no values. Claude Code can read this file, and it knows which variables the project needs.
  6. Keep .env out of git. Add .env* to .gitignore, except .env.example.
  7. Put the deny rules in the project file. Commit .claude/settings.json so that each person on the team has the same rules. The docs say that you can check permission settings into version control.
  8. Use managed settings for a team. The docs describe managed policies for an organization. A rule in managed settings cannot be changed by one developer.

The guide to securing Claude Code lists more settings for the tool.

What else can read my secrets?

Check the other tools that can see the project folder. A rule for Claude Code does not change what other tools do.

  • Other AI tools. Each tool has its own ignore and permission settings. Set them one by one.
  • MCP servers. A server with file access can read the same file. Review each server, and remove the ones that you do not use.
  • Build output. A variable with a public prefix, such as VITE_ or NEXT_PUBLIC_, goes into the browser bundle. Anyone can read it from the live site. See the article on an exposed OpenAI key for the check.
  • Git history. A .env file that you committed once stays readable in the old commits. Rotate the keys in it, and then remove the file from the history.

When should I get help?

Get help in these cases:

  • The .env file held production database credentials or payment keys, and the session ran on a shared or work computer. Rotate all of them, and ask a security engineer to review the access logs of each service.
  • You see charges or data access that you did not cause. Contact the support team of the service with the dates and the key name.
  • The file was committed to a public repository. Delete the keys first. Then remove the file from the history, and treat each old copy as public.
  • You use Claude Code in a company. Ask the administrator about managed settings, so that the deny rules apply to everyone.

After you finish, run one test. Ask Claude Code to print the first line of .env, and confirm that Claude Code refuses.

Common questions

Does a .claudeignore file stop Claude Code from reading .env?
No. The Claude Code docs say that a .claudeignore file has no effect. Move its entries into Read deny rules in settings.json.
Is a Read deny rule enough to protect my secrets?
No. The rule covers the built-in file tools and the file commands that Claude Code recognizes. A script that opens the file itself can still read it. Add the sandbox.
Does a rule in CLAUDE.md work as a block?
No. The docs say that instructions in a prompt or CLAUDE.md shape what Claude tries to do, but they do not change what Claude Code allows.
Do I need to rotate a key that Claude Code read?
Yes, if you do not accept that the key was sent to the model provider as part of the session. Rotate the key and move it out of the local file.
Will the deny rule break my dev server?
A Read deny rule does not stop a program that you start yourself. A sandbox deny rule can stop a command that Claude Code starts, so run the dev server in your own terminal.

Find secrets in your app

A VibeEval scan loads your deployed app and checks the files that the browser downloads for secret keys, so a leaked value does not stay hidden.

14-day free trial · No credit card · Cancel anytime

SCAN YOUR APP →