← Back to Blog

guide

Oct 15, 2026

.claudeignore doesn't stop Claude Code reading your .env. Here's what does.

.claudeignore doesn't stop Claude Code reading .env files. See what deny rules cover, what they miss, and how hook redaction keeps secrets local.

A .env file is where a project usually keeps secrets: API keys, database passwords, and tokens. .claudeignore looks like .gitignore, so it is reasonable to list .env there and assume Claude Code will skip it.

It will not. Anthropic's permissions documentation says a .claudeignore file has no effect. To block Claude Code from reading an .env file you use a Read deny rule. That rule covers more than people expect, and it still leaves a few paths open. This guide shows the test, the rule, and those gaps, using fake secrets only.

TL;DR

  • .claudeignore does not stop Claude Code from reading .env.
  • A Read deny rule blocks Claude's own file tools. It also blocks recognized shell commands such as cat .env when the command names that file.
  • It does not block a script that opens the file itself, or a search that never names the file.
  • Text that does get read can be saved in a plaintext transcript on the laptop.
  • A local hook can replace recognized secrets with placeholders before the model request. Matching is exact text. An env-file read that cannot be redacted safely is blocked. A removed hook checks nothing.

The permissions page was checked on 22 October 2026. Claude Code changes this behavior between versions. After you add a rule, try it on the version you actually have installed.


The 60-second test: .claudeignore vs a direct read

Use an empty folder and fake values. Do not point Claude Code at a real project for this test.

mkdir claude-test && cd claude-test
echo "OPENAI_API_KEY=sk-test-FAKE-abcd1234efgh5678" > .env
echo "DATABASE_URL=postgres://user:fake_password@localhost:5432/app" >> .env
printf '%s\n' '.env' '.env.*' > .claudeignore

Start Claude Code in that folder and ask: "What database connection string is in .env?"

You should see it open .env and repeat the fake values. There is no warning that .claudeignore was ignored.

The Register reported this on Claude Code v2.1.12 in January 2026. If your installed version does something else, trust the session in front of you and the current permissions page.

Why this happens

Git reads .gitignore. Docker reads .dockerignore. Claude Code does not read .claudeignore. The file can sit in the repo and do nothing.

That is the same class of mistake as treating Cursor's ignore file as a security control. See Why .cursorignore is not DLP.


How to block Claude Code from reading an .env file

Anthropic's documented control is a deny rule in .claude/settings.json at the project root, or in your user settings at ~/.claude/settings.json.

{
  "permissions": {
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)",
      "Read(**/*.pem)",
      "Read(**/*.key)"
    ]
  }
}

With that file in place, Claude's Read tool should refuse a matching path. Current docs also say a Read deny rule applies to shell commands Claude Code recognizes, including cat, head, tail, sed, and tee, when the denied path is an argument, and to redirections such as < .env. The same rule blocks Edit on that path, and it blocks Write on Claude Code v2.1.228 or later. Notebook edit needs a separate Edit(...) rule. Confirm cat .env on the version you have installed. The refusal text comes from Claude Code.

Path patterns are easy to get wrong

In these rules, // means "from the root of the disk." Read(//Users/alice/secrets/**) is an absolute path. A pattern with a single leading slash is anchored to the settings file that contains it, not to the disk root. If a file still opens, check which form you used before assuming the feature is broken. The full pattern table is on the permissions page.

One rule for the whole team

For a team, put the same JSON in a managed settings file. Project and user settings cannot override a managed deny rule.

  • macOS: /Library/Application Support/ClaudeCode/managed-settings.json
  • Linux and WSL: /etc/claude-code/managed-settings.json
  • Windows: C:\Program Files\ClaudeCode\managed-settings.json

The filename is managed-settings.json, not settings.json. Delivery options, including MDM and server-managed settings, are in Deploy managed settings. Someone with admin rights on the laptop can still remove other software. Managed settings control Claude Code. They are not a lock on the whole machine.


What a Read deny rule still does not block

Three gaps remain even when cat .env is denied.

