Claude Code API Key and .env: What It Reads, Tested

On this page
  1. Which credential Claude Code uses
  2. Where Claude Code keeps your login
  3. Does Claude Code read .env? Our canary test
  4. A setup that holds
  5. If a key leaks
  6. How we tested

A developer starts Claude Code in a new repository and asks it to wire up the database. Claude reads the project to understand it, .env included, and the database password is now in the conversation. Nothing broke. That is what a coding agent does with the files you give it.

We tested what Claude Code does with keys and .env files on 10 November 2026: which credential it picks when you have both a subscription and an API key, where your login lives, and how far a Read(./.env) deny rule really reaches. We put a canary value in .env and asked Claude Code to read it eight ways.

Short answer: if ANTHROPIC_API_KEY is set in the environment Claude Code starts in, it is used instead of your subscription, even when you are logged in. Claude Code reads .env like any other file unless you deny it: add Read(./.env) and Read(./.env.*) to permissions.deny. In our test that stopped Claude’s own file tools and shell commands that name the file, but not a Python one-liner that opened .env itself. For a hard block, use the sandbox; better still, keep secrets out of plaintext files Claude can reach.

Which credential Claude Code uses

Claude Code accepts several kinds of credentials. When more than one is present, Anthropic documents this order:

  1. Cloud provider credentials, when CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX or CLAUDE_CODE_USE_FOUNDRY is set.
  2. ANTHROPIC_AUTH_TOKEN, sent as a bearer token, for an LLM gateway or proxy.
  3. ANTHROPIC_API_KEY, sent as the X-Api-Key header, “for direct Anthropic API access with a key from the Claude Console”.
  4. The output of an apiKeyHelper script, “for dynamic or rotating credentials”.
  5. Your subscription login from /login.

The third line is the one that surprises people. Anthropic’s environment variable reference says the key “is used instead of your Claude Pro, Max, Ultra, Team, or Enterprise subscription even if you are logged in”. Interactive sessions ask you once and remember the answer; in non-interactive mode (claude -p) “the key is always used when present”.

So check where ANTHROPIC_API_KEY comes from. A shell profile, a direnv file or a project .env that something exports into your shell all count. If you meant to use your subscription, run unset ANTHROPIC_API_KEY before you start Claude Code. In an interactive session you can also change the choice later with the “Use custom API key” toggle in /config, which only appears while the variable is set.

Getting an API key

API keys are created on the API keys page of the Claude Console (platform.claude.com/settings/keys). Anthropic’s docs point there and nowhere else. A “Claude API key” sold on a gift-card marketplace, which Google shows on the first page for this search, does not come from your own Console account: you cannot see its usage or revoke it, so do not build on one.

CI without an API key

For pipelines, claude setup-token prints “a one-year OAuth token” that “authenticates with your Claude subscription”. Anthropic is explicit that the command “does not save the token anywhere”: you copy it into your CI secret store as CLAUDE_CODE_OAUTH_TOKEN. It is still a credential. Treat it like a key.

Where Claude Code keeps your login

Your /login session is a credential too. From Anthropic’s authentication page:

SystemWhere the login is stored
macOSThe encrypted macOS Keychain (falls back to the file below if the Keychain is locked)
Linux~/.claude/.credentials.json, file mode 0600
Windows%USERPROFILE%\.claude\.credentials.json, protected by your user profile’s access controls

If CLAUDE_CONFIG_DIR is set, the file lives there instead. Two practical rules follow: never commit a .claude folder from your home directory, and do not copy it into a container image or a shared backup.

Does Claude Code read .env? Our canary test

We made two folders, each with a .env holding CANARY_TOKEN=canary-7f3a9e-<folder> and a second variable. The second folder also had .claude/settings.json with the deny rules from Anthropic’s own settings example:

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

Then we ran Claude Code 2.1.322 headless (claude -p, Haiku, only the folder’s settings loaded) and asked for the canary eight times. Shell commands were allowed one at a time with --allowedTools, so the only thing standing between Claude and the file was the deny rule.

