Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Secrets Scanning in Git and CI with gitleaks

By Kokil Thapa | Last reviewed: August 2026

Hardcoded credentials remain one of the most common and damaging security failures in production web systems. Implementing secrets scanning in Git and CI with gitleaks catches API keys, database passwords, and private tokens before they reach your repository history or deployment pipeline. This guide provides the exact configuration I use on production Laravel and legal-tech projects to enforce this safety net without disrupting legitimate development workflows.

How do you configure secrets scanning in Git and CI with gitleaks locally?

The first line of defense is always local. Relying solely on CI means secrets have already been pushed to the remote, requiring history rewriting if caught. For developers working on sensitive projects like legal-tech portals handling client data, preventing the leak at the source is non-negotiable. Gitleaks v8.21+ (current stable in 2026) supports native Git hook integration that validates staged changes before the commit object is created.

DeveloperEdits .env copygit add / commitStages ChangesGitleaksPre-commitHook Runs✓ SafeCommit Created✗ BlockedSecret Found
Local secrets scanning in Git with gitleaks pre-commit hook blocks leaks before they enter version control

Installing and configuring the pre-commit hook

Install gitleaks via Homebrew, apt, or direct binary download. For team consistency, pin the version in your project documentation or DevContainer configuration. On Ubuntu 24.04 servers I maintain, I install it globally so all users share the same scanner version:

# Install gitleaks v8.21.x (verify latest tag on GitHub releases)
curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.21.2/gitleaks_8.21.2_linux_x64.tar.gz | tar -xz -C /usr/local/bin gitleaks

# Verify installation
gitleaks version

# Initialize pre-commit hook in your Laravel/PHP project root
gitleaks protect --staged --pre-commit

The --staged flag is critical. Without it, gitleaks scans the entire working directory including untracked files and build artifacts, producing excessive false positives. With --staged, it only examines what you are about to commit. The --pre-commit flag installs the hook script into .git/hooks/pre-commit automatically.

Handling legitimate test fixtures and example configs

Real projects contain placeholder credentials in test suites, example configurations, and documentation. Blocking these halts development unnecessarily. Create a .gitleaksignore file in your repository root to allowlist specific false positives by their SHA256 fingerprint:

# .gitleaksignore
# Example AWS key used in unit tests (tests/Fixtures/AwsClientTest.php)
a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456

# Placeholder Stripe test key in README examples
fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321

Generate fingerprints for legitimate secrets using gitleaks detect --verbose on the offending commit, then copy the Fingerprint value. Never allowlist by file path alone — paths change during refactoring, but content hashes remain stable. Review the allowlist quarterly as part of your CI/CD maintenance routine.

How do you integrate gitleaks into GitLab CI pipelines?

Local hooks can be bypassed. A determined developer can skip them with --no-verify, or a misconfigured environment might not have the hook installed. CI integration serves as the authoritative enforcement point. In my experience managing shared infrastructure for multiple Nepal-based legal and translation service sites, the CI job is where most real-world catches happen after initial adoption.

Merge RequestPush EventGitleaksScan JobFull History + StagedBuild & TestPipeline ContinuesPipeline FailsMR BlockedReportArtifact
GitLab CI pipeline integration for secrets scanning in Git and CI with gitleaks enforces mandatory security gates

Production-ready GitLab CI job configuration

Add this job to your .gitlab-ci.yml. It runs early in the pipeline, fails fast, and uploads results as an artifact for audit trails. This configuration works with GitLab CI runner images that include curl and tar:

gitleaks-scan:
  stage: validate
  image: alpine:3.20
  variables:
    GITLEAKS_VERSION: "8.21.2"
  before_script:
    - apk add --no-cache curl tar git
    - curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz | tar -xz -C /usr/local/bin gitleaks
  script:
    # Scan full history for MRs; use --log-opts for branch-specific ranges
    - gitleaks detect --source . --report-format json --report-path gitleaks-report.json --exit-code 1
  artifacts:
    when: always
    paths:
      - gitleaks-report.json
    expire_in: 30 days
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

