Never commit API keys, passwords, tokens, or credentials to version control. Once a secret is in git history, it is permanently exposed — even deleting the file doesn't remove it from history, and anyone who cloned the repo has a copy forever.
A committed secret is permanently compromised. Even if you delete the file in a subsequent commit, the secret remains in git history forever. Every developer, CI runner, and backup that cloned the repo has a copy. Automated bots scan public repositories for secrets within minutes of a push — leaked AWS keys have led to five-figure cloud bills within hours.
BeforeMerge scans your pull requests against this rule and 3+ others. Get actionable feedback before code ships.
Git is an append-only log. When you commit a file containing an API key, that key is recorded in the repository's history permanently. Deleting the file in a subsequent commit does not remove it — git log --all --full-history and git show will reveal the secret at any time.
This is not theoretical. Automated bots continuously scan GitHub for committed secrets. AWS keys pushed to public repositories are typically exploited within minutes — attackers spin up cryptocurrency mining instances that generate thousands of dollars in charges before the key can be revoked.
Even in private repositories, committed secrets are accessible to every developer with clone access, every CI/CD runner, every backup system, and every fork. If the repository ever becomes public (intentionally or through a breach), every historical secret is exposed.
Never commit API keys, passwords, tokens, database connection strings, private keys, or any other credentials to version control. Use environment variables for all secrets, and ensure .env files are in .gitignore.
// config.ts — hardcoded secrets in source code
export const config = {
database: {
host: "prod-db.example.com",
password: "super_secret_password_123",
},
stripe: {
secretKey: "sk_live_abc123def456ghi789",
},
aws: {
accessKeyId: "AKIAIOSFODNN7EXAMPLE",
secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
},
};# .env committed to git
DATABASE_URL=postgres://admin:password@prod.example.com:5432/mydb
STRIPE_SECRET_KEY=sk_live_abc123// config.ts — reads from environment variables
export const config = {
database: {
host: process.env.DATABASE_HOST!,
password: process.env.DATABASE_PASSWORD!,
},
stripe: {
secretKey: process.env.STRIPE_SECRET_KEY!,
},
aws: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
};# .gitignore
.env
.env.local
.env.production
*.pem
*.keyUse secret scanning tools:
# Scan current codebase
npx secretlint "**/*"
# Scan git history
git log --all -p | grep -iE '(api_key|secret|password|token)\s*[=:]\s*[\x27"][^\x27"]+[\x27"]'Enable GitHub's built-in secret scanning for push protection.
.gitignoregit filter-branch or BFG Repo Cleaner (this rewrites history for all collaborators)