What you need and what you will have at the end
You need a git repository you own, a terminal, and admin rights on the GitHub repository for the last step. Install gitleaks with Homebrew using brew install gitleaks, or use its Docker image from ghcr.io/gitleaks/gitleaks.
At the end you will know whether any secret already sits in your history, and new commits will be checked locally, in CI and by GitHub before they land.
Step 1: Scan the full git history
The gitleaks git command scans a repository's commits. Run it from the repository root. It reads the full history by default, so it finds secrets in old commits, deleted files and other branches you have locally. The --redact flag hides the secret value in the output, which keeps it out of your terminal scrollback.
A clean run exits with code 0 and reports no leaks. A run that finds something exits with code 1 by default and prints each finding with its file, commit, rule and a fingerprint.
gitleaks git -v --redact --report-format json --report-path gitleaks-report.json .Step 2: Read the findings
For each finding note the file, the commit and whether the value is real. Some matches are false positives, such as example keys in documentation or test fixtures. Real ones are anything that would authenticate: cloud keys, API tokens, database URLs with passwords, private keys.
Keep the report file out of git. It contains fingerprints and locations, and depending on flags may contain values, so add it to your ignore file.
echo "gitleaks-report.json" >> .gitignoreStep 3: Add a pre-commit hook
The pre-commit framework runs checks before each commit is created. Install it with your package manager, then add a configuration file at the repository root that points at the gitleaks repository. Use the current release tag in place of the example one below, and find it on the gitleaks releases page.
After adding the file, run pre-commit install once per clone. From then on every commit runs gitleaks and is refused if it finds a secret. In an emergency you can skip the hook with SKIP=gitleaks before the commit command, but that should be rare.
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.24.2
hooks:
- id: gitleaksStep 4: Add a CI check
A local hook depends on each developer installing it. A CI job does not. The official gitleaks GitHub Action needs the full history, so the checkout step must set fetch-depth to 0. The action uses a license key for repositories owned by an organization, and not for personal accounts, so check the action's README for your setup and store any key as a repository secret.
name: gitleaks
on:
pull_request:
push:
workflow_dispatch:
jobs:
scan:
name: gitleaks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}Step 5: Turn on GitHub secret scanning and push protection
Open your repository on GitHub and go to Settings. In the Security section choose Advanced Security. Click Enable next to Secret Protection if it is not already on, then click Enable next to Push protection. Menu names change occasionally, so follow the linked GitHub page if yours differs.
Push protection blocks a push that contains a recognized secret before it reaches the repository. Anyone with write access can bypass the block by giving a reason, and GitHub records that bypass and raises an alert, so treat a bypass as a decision you have to defend. Push protection for users is on by default for public repositories, while for the repository itself it is off until an administrator enables it.
Step 6: What to do when something is found
- 1Treat the secret as compromised, even if the repository is private.
- 2Create a new credential at the provider and deploy the app using it, loaded from an environment variable on the server.
- 3Revoke the old credential. This is the step that actually closes the hole.
- 4Check the provider's logs and billing for activity you did not cause.
- 5Remove the value from the code and confirm the app runs from the environment variable.
- 6Rewriting history is optional cleanup and not a substitute for revoking. If you do it, everyone must re-clone.
- 7Once handled, record the finding's fingerprint in a .gitleaksignore file only if it is a confirmed false positive.
Common mistakes
How to verify
Create a throwaway branch and add a file containing a fake key that matches a known pattern, such as an AWS-style key id, and try to commit. The pre-commit hook should refuse. Delete the branch afterward and never push the fake value. For the CI job, open a pull request and confirm the check runs and passes on clean code. The pass condition is a clean full-history scan, a refused fake commit and a green CI job.
Keep it working
Update the rev in the pre-commit file and the action version when new releases come out, re-run the full history scan after merging a long-lived branch, and run pre-commit install in every new clone.
Frequently asked questions
Does gitleaks scan my whole history?
Yes. The git command reads the commit history of the repository, and you can narrow it with the --log-opts flag, for example to scan a range of commits. In CI the checkout must fetch full history or older commits are missed.
Is deleting the secret from the code enough?
No. The old value stays in git history and in any clone or fork. It is valid until you revoke it at the provider. Rotate first, then clean up.
Can I ignore a false positive?
Yes. Each finding has a fingerprint, and adding it to a .gitleaksignore file suppresses that one finding. You can also add a gitleaks:allow comment on a line in a trusted test file. Confirm the value is not real first.
Do I still need gitleaks if GitHub push protection is on?
Yes, for coverage. Push protection checks for secret types GitHub recognizes at push time. Gitleaks runs locally, scans history you already have, and lets you add custom rules, so the two complement each other.