Learning objectives
After reading this article you will be able to:
- Move hard-coded secrets into environment variables
- Block secrets from reaching Git with .gitignore, a pre-commit scan and push protection
- Respond correctly when a secret has already been committed
On this page
A secret is any value that grants access: a bot token, a database password, a payment API key, a signing key. The moment one lands in a Git commit, you lose control of it. It is copied to every clone, shown in diffs, and, if the push is public, bots can pick it up quickly.
The fix is a habit more than a tool: the code asks for the secret at runtime, and the secret lives somewhere else. The steps below go from the first refactor to what to do when something leaks.
Why is a hard-coded secret a problem?#
Git never forgets. Deleting a line in a later commit leaves the old value in the history, and history is copied to every clone and fork. Private repositories are not a safe place either: more people get read access over time, CI systems print logs, and repositories get made public by accident.
Hard-coding also couples code and environment. The same file cannot run with a test key on your laptop and a real key in production without editing source.
How do I move a secret out of the code?#
Read it from an environment variable. Before:
const token = "MTIzNDU2Nzg5MA.fake.token"; // hard-coded: never do thisAfter, in Node.js, with a check that fails fast if the value is missing:
// index.js
const token = process.env.DISCORD_TOKEN;
if (!token) {
throw new Error("DISCORD_TOKEN is not set");
}The same in Python:
import os
token = os.environ["DISCORD_TOKEN"] # raises KeyError if missingFor local development, put the values in a .env file and load it. Node.js can do it natively with a flag (--env-file, available since v20.6.0 and no longer experimental in v22.21 and v24.10):
node --env-file=.env index.jsIf the file may be absent, use --env-file-if-exists=.env (since v22.9.0). Without it, Node.js throws when the file is missing. A variable already set in the real environment wins over the file. See the .env file for syntax and Python loading, and environment variables for the concept.
How do I stop secrets from reaching Git?#
Use three layers, because each catches what the others miss.
1. Ignore the file. Add this to .gitignore before you create the first .env:
.env
.env.*
!.env.exampleThen commit a .env.example with variable names and fake values, so teammates know what to set:
DISCORD_TOKEN=replace_me
DATABASE_URL=postgresql://user:password@localhost:5432/mydb.gitignore only affects untracked files. If .env was already committed, run git rm --cached .env first.
2. Scan before you commit. Gitleaks is an open source scanner. As a pre-commit hook, add this to .pre-commit-config.yaml (replace rev with the current version from the project's releases page, or run pre-commit autoupdate), then run pre-commit install:
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.30.1
hooks:
- id: gitleaksTo scan the full history of an existing repository once:
gitleaks git -v .The older detect and protect commands were deprecated in v8.19.0 in favor of git, dir and stdin.
3. Block on the server. GitHub push protection rejects pushes that contain recognized secret patterns. For users it is on by default for public repositories on GitHub.com. Turning it on for a repository is up to a repository administrator or organization owner and requires GitHub Secret Protection. It is a safety net, not a substitute for the first two layers, because it only recognizes supported and custom patterns.
Where should secrets live instead?#
Pick the simplest place that fits the stage:
| Stage | Where the secret lives |
|---|---|
| Local development | An ignored .env file, or your shell |
| CI/CD | The CI provider's encrypted secrets, passed to the job as environment variables |
| Production | The hosting platform's environment variable settings, or a dedicated secrets manager |
A dedicated secrets manager adds access control, audit logs and rotation. It is worth it when many people or services share credentials. For a single bot or API, platform environment variables are usually enough.
Two rules apply everywhere. Give each environment its own secret, so a leaked development key cannot touch production. And give each secret the least permission it needs, such as a read-only database user for a reporting script.
Can I keep secrets in front-end code?#
No. Anything sent to the browser is public. Next.js, for instance, inlines variables prefixed with NEXT_PUBLIC_ into the JavaScript bundle at build time, by design. Other frameworks have similar prefixes. A payment secret key or a database URL in a front-end variable is published to every visitor.
Keep real secrets on a server, and have the browser call your own endpoint. Public identifiers, such as a publishable key meant for the client, are fine in front-end code.
What do I do if a secret was already committed?#
Order matters. Revoke first, clean later.
- Revoke or rotate the secret now. Generate a new token or password at the provider and update your deployment. GitHub's own guidance puts this first: once the old value is revoked, it can no longer be used, which may solve the problem on its own.
- Check for misuse. Look at the provider's audit or usage logs for activity you do not recognize.
- Remove it from history, if you want. Use git filter-repo to rewrite the history, then force-push. Ask collaborators to re-clone, or they may push the old data back.
- Accept the limits. Rewriting does not clean other clones, forks, or cached views of old commits. That is why step 1 is the real fix.
For the exact steps after a leak on GitHub, read what to do when an API key is exposed on GitHub. A wider list of deployment checks is in the deploy security checklist.
How do I verify it worked?#
git ls-files | grep -i "\.env"lists tracked env files. Only.env.exampleshould appear.git log -p -S"your-secret-prefix"searches history for a value you worry about. Use a prefix, not the whole secret.- Run
gitleaks git -v .and confirm it reports no leaks. - Start the app with the variable unset. It should fail with your clear error, not run with a default.
How to do this on Square Cloud#
On Square Cloud, you set environment variables per application: in the dashboard, through the API, in the CLI or in the VS Code extension. The dashboard can import a .env file and export the variables back as KEY=VALUE, so your local file and production stay easy to sync.
A change takes effect on the next restart, with no rebuild. Your code reads the values the usual way, through process.env or os.environ, so nothing in your source has to know where they are stored. Each application also gets its own SQUARECLOUD_APP_ID variable at runtime.
Because variables are set per application, you can give each app its own tokens and keep the .env file out of the ZIP or repository you deploy. Read more on the environment variables page.
Infrastructure
L3/L4/L7 DDoS mitigation and a WAF on every plan
FAQs
Is it safe to commit a secret to a private repository?
It is not recommended. Everyone with read access, every clone, every CI log and every future collaborator can see it, and a repository can become public by mistake. Treat the repository as a place for code only.
Does deleting a committed secret in a new commit fix the leak?
No. The secret stays in the Git history and in every clone and fork. The only real fix is to revoke the secret and issue a new one. Rewriting history is a cleanup step afterwards.
Are .env files a secrets manager?
No. A .env file is plain text on disk, useful for local development. In production, set the variables in your hosting platform or in a dedicated secrets manager, and keep the file out of Git.
Can I put secrets in front-end code?
No. Anything shipped to the browser can be read by every visitor. Variables with prefixes such as NEXT_PUBLIC_ are inlined into the JavaScript bundle on purpose. Keep real secrets on a server and expose only an endpoint.
More in Security
See all 10 articles- What is a brute force attack and how to defend against itA brute force attack tests passwords in bulk until one works. Learn the types (spraying, credential stuffing), how they work and the defenses, with code that runs.
- My app was hacked: what to do nowIf your app was hacked, contain access, rotate every secret and preserve the logs before cleaning up. See the right order, how to find the cause and how to recover.
- Security checklist for deploying a bot, site or APIA security checklist before you deploy a bot, site or API: secrets in environment variables, dependencies, HTTPS, headers, least privilege and logs.
- How to protect your app from DDoS attacksTo protect against DDoS, combine edge mitigation (CDN and WAF) with rate limiting, timeouts and caching in your app. Step-by-step guide with working code.