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
.envin my.gitignore? - Am I reading secrets from
process.env/os.environ/Environment.GetEnvironmentVariable, not hardcoded strings? - Does my
.env.exampledocument 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.