The when: always directive ensures the report uploads even when secrets are found. Without it, a failed job produces no artifact, making triage impossible. Set expire_in according to your compliance requirements — 30 days covers most audit windows while avoiding storage bloat.

Scanning only changed commits in large repositories

Full-history scans on mature projects with thousands of commits take minutes. For monorepos or legacy codebases, restrict scanning to the merge request diff range:

script:
  # Fetch target branch for accurate diff calculation
  - git fetch origin ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}
  # Scan only commits between target and source
  - gitleaks detect --source . --log-opts "--since=${CI_MERGE_REQUEST_DIFF_BASE_SHA}" --report-format json --report-path gitleaks-report.json --exit-code 1

This reduces scan time from minutes to seconds on active branches. Reserve full-history scans for nightly scheduled pipelines or initial baseline assessments. On a legal document management system I maintained, switching to diff-based scanning reduced CI feedback time from 4 minutes to 18 seconds per merge request.

What custom gitleaks rules should PHP and Laravel projects use?

Gitleaks ships with 100+ built-in rules covering AWS, GitHub, Stripe, and other major providers. However, PHP and Laravel ecosystems have unique secret patterns that default rules miss. Custom rules catch framework-specific leaks like APP_KEY values, Sanctum tokens, and Nepal payment gateway credentials.

Rule TargetPattern TypeFalse Positive RiskDetection Confidence
Laravel APP_KEYRegex + entropyLowHigh
eSewa / Khalti KeysPrefix + lengthMediumHigh
Sanctum Personal TokensFormat + contextLowMedium
Generic Base64 SecretsEntropy onlyHighLow
Database URLs in ConfigURI scheme + passwordMediumHigh

Creating a custom ruleset for Laravel applications

Create .gitleaks.toml in your project root. Extend the default rules rather than replacing them to retain upstream provider coverage:

# .gitleaks.toml
title = "Laravel Project Secrets Rules"

[extend]
useDefault = true

[[rules]]
id = "laravel-app-key"
description = "Detected Laravel APP_KEY in source code"
regex = '''APP_KEY\s*=\s*base64:[A-Za-z0-9+/]{43}='''
keywords = ["APP_KEY", "base64"]
tags = ["laravel", "php"]

[[rules]]
id = "nepal-payment-esewa"
description = "Detected eSewa merchant secret key"
regex = '''(?i)(esewa|ESEWA).{0,30}(secret|key|token)\s*[:=]\s*['\"][a-zA-Z0-9]{32,64}['\"]'''
keywords = ["esewa", "merchant"]
tags = ["nepal", "payment"]

[[rules]]
id = "sanctum-personal-token"
description = "Detected Laravel Sanctum personal access token"
regex = '''\b[a-zA-Z0-9]{40}\|[a-zA-Z0-9]{64}\b'''
keywords = ["|"]
tags = ["laravel", "sanctum", "api"]

[[rules]]
id = "database-url-with-password"
description = "Detected database connection string with embedded password"
regex = '''(mysql|postgres|pgsql):\/\/[^:]+:[^@]+@[^\s]+'''
keywords = ["mysql://", "postgres://", "pgsql://"]
tags = ["database", "config"]

The Sanctum token rule deserves explanation. Sanctum formats tokens as {id}|{hash} where both segments are fixed-length alphanumeric strings. The pipe character combined with exact lengths makes this pattern highly specific. Generic base64 detectors miss this format entirely because the pipe breaks standard encoding assumptions.

Validating custom rules before deployment

Never deploy untested regex rules. False positives erode trust and lead developers to disable scanning. Validate against known-good and known-bad samples:

# Test against fixture files containing intentional secrets
gitleaks detect --config .gitleaks.toml --source tests/Fixtures/SecretsSamples/ --verbose

# Verify no matches on clean production config templates
gitleaks detect --config .gitleaks.toml --source config/ --verbose

# Benchmark rule performance on full repo (should complete in <30s for mid-size projects)
time gitleaks detect --config .gitleaks.toml --source . --no-git

If a rule triggers more than 5 times on legitimate code during validation, refine the regex or add contextual keywords. Precision matters more than recall in custom rules — let the default high-confidence rules handle generic cases.