Table titled What a Read(./.env) deny rule stopped, Claude Code 2.1.322, headless runs with a canary value in .env, 10 November 2026. Read tool asked to read .env: leaked without a rule, blocked with the deny rule. Grep tool across the folder including hidden files: leaked without a rule, not found with the rule. Shell command Get-Content .env, which names the file: blocked with the rule. Shell command Select-String -Path star, which does not name it: held for approval. Shell command python -c print open .env read, highlighted: leaked with the rule. Whether a .env variable is in the environment of commands: no, None. Source: our tests with claude -p, Haiku and project settings only on Windows 11; Anthropic documents that deny rules do not cover a Python or Node script that opens files itself.

What happened:

  • Without a rule, Claude read .env without asking. The Read tool returned canary-7f3a9e-open, and so did the Grep tool searching the folder. Nothing in a default setup marks .env as special.
  • The deny rule stopped Claude’s own tools. With the rule, the Read tool reported it was blocked and the Grep tool found nothing.
  • It also stopped a shell command that named the file. Get-Content .env was refused: “get-content targeting ’…\deny.env’ was blocked”.
  • A search that did not name the file never ran. Select-String -Path * was held: “Glob patterns in paths cannot be statically validated … Requires manual approval.” In a headless run that means no; in an interactive session it would be a prompt you could approve.
  • A Python one-liner got through. python -c "print(open('.env').read())" printed canary-7f3a9e-deny. The rule never saw the file name, because Python opened the file, not Claude.
  • Claude Code did not load .env into the environment. A command printing the second variable from inside Python got None.

The Python result is not a bug report; Anthropic documents it. Deny rules apply to Claude’s file tools, to file commands it recognises in the shell “such as cat, head, tail, sed, and tee”, and to redirections. “They don’t apply to a command that reads files without naming them, such as grep -r pattern . run from the directory that holds the file, or to arbitrary subprocesses that read or write files indirectly, like a Python or Node script that opens files itself. For OS-level enforcement that blocks all processes from accessing a path, enable the sandbox.”

Our runs were on Windows, where Claude Code used PowerShell as its shell, so the command names differ from macOS and Linux. Anthropic describes the same limits for Bash: the rule covers commands it recognises that name the file, and nothing that opens the file on its own.

A setup that holds

From the tests and Anthropic’s docs, in order of strength:

  1. Keep long-lived secrets out of the working tree. A secret Claude cannot reach cannot end up in a transcript, a commit or a pull request. Use a secrets manager or a tool that injects values at run time; for Claude Code’s own key, apiKeyHelper can fetch it from one. Our open-source envseal is one such tool: it keeps each secret encrypted in git and envseal run -- npm start passes the values to that one process, so the folder needs no plaintext .env. Two limits, in the same spirit as the test above: a command Claude runs through envseal run gets the secrets too, and envseal pull writes a plaintext file again.
  2. Deny the files anyway. Put Read(./.env) and Read(./.env.*) in permissions.deny, per project in .claude/settings.json or for every project in ~/.claude/settings.json. Bare filenames “follow gitignore semantics and match at any depth”, so Read(.env) also covers .env files in subfolders. On Windows, Anthropic suggests //**/.env to match every drive.
  3. Delete .claudeignore. It does nothing: “If your project has a .claudeignore file, it has no effect, so move its entries into Read deny rules.”
  4. Do not pre-approve interpreters. An allow rule like Bash(python:*) is exactly the door our one-liner used. Approve scripts one by one, or run them in the sandbox.
  5. Turn on the sandbox where a leak would hurt. It is the only layer Anthropic describes as OS-level enforcement for all processes.
  6. Scan before you share. Check a diff, a log or a ticket for keys before it leaves your machine.

If a key leaks

Anthropic keys have recognisable shapes, which helps. GitHub’s secret scanning table lists the Anthropic API key as a partner pattern with user alerts, push protection and validity checks, and the Admin API key with partner alerts, user alerts, push protection and validity checks. So a push to GitHub is the place a leaked key is most likely to be caught. Logs, chat transcripts and tickets are where it is not.