A search that never names the file. Anthropic's docs say a command such as grep -r API_KEY ., run from the project directory, can still print a denied file. The rule looks for the denied path in the command. A search of . does not name .env.

A program that opens the file itself. A Python or Node script that reads .env, or a call such as load_dotenv(), is not one of the recognized file commands. The deny rule does not follow the script into the file it opens. For a block that applies to every process, Anthropic points you to the Claude Code sandbox, which is a separate setting.

Text already in the environment. printenv and echo "$DATABASE_URL" are not file reads. A deny rule on .env does not hide variables that are already set.

Instructions in CLAUDE.md do not close these gaps. CLAUDE.md is guidance for the model. It is not a permission check. GitHub issue anthropics/claude-code#59094 is a closed report of a shell pipeline printing live keys despite instructions not to read .env. Treat it as a past incident, not as a description of today's cat behavior.

For how shell output shows up in alerts, see Detecting AI agent secret exfiltration (AD-004).


Where the secret goes after a read

Two copies matter.

The first is the model request. If the secret was in the tool output, it was sent to the model provider as part of the prompt.

The second is the laptop. Claude Code writes the conversation to a plaintext file under ~/.claude/projects/. The folder name is your project path with punctuation replaced by dashes, not a hash. The session file is <session>.jsonl. Anthropic's .claude directory guide says these transcripts are not encrypted. Operating-system file permissions are the only local protection.

cleanupPeriodDays deletes transcripts older than the number of days you set. The default is 30. The minimum is 1. Setting it to 0 is rejected. CLAUDE_CODE_SKIP_PROMPT_HISTORY skips writing both transcripts and prompt history. That limits the next session. It does not remove a secret the model already received.

Deleting the local transcript is still worth doing. Rotating the key is the part that matters.


Claude Code DLP: redact at the hook, before the model call

A hook is a small program the coding assistant runs at a set moment. Claude Code's hooks reference includes three that matter here:

  • PreToolUse runs before a tool executes.
  • UserPromptSubmit runs when you send a prompt.
  • PostToolUse runs after a tool returns, before that output is added to the next model request.

ReclaimLLM registers those hooks with rclm-hooks-install. Data loss prevention (DLP) is on by default. --no-dlp turns it off.

On Claude Code, the hook looks for env files such as .env, *.env, and .envrc, and for command output that matches a secret shape (a known key prefix, a private key, a database URL with a password, or a long high-entropy token). It replaces the secret value with a placeholder such as [REDACTED:API_KEY]. The variable name can stay, so the assistant still knows the setting exists. A direct read of an env file hides the values in that file, including ordinary non-secret lines.

This is exact text replacement. It does not read the sentence and decide that something is sensitive. A secret in an unusual format can pass through.

Two failure cases are different. If DLP can read the env file, the tool continues with the values replaced. If it cannot safely do that — the file cannot be read, or a single env file is over 1 MB, or the env files in the workspace together are over 8 MB — that env-file tool call is blocked instead of sending the raw file. An unexpected error while inspecting some other tool does not block that tool. A hook that has been deleted does not block anything. That last case is why you also want a coverage check.

To keep a whole folder out of ReclaimLLM's uploaded session copy:

rclm-hooks-install --exclude-folder ~/work/classified-engine

That skips upload for sessions under that folder. It does not stop Claude Code from reading the files locally.

For sessions you do store, paid and enterprise plans can encrypt the stored transcript. The recovery key is shown once for download. It is not emailed, and the plaintext key is not stored. Stored data regions are the US and the EU. Encryption protects ReclaimLLM's copy. It does not undo a prompt that already went to the model provider. Setup is in the sensitive data docs and the privacy controls page. A broader checklist is in Prevent API keys and .env files from reaching AI coding tools.


Know when a sensitive file was touched

Deny rules and redaction are things you turn on. You also want a record when they were off.

AD-004 is ReclaimLLM's detector for sensitive-file access (patterns such as .env, credential files, and *.pem). An immediate email is reserved for the serious cases: a critical finding where DLP did not cover the access, or a high finding for an unauthorized env write. Repeated alerts for the same event are held for 4 hours unless the severity goes up. Access that was already redacted is more likely to show up in the daily digest than as an instant page.

