Safeguard
Security Guides•

GitLab protected branches: setup and common mistakes

How to configure GitLab protected branches so nobody pushes straight to production: roles, the No one setting, wildcards and the most permissive rule trap.

Safeguard Security Team
3 min read

In GitLab, set "Allowed to push and merge" to No one on your production branch and limit "Allowed to merge" to Maintainers. Then changes only land through merge requests. Be careful with wildcards, because when several rules match a branch the most permissive one controls.

The defaults

GitLab protects the default branch by default. But the documentation notes that an unconfigured "Allowed to push and merge" does not restrict push access, so to block direct pushes you must explicitly set it to No one. By default, Maintainers and Owners can unprotect a branch.

The two main settings

  • Allowed to merge: roles that can merge through merge requests and create protected branches.
  • Allowed to push and merge: roles, plus deploy keys, that can push and merge. Groups and individual users can be added on Premium and Ultimate.

GitLab's recommended setup: production branches use Maintainers only for merge and No one for push and merge, with code owner approval. Active development branches can allow Developers and Maintainers to merge.

For merge controls to work, set Allowed to push and merge to a broader set of users than Allowed to merge, as the documentation notes.

How to set it up

In a project, with Maintainer or Owner role: go to Settings, Repository, Branch rules, then Add branch rule. Enter a branch name or pattern, create the rule, and edit the two settings.

Group level protected branches exist for top level groups on Premium and Ultimate. That is a good way to apply one standard everywhere.

Force push and code owners

Force pushes are controlled by the Allowed to force push option. Leave it off for protected branches.

Code owner approval is Premium and Ultimate. With it on, merge requests need approval from a code owner for each matched rule, and direct pushes to the matched branch are denied. If multiple rules match, it is required when any of them enables it.

The wildcard trap

You can use * to protect many branches, such as production/* or *-stable. Names are case sensitive. When several rules match a branch, the most permissive rule controls its behavior.

Example: you protect release/* strictly, but an older rule for * lets Developers push. A release branch then accepts developer pushes. Audit overlapping patterns after every change.

A checklist

  1. Production and release branches: push No one, merge Maintainers.
  2. Force push off.
  3. Code owner approval where your tier allows.
  4. Check the list of who can unprotect. On Premium and Ultimate you can control this through the API.
  5. Review wildcard overlaps.
  6. Pair with merge request approval rules.

Safeguard policies and gates can add a security check to the merge request flow. The free scan is at gold.safeguard.sh/scan.

Quick check

Is the default branch safe out of the box?

It is protected, but direct push is not blocked until you set push and merge to No one.

Which rule wins when two match?

The most permissive one controls behavior, so review overlapping wildcards.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.