We checked our own free API key leak checker, which runs the keysnitch engine in your browser, against random values in four Anthropic shapes: an API key in an ANTHROPIC_API_KEY= line, an admin key, and two sk-ant- tokens of other kinds. It flagged all four (keysnitch flags any sk-ant- value of 20 to 200 characters, plus the exact API key shape). Nothing you paste leaves the page.

If a key did leak, deleting the line is not enough. Revoke the key in the Claude Console, create a new one, and check the console’s usage for calls you did not make. Our post on why a deleted secret is not a revoked secret explains why history keeps it. For .env itself, our test of how 11 loaders parse the same .env file shows which ones run commands written inside it.

Keys pasted into Jira tickets and Confluence pages are a separate problem from git: Secret Sentinel finds them there and redacts them in place.

How we tested

  • Claude Code 2.1.322 on Windows 11, 10 November 2026. Each run: claude -p "<ask>" --model haiku --setting-sources project, so the user’s own settings and allow rules were not loaded; shell commands allowed with --allowedTools "PowerShell(<command>:*)" for that run only.
  • The canary values were made up and never valid credentials. The test folders were deleted after the runs.
  • Anthropic quotes are from code.claude.com: Settings, Configure permissions, Environment variables and Authentication, read the same day. GitHub’s table is from “Supported secret scanning patterns”.
  • keysnitch 0.1.1, the version on our checker page, with random values generated for each shape.

Frequently asked questions

Can Claude Code read my .env file?

Yes, unless you deny it. In our test Claude Code's Read tool returned the canary value from .env without asking. Adding "Read(./.env)" and "Read(./.env.*)" to permissions.deny in .claude/settings.json blocked the Read tool, the Grep tool and a shell command that named the file. A Python one-liner that opened .env itself still printed the value, which matches Anthropic's documentation: deny rules do not cover scripts that open files on their own. For a hard block, Anthropic points to the sandbox.

Does Claude Code use my API key or my subscription?

If ANTHROPIC_API_KEY is set in the environment Claude Code starts in, Anthropic says it "is used instead of your Claude Pro, Max, Ultra, Team, or Enterprise subscription even if you are logged in". In interactive mode Claude Code asks once whether to use the key; in non-interactive mode (claude -p) it always uses it. A key exported by a project .env loader or a shell profile can therefore move your usage onto API billing. Run unset ANTHROPIC_API_KEY to go back to the subscription.

Where is my Claude Code login or API key stored?

On macOS in the encrypted Keychain; on Linux in ~/.claude/.credentials.json with file mode 0600; on Windows in %USERPROFILE%\.claude\.credentials.json, protected by the access controls of your user profile. If CLAUDE_CONFIG_DIR is set, the file moves there. A Console login that creates an API key keeps it in the same place; a key in ANTHROPIC_API_KEY lives wherever you set that variable. Keep that folder out of backups you share and out of any repository.

How do I use Claude Code in CI without an API key?

Run claude setup-token. It prints a one-year OAuth token tied to your Claude subscription (Pro, Max, Ultra, Team or Enterprise) and does not save it anywhere. Store it in your CI secret store as CLAUDE_CODE_OAUTH_TOKEN. Treat it like an API key: our checker flags any value that starts with sk-ant-, so it will flag this token too if it ends up in a file or ticket.

Does .claudeignore work?

No. Anthropic's permissions page says: "If your project has a .claudeignore file, it has no effect, so move its entries into Read deny rules."

What happens if I commit an Anthropic API key to GitHub?

GitHub's secret scanning knows Anthropic API keys: the table in GitHub's docs marks them as a partner pattern with user alerts, push protection and validity checks. Push protection can stop the push; if the key does reach a public repository, revoke it in the Claude Console anyway and create a new one. Deleting the commit does not make the key safe again.

Keep reading