How to stop Claude Code reading your .env file

Stop Claude Code reading .env with Read(.env) deny rules and the sandbox's denyRead. Anthropic's docs say a .claudeignore file has no effect.

Contents

To stop Claude Code reading your .env file, add Read(.env) and Read(.env.*) to permissions.deny in ~/.claude/settings.json. Its Read tool and cat .env are then refused. A Python script that opens the file still gets through, which is what the sandbox's denyRead is for. A .claudeignore file does nothing.

Why Claude Code reads .env#

Nothing sinister. Ask it why npm run dev can't reach Postgres and it opens .env to look at DATABASE_URL, as you would. Every other line of the file comes along. The contents are then part of the conversation: sent to the model, kept in the session history, and sometimes repeated back in a reply.

In January 2026, The Register reported that Claude Code read .env files listed in .claudeignore and .gitignore, and that Claude, asked how to keep it away from a .env file, had said .claudeignore works like .gitignore. It doesn't. Anthropic's permissions docs now say it outright: "If your project has a .claudeignore file, it has no effect, so move its entries into Read deny rules."

Prompt injection is the second reason. A line in a README, an issue or a fetched page can tell the agent to cat .env and send the output somewhere. A key the agent can't read is a key it can't send.

Step 1: deny rules#

Add this to ~/.claude/settings.json so it covers every project:

json
{
  "permissions": {
    "deny": [
      "Read(.env)",
      "Read(.env.*)",
      "Read(**/secrets/**)",
      "Read(~/.ssh/**)",
      "Read(~/.aws/**)"
    ]
  }
}

A bare file name such as .env matches at any depth under the folder Claude Code started in, so Read(.env) also covers apps/api/.env. A Read deny rule also blocks Edit and Write on the same path. Run /permissions to check the rules loaded.

The docs list what the rules cover:

  • Claude's built-in file tools, with search tools and @file mentions on a best-effort basis
  • file commands Claude Code recognises in the shell, "such as cat, head, tail, sed, and tee"
  • redirects like < .env

And what they miss: "a command that reads files without naming them, such as grep -r pattern .", and "arbitrary subprocesses that read or write files indirectly, like a Python or Node script that opens files itself". Your app's own test run loads .env on purpose, and that's fine. A script written to dump it is the risk. The full syntax, with the ask list and wildcards, is in allow, ask and deny rules.

Step 2: the sandbox, for shell commands#

Claude Code's sandbox makes the operating system enforce limits on every shell command Claude runs and every process those commands start. It runs on macOS, Linux and WSL2 and is off by default. Turn it on with /sandbox, then deny reads of secrets:

json
{
  "sandbox": {
    "enabled": true,
    "filesystem": {
      "denyRead": ["~/**/.env"]
    },
    "credentials": {
      "files": [
        { "path": "~/.aws/credentials", "mode": "deny" },
        { "path": "~/.ssh", "mode": "deny" }
      ],
      "envVars": [
        { "name": "GITHUB_TOKEN", "mode": "deny" }
      ]
    }
  }
}

Two things to know from the sandbox docs. By default sandboxed commands can read "most of the machine, including credential files such as ~/.ssh and ~/.aws/credentials", so you have to list what to hide. And the sandbox covers shell commands only: the Read tool follows permission rules, not denyRead. You need both layers. The envVars entry unsets a variable for sandboxed commands, which matters because they otherwise inherit Claude Code's environment, secrets included.

Step 3: a hook that refuses edits#

Deny rules stop reads, not edits. Anthropic's docs ship a hook for edits, protect-files.sh. Save it as .claude/hooks/protect-files.sh and register it on PreToolUse with "matcher": "Edit|Write". An Edit or Write whose path contains .env, package-lock.json or .git/ then ends with exit code 2, and Claude gets the script's message, Blocked: <path> matches protected pattern '.env', and acts on it. The script is number 5 in our hooks examples, tested before we published it.

Step 4: keep real secrets out of reach#

A rule can be got round. A secret that isn't on the machine can't be read.

  1. Production keys go in a secret manager or your host's store (fly secrets set, wrangler secret put, GitHub Actions secrets) and load at deploy time. Never in the repo folder.
  2. The local .env gets development keys only: a test database, one bucket, a read-only role, short-lived where the provider allows it.
  3. Run env before you type claude. Every command the agent runs sees what it prints, AWS_SECRET_ACCESS_KEY too if it's set, so start the agent from a shell without production credentials.
  4. Rotate any key that was read into a session. Treat it as exposed.
  5. Unattended runs go in a container with nothing secret mounted. The setup, with Anthropic's init-firewall.sh, is in skipping permission prompts safely.

How Shuriken handles your keys, and what it can't do#

Shuriken keeps the AI sign-ins, API keys and MCP sign-ins you add as sources in your vault. They are encrypted on your device before they leave it, so our cloud only ever stores a sealed copy it can't open, and MCP sign-ins travel sealed between your own devices. The security page has the exact list of what our cloud can and cannot see, and the exceptions.

What a vault can't do is hide a key from an agent on the same computer. An agent runs there as you, and chats aren't sandboxed, so it can read what Shuriken keeps on that machine, and where the Shuriken app is signed in, that includes the key that opens your vault. We'd rather you hear that from us. So the rules above apply in Shuriken exactly as in a terminal: deny reads of secrets, use the sandbox, and run untrusted work on a machine or container that holds nothing you'd miss.

What Shuriken adds is a check on the way out, on by default. It scans the lines a branch adds against 12 rules taken from gitleaks' defaults: AWS, GitHub, GitLab, Anthropic, OpenAI, Slack, Stripe, Google and Linear keys, private key blocks, and secret = "…" assignments whose value looks random.

  • Finish refuses the branch: "The branch adds what looks like a secret; nothing was merged."
  • Create PR and Push in the app stop at Possible secrets found, with each hit masked to its first four and last two characters, and Fix with agent next to Create PR anyway or Push anyway.
  • An agent's own git push runs through a pre-push hook that Shuriken writes into repositories with no pre-push hook or core.hooksPath of their own. It stops with "Shuriken's secret scan stopped this push: these added lines look like secrets."
  • A false alarm goes under secretScan.allow in shuriken.json.

The scan catches a key pasted into code. It can't see a key the agent read and sent somewhere else, so it doesn't replace the steps above. To run your agents on computers and servers you control, download Shuriken.

Sources

  1. Configure permissions (Claude Code docs) (accessed 22 October 2026)
  2. Settings files and precedence (Claude Code docs) (accessed 22 October 2026)
  3. Configure the sandboxed Bash tool (Claude Code docs) (accessed 22 October 2026)
  4. Claude Code's prying AIs read off-limits secret files (The Register, 28 January 2026) (accessed 22 October 2026)

Frequently asked questions

Does .claudeignore stop Claude Code reading files?

No. Anthropic's permissions docs say a .claudeignore file has no effect and to move its entries into Read deny rules in settings.json.

What is the right deny rule for .env files?

Add Read(.env) and Read(.env.*) to permissions.deny. A bare file name matches at any depth under the project. For every project, put them in ~/.claude/settings.json.

Can Claude Code still read .env with a deny rule?

Through its file tools and common commands like cat, no. Through a script that opens the file itself, yes. The sandbox's denyRead closes that path for shell commands.

Should I keep API keys in .env at all?

Development keys, yes: a test database, one bucket, a read-only role. Production keys stay in a secret manager or your host's secret store, such as fly secrets or GitHub Actions secrets, where the agent can't reach them.

Free tools