The email includes the detector name, severity, a file-pattern category such as .env, how it was accessed (direct tool, shell read, or a script mention), and whether DLP had redacted it. It does not include the secret, the prompt, the full shell command, or source code. Open the finding in the console for the session link.

The Files view marks sensitive paths and shows who read or edited them, on which client and session. That is for review after the fact. It is not a block. See the audit log, file lineage, and the Signals walkthrough.


Already leaked a key? Do these five things

  1. Rotate the key at the provider. If it entered a model prompt, treat it as exposed. Deleting a chat does not pull it back.
  2. Remove the local transcript. Find the session under ~/.claude/projects/ and delete that .jsonl file, or shorten cleanupPeriodDays so old transcripts do not linger. The default retention is 30 days.
  3. See which sessions touched the file. In ReclaimLLM, use the Files view and the audit log for that date range.
  4. Add the deny rule, then install hook DLP with the commands in the next section. DLP is on unless you pass --no-dlp.
  5. Stop storing long-lived secrets in the repo directory when you can. Inject them at runtime, or use a secrets manager. Anthropic's secure deployment notes list credential files to keep out of agent context.

A 10-minute Claude Code setup

| Control | What it does | What it does not do | |---|---|---| | .claudeignore | Nothing in Claude Code | File tools, shell, transcripts | | Read deny rules | Blocks the Read tool and recognized commands that name the file, such as cat .env | Scripts that open the file, searches that do not name it, values already in the environment, plaintext transcripts | | Managed settings | Stops project settings from overriding that deny | Other programs on the laptop | | Hook DLP | Replaces recognized secrets with placeholders such as [REDACTED:API_KEY]. Blocks an env-file read it cannot safely redact (unreadable file, or over the 1 MB / 8 MB limits) | Secrets that do not match. A deleted hook. Unexpected errors on tools that are not env-file reads | | --exclude-folder | Skips upload of sessions from that folder | Local reads by Claude Code | | AD-004 | Emails on serious uncovered access. The message has no secret in it | The read itself | | Encrypted session storage | Encrypts the copy ReclaimLLM stores, on paid and enterprise plans | The prompt already sent to the model provider |

Install

macOS and Linux:

curl -fsSL https://reclaimllm.com/install.sh | bash

The script installs the rclm CLI and runs rclm-hooks-install. DLP is already on. If a new terminal cannot find the command, run rclm-hooks-install once after opening it. You can also install with pipx install rclm or pip install rclm, then run rclm-hooks-install.

Hook details: native hooks. For the team rollout, see Claude Code security.


Frequently asked questions

Does .claudeignore stop Claude Code from reading .env files?

No. Claude Code does not use .claudeignore. Anthropic's permissions docs say to move those entries into Read deny rules.

How do I block Claude Code from reading an .env file?

Add Read(./.env) and Read(./.env.*) under permissions.deny in .claude/settings.json. For a team, put that rule in managed-settings.json. To replace secrets that still appear in tool output, install hook DLP with rclm-hooks-install.

Do Read deny rules stop cat .env?

On current Claude Code, they are supposed to. The docs include cat, head, tail, sed, and tee when the command names the denied file. They do not cover a script that opens the file itself, or a search that does not name it, such as grep -r API_KEY .. Try cat .env after you add the rule, on the version you run.

Where does Claude Code store transcripts, and are secrets in them?

In plaintext files at ~/.claude/projects/<project>/<session>.jsonl. A secret that was printed in tool output is in that file. Transcripts are not encrypted at rest. cleanupPeriodDays defaults to 30 days.

What should I do if Claude Code already read my API keys?

Rotate the key at the provider, delete the local session transcript, check which sessions touched the file, then add a Read deny rule and hook DLP.

Can I enforce this for a whole team?

Yes for Claude Code's own tools: a managed deny rule cannot be overridden by project settings. Hook DLP is a separate install on each machine. To see whether those hooks are still present later, measure coverage rather than assuming the install stuck. That is a different problem, covered in Is your AI DLP still installed?.