This error occurs when GitLab's Secret Push Protection detects potential secrets (API keys, tokens, passwords, or private keys) in your commits. You must either remove the secrets from your commits, rotate the exposed credentials, or explicitly skip the check for false positives.
The "Push was rejected because it contains secrets" error is triggered by GitLab's Secret Push Protection feature, a security mechanism that scans commits for sensitive data before allowing them to be pushed to the repository. When you push commits over HTTP(S) or SSH, GitLab analyzes the diffs (changes) in each commit looking for patterns that match known secret formats. If a potential secret is detected, the push is blocked to prevent accidental exposure of credentials. **What GitLab detects:** - **API keys and tokens** - GitLab Personal Access Tokens, AWS Access Keys, Google API Keys, etc. - **Database connection strings** - URLs containing embedded usernames and passwords - **Private keys** - RSA, DSA, EC private keys used for authentication - **High-entropy strings** - Random-looking strings that match secret patterns This is a security feature, not a bug. GitLab is protecting your repository from credential leaks, which are a leading cause of security breaches. Once secrets are pushed to a repository (even a private one), they can be exposed through forks, CI/CD logs, or compromised accounts. **Important:** Secret Push Protection only scans the diffs in your commits, not entire files. If a secret was already present in a file and your commit doesn't modify the line containing it, the secret won't be detected by this feature.
When your push is blocked, GitLab provides detailed information about what was detected. Look at the error message carefully:
remote: PUSH BLOCKED: Secrets detected in code changes
remote: -- commit abc123: config/settings.py:15 AWS Access Key
remote: -- commit def456: src/api.js:42 GitLab Personal Access TokenThe message tells you:
- Commit ID - Which commit contains the secret
- File path and line number - Exact location of the detected pattern
- Secret type - What kind of secret was identified
Note down each detection - you'll need to address them all before you can push successfully.
Before taking action, evaluate whether the detected pattern is an actual secret:
True positive indicators:
- The string is a real credential you use in production or development
- It matches the format of actual service API keys (e.g., AWS keys starting with "AKIA")
- It's a connection string with real database credentials
- It's a private key file or PEM-encoded content
False positive indicators:
- It's example/placeholder text from documentation
- It's a test fixture with fake credentials that happen to match patterns
- It's a high-entropy string that isn't actually a secret (UUID, hash, etc.)
- It's in a file that's meant to be committed (like a public key)
If it's a true positive: Continue to step 3 to remove the secret.
If it's a false positive: Skip to step 5 to bypass the check.
If the secret is in your most recent (unpushed) commit, you can amend it:
Step 1: Remove or replace the secret in the file
# Edit the file to remove the hardcoded secret
nano config/settings.py
# Replace the secret with an environment variable reference
# Before: API_KEY = "sk-abc123..."
# After: API_KEY = os.environ.get("API_KEY")Step 2: Stage the changes and amend the commit
git add config/settings.py
git commit --amend --no-editStep 3: Try pushing again
git push origin mainImportant: If the secret was ever committed (even if you amend), consider it compromised. Rotate the credential immediately - see step 6.
If the secret is in a commit further back in your unpushed history, you need to rewrite history.
Option A: Interactive rebase (for a few commits)
# Find how many commits back the secret is
git log --oneline
# Start interactive rebase (e.g., last 5 commits)
git rebase -i HEAD~5In the editor, change pick to edit for the commit containing the secret. Save and close. Then:
# Edit the file to remove the secret
nano config/settings.py
# Stage and amend
git add config/settings.py
git commit --amend --no-edit
# Continue the rebase
git rebase --continueOption B: Use git-filter-repo (for multiple occurrences)
# Install git-filter-repo
pip install git-filter-repo
# Replace a specific secret string throughout history
git filter-repo --replace-text <(echo 'sk-abc123secretkey==>REMOVED')
# Or remove a file entirely from history
git filter-repo --path secrets.env --invert-pathsOption C: Use BFG Repo-Cleaner (faster for large repos)
# Download BFG (requires Java)
# Create a file listing secrets to remove
echo "sk-abc123secretkey" >> secrets.txt
echo "AKIA12345EXAMPLE" >> secrets.txt
# Run BFG
java -jar bfg.jar --replace-text secrets.txt my-repo.git
# Clean up
cd my-repo.git
git reflog expire --expire=now --all
git gc --prune=now --aggressiveAfter rewriting history, you'll need to force push (with caution):
git push --force-with-lease origin mainIf GitLab detected a false positive (not an actual secret), you can skip the check.
Method 1: Push option flag
git push -o secret_push_protection.skip_all origin mainMethod 2: Commit message directive
Add [skip secret push protection] to a commit message:
# Amend the last commit to add the skip directive
git commit --amend -m "Add configuration file [skip secret push protection]"
# Or create a new commit with the directive
git commit --allow-empty -m "Enable push [skip secret push protection]"
git push origin mainImportant considerations:
- Only use this for confirmed false positives
- The skip applies to all secrets in the push, so verify there are no real secrets mixed in
- Consider reporting persistent false positives to your GitLab administrator
- Your organization may have policies against skipping secret detection
Critical: If a real secret was committed, treat it as compromised even if you removed it before pushing successfully. The secret may exist in:
- Your local reflog
- Other developers' local copies
- Backup systems
- Shell history
Rotation checklist by secret type:
API Keys/Tokens:
# Generate a new key in the service's dashboard
# GitLab: Settings > Access Tokens > Revoke old, create new
# AWS: IAM > Security credentials > Create new access key
# Then update your application/environmentDatabase credentials:
-- Change the password
ALTER USER myuser PASSWORD 'new-secure-password';Private keys:
# Generate a new key pair
ssh-keygen -t ed25519 -C "[email protected]"
# Add the new public key to services
# Remove the old public key from all authorized_keys filesAfter rotation:
1. Update all systems using the old credentials
2. Verify the old credentials no longer work
3. Monitor for unauthorized access using audit logs
Set up preventive measures to avoid this issue in the future:
1. Use environment variables
# .env file (add to .gitignore!)
DATABASE_URL=postgres://user:pass@host/db
API_KEY=sk-abc123...
# In your code
import os
api_key = os.environ.get("API_KEY")2. Add sensitive patterns to .gitignore
# .gitignore
.env
.env.local
*.pem
*.key
id_rsa
secrets.json
credentials.json3. Install pre-commit hooks
# Using git-secrets (AWS)
brew install git-secrets
git secrets --install
git secrets --register-aws
# Using detect-secrets
pip install detect-secrets
detect-secrets scan > .secrets.baseline4. Use a secrets manager
Instead of environment variables, consider:
- HashiCorp Vault
- AWS Secrets Manager
- Azure Key Vault
- GitLab CI/CD Variables (for pipelines)
5. Enable GitLab pipeline secret detection
Add to your .gitlab-ci.yml:
include:
- template: Security/Secret-Detection.gitlab-ci.ymlThis scans your entire repository periodically, catching secrets that bypass push protection.
How GitLab Secret Push Protection Works:
Secret Push Protection (introduced in GitLab 15.x, GA in 17.11) uses pattern matching to detect secrets. It scans only the diffs of pushed commits, not entire files, which means:
1. Secrets already in the repository aren't detected (use pipeline secret detection for full scans)
2. Only changes pushed via HTTP(S) or SSH are scanned
3. Web IDE commits may bypass push protection (check your version)
Detection Patterns:
GitLab uses high-confidence patterns to minimize false positives. Common patterns include:
| Secret Type | Pattern Example |
|-------------|-----------------|
| GitLab PAT | glpat- prefix |
| AWS Access Key | AKIA prefix, 20 chars |
| GitHub Token | ghp_, gho_, ghs_ prefixes |
| Private Keys | -----BEGIN RSA PRIVATE KEY----- |
Exclusions and Custom Rules:
GitLab administrators can configure:
- Path exclusions (e.g., test/ directories)
- Custom patterns to detect organization-specific secrets
- Allowlists for known false positives
Check with your GitLab admin if you're experiencing persistent false positives.
Enterprise Considerations:
In enterprise environments:
- Secret push protection may be enforced at the group or instance level
- Skipping may require special permissions or be disabled entirely
- Audit logs track when secrets are detected and when protection is skipped
- Consider integrating with SIEM for security monitoring
When Secrets Were Already Pushed:
If secrets were pushed before protection was enabled:
1. Use git log -p --all -S 'secret_string' to find all commits containing the secret
2. Rewrite history using git-filter-repo or BFG
3. Force push the cleaned history
4. Notify all team members to re-clone or rebase
5. Clear CI/CD caches that may contain the old commits
6. Request GitLab support to clear server-side caches if using GitLab.com
Handling Merge Requests:
If your MR is blocked by secret detection:
1. Fix the secret in your feature branch
2. Force push the corrected branch
3. The MR will update automatically
4. CI/CD will re-run on the new commits
CI/CD Pipeline Failures:
If your pipeline fails at the push step:
deploy:
script:
- git push -o secret_push_protection.skip_all https://[email protected]/repo.gitOnly use this if the "secrets" are false positives (like generated hashes in build artifacts).
fatal: bad object in rev-list input
Git rev-list encounters bad or invalid object
fatal: Out of memory, malloc failed during pack operation
Out of memory during Git pack operation
kex_exchange_identification: Connection closed by remote host
Connection closed by remote host when connecting to Git server
fatal: unable to access: Proxy auto-configuration failed
How to fix 'Proxy auto-configuration failed' in Git
fatal: unable to access: Authentication failed (proxy requires basic auth)
How to fix 'Authentication failed (proxy requires basic auth)' in Git