Branch Protection Rules: Securing Your Main Branch on GitHub
Stop accidental pushes to your main branch. Learn to enable branch protection, enforce status checks, and secure your GitHub repository with CI gates.

Previously in this course, we explored failing on lint errors to ensure our code meets quality standards. While automated linting catches errors, it doesn't stop a developer—or a stray script—from accidentally pushing broken code directly to your main branch.
In this lesson, we close that gap by implementing branch protection. This is the final gate in our CI/CD process, ensuring that no code reaches our primary branch without first passing all defined status checks.
Why Branch Protection Matters
In a professional environment, the main branch is the source of truth. If it breaks, your production deployment breaks. Relying on "team discipline" to avoid pushing to main is not a strategy; it’s a failure waiting to happen.
By enabling branch protection, we move from a "trusted" model to a "verified" model. GitHub becomes the enforcer, blocking any git push that bypasses your CI/CD pipeline.
Understanding the Protection Layers
When you enable branch protection, you aren't just flipping a switch; you are defining a security contract. The two most critical controls for our current project are:
- Restrict pushes: Prevents any user from pushing directly to the branch. This forces everyone to use Pull Requests.
- Require status checks: Forces the repository to wait for CI (like your linting and testing jobs) to report a "success" status before the "Merge" button becomes active.
Hands-on: Applying Protection Rules
You don't need a configuration file for this; it’s a repository setting. Follow these steps to secure your project:
- Navigate to your repository on GitHub.
- Click the Settings tab.
- On the left sidebar, click Branches.
- Click Add branch protection rule.
- In the "Branch name pattern" field, type
main. - Check Require a pull request before merging.
- Check Require status checks to pass before merging.
- In the status checks search box, select the specific jobs you want to require (e.g., your
lintortestjobs). - Click Create or Save changes.
Worked Example: The "Failed Check" Scenario
Once these rules are active, the behavior of your repository changes immediately. Imagine a developer tries to merge a PR that contains failing tests:
- Before Protection: The "Merge" button is always green. A mistake could easily be merged.
- After Protection: The GitHub UI will show a message: "Required status check 'test' failed." The "Merge" button will be greyed out and unclickable.
Even if someone tries to force the issue via the command line, the server-side hook will reject the push:
Bash$ git push origin main ! [remote rejected] main -> main (protected branch hook declined) error: failed to push some refs to 'github.com/your-repo'
Practice Exercise
Now it’s your turn to lock down the repository from our previous lessons:
- Verify that your CI pipeline is currently passing.
- Follow the steps above to enable branch protection for
main. - Attempt to push a small, non-breaking change directly to
mainfrom your local machine. - Observe the rejection error in your terminal—congratulations, your security gate is working!
Common Pitfalls
- Forgetting to set the pattern: Ensure the branch name pattern exactly matches your branch name (e.g.,
mainvsmaster). - Missing status checks: If you rename a job in your YAML file, you must update the branch protection settings to reflect the new job name, or the "Merge" button will be permanently locked.
- Admin lockouts: Admins can sometimes bypass these rules. Make sure you check "Do not allow bypassing the above settings" if you want to enforce these rules even for repository owners.
FAQ
Q: Can I still merge if the build is just "skipped"? A: No, branch protection requires a "success" state. If a job is skipped due to conditional logic, it might prevent a merge. Always ensure your status checks are configured to run under the conditions you expect.
Q: Does this work for other branches?
A: Yes. You can use wildcards in the branch name pattern (like release/*) to protect multiple branches at once.
Recap
We’ve successfully moved from manual discipline to automated enforcement. By enabling branch protection, we've guaranteed that our main branch remains stable, verified, and secure, building on the foundations we laid when managing secrets and handling environment variables.
Up next: Now that the branch is protected, we need to ensure that humans are actually looking at the code, too. We’ll learn about Mandatory Reviewers.
Work with me

CI/CD Pipeline & Docker Containerization
Ship with confidence: automated CI/CD pipelines and Docker setups so every push is tested and deployed — no more manual, error-prone releases.

VPS Server Setup, Deployment & Hardening
Get your app live on a fast, secure server — properly configured, hardened, and deployment-ready. No more wrestling with the command line.

