Safeguard
Developer Tools•

Caret or tilde in composer.json: which version constraint is safer?

Caret allows non breaking updates up to the next major. Tilde is tighter on the last digit you write. What each allows, and why open ranges are risky.

Safeguard Security Team
3 min read

Tilde is usually the tighter of the two for applications, and caret is the Composer recommendation for libraries. Caret ^1.2.3 allows up to but not including 2.0.0. Tilde ~1.2.3 allows up to but not including 1.3.0. Whichever you pick, avoid unbounded ranges.

What each operator allows

ConstraintMeansAllows
1.0.2Exact versionOnly that version
^1.2.3Caret1.2.3 up to below 2.0.0
^0.3Caret on 0.x0.3.0 up to below 0.4.0
~1.2Tilde1.2 up to below 2.0.0
~1.2.3Tilde with patch1.2.3 up to below 1.3.0
1.0.*Wildcard1.0 up to below 1.1
>=1.0UnboundedAny future version, including breaking ones

The Composer docs call the caret operator the recommended one for maximum interoperability when writing library code. For 0.x versions it is stricter, because semantic versioning treats those as unstable.

Why unbounded ranges are a supply chain risk

A constraint like >=3.4 or * accepts any future major release. The Composer FAQ points out that once a version is tagged, its dependencies cannot be changed, so a broken release stays broken. The security view is similar: an open ended range gives a new owner of a package, or an attacker who gets in, a path into every project that did not set an upper bound. A lock file softens this for applications, but not for the next person who runs update.

How to choose

  1. For an application, prefer constraints that match how much change you will review. Tilde with a patch number is tight. Caret is looser.
  2. For a library, use caret so your users can resolve against other packages.
  3. Never use an open ended range for a package you do not control.
  4. Avoid dev branches as constraints in anything that deploys.
  5. Rely on composer.lock for exact versions, and treat constraints as the policy for what update may pick.
composer require vendor/package:^2.4
composer outdated --direct

A practical habit

Constraints say what is allowed. The lock file says what is installed. When you bump a constraint, read the lock diff. A wide constraint plus a casual composer update is how a surprise reaches production.

Try it yourself

Click through it at your own pace, or open it full screen. See how a constraint decides whether a patched version can be taken.

Where Safeguard fits

Safeguard's SBOM and dependency scanning reads composer.lock and Gemfile.lock and flags known advisories. A free scan takes a few minutes.

Quick check

Is caret or tilde riskier?

Caret generally allows more change, since it spans to the next major. Tilde with a patch number allows less.

Do constraints matter if I have a lock file?

Yes. They decide what composer update can choose the next time it runs.

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.