How to Use .env Files to Keep Your API Keys Out of Git

Committing an API key to a public repository is one of the most common, and most expensive, mistakes a developer can make. Bots scrape GitHub constantly for leaked keys, and a committed secret can be exploited within minutes.

The fix is simple: use environment variables via .env files and never let secrets touch your codebase. If you’re working with AI APIs like Claude or OpenAI, this is especially important — see my guide on how to get your Claude API key for the full setup.

What Is a .env File?

A .env file is a plain text file that stores key-value pairs:

ANTHROPIC_API_KEY=sk-ant-your-key-here
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
STRIPE_SECRET_KEY=sk_live_abc123

Your application reads these values at runtime. The .env file lives on your machine (or your server) but never gets committed to Git.

Step 1: Create the .env File

In your project root:

touch .env

Add your secrets:

# .env
ANTHROPIC_API_KEY=sk-ant-your-key-here
OPENAI_API_KEY=sk-your-openai-key

No quotes needed around values. No spaces around the =.

Step 2: Add .env to .gitignore

This is the critical step. Open (or create) your .gitignore and add:

# .gitignore
.env
.env.local
.env.*.local

If you’re starting a new project, do this before your first commit. If .gitignore already exists, add the lines and commit the change.

Important: .gitignore only prevents future commits. If you’ve already committed a .env file, adding it to .gitignore won’t remove it from your Git history. See the What to Do If You Already Committed a Key section below.

Verify it’s working:

git status

You should not see .env in the list of untracked files. If you do, .gitignore isn’t set up correctly.

Step 3: Read Environment Variables in Your Code

Python

Install python-dotenv:

pip install python-dotenv
import os
from dotenv import load_dotenv

load_dotenv()  # Reads .env file into environment

api_key = os.environ["ANTHROPIC_API_KEY"]

The Anthropic SDK picks up ANTHROPIC_API_KEY automatically, so you don’t even need to pass it:

import anthropic

client = anthropic.Anthropic()  # Uses ANTHROPIC_API_KEY from environment

Node.js / JavaScript

Install dotenv:

npm install dotenv
import "dotenv/config";

const apiKey = process.env.ANTHROPIC_API_KEY;

Or load it explicitly:

import dotenv from "dotenv";
dotenv.config();

console.log(process.env.ANTHROPIC_API_KEY);

Like the Python SDK, the Anthropic JS SDK reads ANTHROPIC_API_KEY automatically:

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic(); // Uses ANTHROPIC_API_KEY from environment

C# / .NET

.NET has built-in configuration support. Add a appsettings.json (committed, no secrets) and use environment variables or user secrets for the actual keys:

dotnet user-secrets set "Anthropic:ApiKey" "sk-ant-your-key-here"
var builder = WebApplication.CreateBuilder(args);
var apiKey = builder.Configuration["Anthropic:ApiKey"];

For console apps, read directly from the environment:

var apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY");

In .NET, never put secrets in appsettings.json as that file gets committed. Use dotnet user-secrets for local dev and environment variables for production.

What to Do If You Already Committed a Key

If you’ve already pushed a secret to GitHub, changing your .gitignore isn’t enough. The key is still in your Git history. You need to:

1. Revoke the Key Immediately

Go to your API provider’s dashboard and rotate or delete the compromised key. This is the most important step. Do it first, before anything else.

2. Remove It from Git History

Use BFG Repo Cleaner (faster and simpler than git filter-branch):

# Install BFG (requires Java)
# macOS:
brew install bfg
# Windows/Linux: download from https://rtyley.github.io/bfg-repo-cleaner/

# Remove the file from all history
bfg --delete-files .env

# Or replace specific strings
echo "sk-ant-your-key-here" > passwords.txt
bfg --replace-text passwords.txt

# Clean up and force push
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push --force

3. Check GitHub’s Secret Scanning

GitHub automatically scans for known secret formats and will alert you if it finds one. If you have push protection enabled, it will even block the push before it happens.

Enable it: Repository Settings > Code security and analysis > Secret scanning.

Create a .env.example File

Include a .env.example in your repo so other developers know what variables they need:

# .env.example (committed to Git, no real values)
ANTHROPIC_API_KEY=your-key-here
DATABASE_URL=your-database-url
STRIPE_SECRET_KEY=your-stripe-key

This file gets committed. It documents the required variables without exposing actual secrets.

Environment Variables in CI/CD

In production, you don’t use .env files. Instead, set environment variables directly in your deployment platform:

GitHub Actions:

# .github/workflows/deploy.yml
env:
  ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

Add the secret in Repository Settings > Secrets and variables > Actions.

Azure App Service:

az webapp config appsettings set \
  --name your-app \
  --resource-group your-rg \
  --settings ANTHROPIC_API_KEY="sk-ant-your-key-here"

Docker:

docker run -e ANTHROPIC_API_KEY="sk-ant-your-key-here" your-image

Or use a .env file with Docker Compose:

# docker-compose.yml
services:
  app:
    env_file:
      - .env

Quick Checklist

Before every commit, ask yourself:

  • Is .env in my .gitignore?
  • Am I reading secrets from process.env / os.environ / Environment.GetEnvironmentVariable, not hardcoded strings?
  • Does my .env.example document all required variables?
  • Are my CI/CD secrets stored in the platform’s secret manager, not in config files?
  • Have I set spend limits on my API keys in case one leaks?

If you answered yes to all five, your keys are safe.