GitHub push protection blocked your push (GH013: Push cannot contain secrets)

Nothing reached GitHub. Take the secret out of every unpushed commit that contains it, then push again. Use the bypass link only for false positives and test values.

You ran git push and got GH013: Repository rule violations found with Push cannot contain secrets. GitHub secret scanning found something that looks like a real credential in one of the commits you were pushing, and push protection rejected the whole push. The output looks like this (shortened):

remote: error: GH013: Repository rule violations found for refs/heads/main.
remote:
remote: - GITHUB PUSH PROTECTION
remote:   —————————————————————————————————————————
remote:     Resolve the following violations before pushing again
remote:
remote:     - Push cannot contain secrets
remote:
remote:      (?) Learn how to resolve a blocked push
remote:      https://docs.github.com/code-security/secret-scanning/...
remote:
remote:       —— GitHub Personal Access Token ——————————————————————
remote:        locations:
remote:          - commit: 8728dbe67
remote:            path: config/settings.py:4
remote:
remote:        (?) To push, remove secret from commit(s) or follow this URL to allow the secret.
remote:        https://github.com/OWNER/REPO/security/secret-scanning/unblock-secret/...
remote:
To github.com:OWNER/REPO.git
 ! [remote rejected] main -> main (push declined due to repository rule violations)
error: failed to push some refs to 'github.com:OWNER/REPO.git'

The short answer: the push was rejected, so the commits are not on GitHub. Deleting the secret in a new commit does not fix it, because the push still contains the older commit that added it. You have to rewrite your unpushed commits so none of them contains the secret, then push normally. No force push is needed, because you are only changing commits GitHub never accepted.

1. Find the commits that contain the secret

The locations block lists each commit and path:line where the secret was found. GitHub shows up to five detected secrets at a time, so there may be more after you fix these. To see every commit you are about to push, compare your branch with its remote branch (replace origin/main with your branch's upstream):

git log --oneline origin/main..HEAD

To narrow that to commits that touched the flagged file:

git log --oneline origin/main..HEAD -- config/settings.py

Search by file rather than by the secret's value. Typing the key into git log -S puts it in your shell history, which is another copy to clean up.

2. Move the secret out of the code

Replace the hard-coded value with an environment variable or a config file that git ignores. If the flagged file is a .env file, stop tracking it and ignore it. git rm --cached removes it from the index and leaves it on disk:

git rm --cached .env
echo ".env" >> .gitignore
git add .gitignore

Then rewrite the commits with whichever of the next three options fits.

3. If only your latest commit has the secret: amend it

After editing the file, fold the fix into the last commit. This is GitHub's documented fix:

git commit --amend --all

--all stages changes to files git already tracks, so it picks up your edit. If you used git rm --cached and staged .gitignore as in step 2, run git commit --amend --no-edit instead. Check that the file or line is gone with git show --stat HEAD, then git push.

4. If several unpushed commits have it: squash them with reset --soft

If you do not need to keep the individual commits, the simplest route is to undo them while keeping all their changes staged, remove the secret once, and make a single new commit:

git reset --soft origin/main
git status

All the work from those commits is now staged and nothing is lost. Remove the secret (edit the file, or git rm --cached .env), then commit and push:

git commit -m "Add settings loader"
git push

git reset --soft HEAD~3 does the same for the last three commits if you would rather count. Never use --hard here: it discards the changes instead of keeping them staged.

5. If you want to keep your commits: interactive rebase

To fix the commit in place and keep the rest of your history, follow GitHub's steps:

  1. From the error message and git log, find the earliest commit that contains the secret, for example 8728dbe67.
  2. Start an interactive rebase from its parent: git rebase -i 8728dbe67~1
  3. In the editor, change pick to edit on that commit's line, then save and close.
  4. Git stops at that commit. Remove the secret from the file, then run git add . and git commit --amend.
  5. Run git rebase --continue. If later commits also contain the secret, mark each of them edit as well, or resolve the conflicts as git replays them.
  6. Run git push.

One catch: if the commit did nothing except add the secret file, amending it leaves it empty and git refuses with You asked to amend the most recent commit, but doing so would make it empty. In that case change pick to drop for that commit instead of edit, as long as no later commit changes the same file.

Before pushing, read the diff of everything you are about to send and confirm the value is gone:

git log -p origin/main..HEAD

6. The bypass URL: when it is the right call

The URL after To push, remove secret from commit(s) or follow this URL to allow the secret opens a page where you can allow this one secret. It only works for the user who made the push; anyone else gets a 404. Depending on the repository's settings, you pick a reason:

Click Allow me to push this secret, then run git push again within three hours, or you have to repeat the process. When the repository has secret scanning enabled, the bypass is not silent: GitHub creates a secret scanning alert (closed for the first two reasons, open for the third) and notifies the repository's admins and owners, with the reason you chose.

The first two reasons are what the bypass is for. "I'll fix it later" means the live credential goes into the repository, where everyone with read access, every clone and every fork can see it, and removing it later means rewriting pushed history (see how to remove an API key from git history). It is almost always cheaper to spend two minutes on step 3 or 4. If you did push a real secret, revoke it at the provider: what to do when you leak an API key.

If you see no bypass option, your organization uses delegated bypass. The same page lets you submit a request with a comment. If it is approved, you can push that secret, including in future commits. If it is denied, you have to remove it from the commits.

7. Do you need to rotate the key?

Push protection stopped it, so the secret did not land on GitHub. Rotate it anyway if it went anywhere else: another remote, a CI system, a chat message or a pasted log, or if you or a teammate used the bypass. If the key never left your laptop, moving it out of the code is enough.

8. Other things that trip push protection

To catch the secret before it is even committed, run a scanner as a pre-commit hook: see how to scan for secrets before you push. Push protection only recognizes supported secret formats, so a hook plus a gitignored .env is a better first line.