Keel

Security1 min read

How to keep secrets out of Git repositories

Prevent API keys and passwords from reaching Git with ignore rules, scanners, and push protection, and learn the correct cleanup order if one gets committed.

By the Keel team

Git keeps history. A secret committed once is stored in every clone and fork, and removing it from the latest commit does not remove it from earlier ones. Prevention is far cheaper than cleanup.

Prevent

Ignore local environment files

# .gitignore
.env
.env.*
!.env.example

Note that .gitignore only affects untracked files. If .env was already committed, stop tracking it with git rm --cached .env, then treat its contents as exposed.

Commit an example file, not real values

A .env.example with variable names and empty values documents requirements without risk. Keep the real values in a secrets store.

Scan before commit

Tools such as gitleaks can run as a pre-commit hook and in CI, and flag strings that look like credentials before they leave your machine.

Turn on push protection

GitHub's secret scanning can detect known token formats and, with push protection enabled, block a push that contains one.

Respond: the order matters

  1. Revoke or rotate the credential first. Assume anything pushed was seen. Rewriting history afterwards cannot recall copies.
  2. Check the provider's logs for use you do not recognise.
  3. Remove it from history with a tool such as git filter-repo, then force-push and ask collaborators to re-clone.
  4. Follow your host's guidance. GitHub documents the process in removing sensitive data from a repository, including cached views and forks.
  5. Store the replacement properly, outside the repository.

Where the secrets go instead

Your application still needs the values. Keep them in a store with access control and import or inject them per environment. Keel can import an existing .env file once, so the file can be deleted from developers' machines afterwards, and keeps version history of every change.

Private repositories are not a safe place either

A private repository limits who can read today. It does not account for future access changes, forks, leaked tokens, or mirrored clones. Keep the same rules.