How do you remediate exposed secrets found by gitleaks?

Finding a secret is only half the problem. Remediation requires revoking the compromised credential, removing it from Git history, and verifying no downstream systems cached the old value. Skipping any step leaves residual risk.

1. DetectGitleaks Alert2. RevokeRotate Credential3. Rewritegit-filter-repo4. VerifyRescan History5. MonitorAudit LogsCritical Notes• Revocation BEFORE history rewrite — old secret still valid during cleanup• Force-push required after filter-repo — coordinate with all contributors• Check third-party audit logs (AWS CloudTrail, Stripe Dashboard) for misuse• Update all environments: dev, staging, production, CI secrets store
Five-step remediation workflow after secrets scanning in Git and CI with gitleaks detects exposed credentials

Credential revocation must precede history rewriting

This ordering is counterintuitive but essential. If you rewrite Git history first, the old secret remains valid in production systems during the cleanup window. An attacker who cloned the repository before the force-push retains access. Always rotate the credential immediately upon discovery, then proceed to history cleanup.

For Laravel projects, regenerate APP_KEY with php artisan key:generate and re-encrypt any data encrypted with the old key. For API integrations like those described in Laravel API best practices, generate new tokens and update environment variables across all deployed instances simultaneously.

Rewriting Git history safely with git-filter-repo

Avoid git filter-branch — it is deprecated and error-prone. Use git-filter-repo (installed via pip or package manager) for reliable history rewriting:

# Install git-filter-repo
pip install git-filter-repo

# Remove specific secret value from entire history
git filter-repo --replace-text expressions.txt --force

# expressions.txt format (one per line):
# literal:sk_live_abc123xyz==>REDACTED_SECRET

# After rewrite, force-push all affected branches
git push --force --all
git push --force --tags

Coordinate force-pushes with your team. Every contributor must re-clone or reset their local branches. Document the incident timestamp and affected commit ranges so future bisect operations account for the discontinuity. On shared infrastructure like the Deployer 7 + GitLab CI setup I use for sister sites, schedule rewrites during low-traffic windows to minimize deployment disruption.

Post-remediation verification checklist

  1. Run gitleaks detect --source . on the rewritten repository — zero findings expected
  2. Search raw Git objects: git grep -n "old_secret_value" $(git rev-list --all)
  3. Verify rotated credentials work in all environments (dev, staging, production)
  4. Check third-party provider audit logs for unauthorized usage during exposure window
  5. Update CI/CD secret stores (GitLab Variables, Vault, AWS Secrets Manager)
  6. Document incident in security log with timeline, impact assessment, and preventive measures added

Conclusion

Implementing secrets scanning in Git and CI with gitleaks transforms credential security from reactive incident response to proactive prevention. Start with local pre-commit hooks for immediate developer feedback, enforce mandatory CI gates for authoritative protection, extend default rules for your specific technology stack, and establish clear remediation procedures before your first real leak occurs. The configuration patterns in this guide reflect battle-tested setups from production Laravel applications and legal-tech platforms serving real clients.

If your team needs help integrating secrets scanning into existing CI/CD pipelines, configuring custom rules for PHP/Laravel projects, or remediating historical leaks safely, reach out to discuss your specific requirements. Proper secrets management is foundational infrastructure, not optional compliance theater.

Frequently Asked Questions

Gitleaks is a lightweight, open-source SAST tool specifically optimized for detecting hardcoded secrets in Git repositories. Unlike heavier alternatives, it runs extremely fast locally and in CI pipelines with minimal configuration overhead, making it ideal for pre-commit hooks and GitLab CI jobs where speed matters more than enterprise reporting features.

Download the latest Linux AMD64 binary from GitHub releases, extract it, and move to /usr/local/bin/gitleaks. Verify with gitleaks version. This avoids Docker overhead during local development while ensuring you test against the exact same binary version used in your production CI pipeline for consistent detection results across environments.

It scans full Git history by default, catching secrets deleted in previous commits but still present in objects. Use --no-git flag to scan only working directory files. In my experience, always run full history scans initially after adopting gitleaks, as legacy credentials often lurk in old commits even when removed from HEAD.

Add gitleaks detect --staged to your .pre-commit-config.yaml under the gitleaks repo entry. This scans only staged changes before commit, preventing secrets from entering history entirely. The --staged flag is critical; without it, pre-commit scans all files and becomes too slow for daily development workflows on larger Laravel or WordPress projects.

Yes, add a job using the zricethezav/gitleaks Docker image running gitleaks detect --verbose --report-format json --report-path gl-secret-scan.json. Configure it as a merge request pipeline stage failing on exit code 1. On projects like notarykathmandu.com, this catches leaked API keys before they reach protected branches without slowing deployments.

Test fixtures, example environment files, UUIDs matching regex patterns, and documentation snippets containing placeholder tokens frequently trigger alerts. Rather than disabling rules globally, use allowlist entries scoped to specific file paths or commit SHAs. Over-tuning rules causes missed detections; targeted path-based exclusions maintain security coverage while reducing noise in real codebases.

Create a .gitleaksignore.toml file listing specific rule IDs, paths, or commit hashes to exclude. Never disable entire rule categories. For example, if test fixtures contain fake AWS keys, allowlist tests/Fixture/*.php rather than weakening the aws-access-key-id rule. Document each exclusion with justification for audit trails during security reviews.

No, gitleaks only scans text-based content within Git objects. Binary files, compiled assets, and images are ignored entirely. For comprehensive secret scanning including binaries, combine gitleaks with tools like truffleHog or specialized binary analyzers. In practice, most accidental leaks occur in source code and config files where gitleaks excels at rapid detection.

Gitleaks is completely free and MIT-licensed for all uses including commercial projects. There are no paid tiers, licensing fees, or usage limits. Enterprise support exists via separate consulting arrangements, but the core scanning engine remains fully functional without payment. Budget NPR 0 for the tool itself; costs arise only from engineering time configuring and maintaining it.

Yes, gitleaks runs entirely locally or within your own CI infrastructure. No code leaves your environment unless you explicitly configure external reporting endpoints. When running in GitLab CI on shared EC2 instances, secrets stay within your VPC. This makes it suitable for legal-tech portals handling sensitive client data where third-party SaaS scanners violate compliance requirements.

The pipeline fails immediately with exit code 1, blocking merge requests or deployments. Review the JSON report artifact to identify the leak location and type. Remediate by rotating the credential first, then removing it from history using git-filter-repo or BFG Repo-Cleaner. Simply deleting the secret in a new commit leaves it accessible in Git history indefinitely.

Immediately revoke the exposed credential at its source (AWS IAM, Stripe dashboard, database user) before fixing code. Generate new credentials, update environment variables and deployment configs, then clean Git history. Test thoroughly after rotation. In production Laravel applications, also check logs and access records for unauthorized usage during the exposure window. Rotation priority exceeds code cleanup urgency.

Run on every merge request to catch issues before integration, plus scheduled nightly scans on default branches to detect regressions from force-pushes or rebases that bypass MR pipelines. Push-triggered scans create excessive CI load on active repositories. On sister sites sharing Deployer 7 pipelines, MR-only scanning balances security coverage with pipeline throughput and developer feedback speed.

Gitleaks offers comparable secret detection accuracy for free with faster execution and zero vendor lock-in. Paid platforms provide better UI, remediation tracking, and compliance reporting but cost USD 49+ per committer monthly (~NPR 6,500). For Nepal-based teams or budget-conscious agencies, gitleaks delivers essential protection without recurring SaaS expenses. Upgrade only when audit requirements demand formal attestation workflows.

Yes, define custom regex rules in .gitleaks.toml under [[rules]] sections specifying id, description, regex pattern, and keywords. Test patterns against known samples before deploying. For Nepal Gift Card's internal token format, adding a project-specific rule caught leaks that generic patterns missed. Custom rules require maintenance as formats evolve but significantly improve detection precision for proprietary authentication schemes.

Share this article

Quick Contact Options
Choose how you want to connect me: