Safeguard
Developer Tools•

Composer constraints and composer.lock: what to commit and when

Commit composer.lock for PHP applications, not necessarily libraries. How caret and tilde constraints work and how composer install differs from update.

Safeguard Security Team
4 min read

For a PHP application, commit composer.lock and deploy with composer install, which uses the exact versions in the lock. For a library, the Composer documentation says committing the lock file is not necessary. The constraints in composer.json state what you accept; the lock records what you got.

How the main constraints behave

Composer's versions article explains the two operators most projects use.

ConstraintEquivalent rangeNotes
~1.21.2 up to but not including 2.0.0Last digit you wrote can rise
~1.2.31.2.3 up to but not including 1.3.0Patch updates only
^1.2.31.2.3 up to but not including 2.0.0Sticks closer to semantic versioning
^0.30.3.0 up to but not including 0.4.0Pre 1.0 handled conservatively
1.0.*1.0 up to but not including 1.1Wildcard

The documentation describes the caret as the recommended operator for maximum interoperability. It also warns that unbounded ranges can install versions that break backwards compatibility, and suggests the caret instead.

Install and update are different commands

Per the basic usage guide, the update command resolves everything in composer.json, writes exact versions to composer.lock, then installs. You should commit composer.lock so that everyone on the project is locked to the same versions.

When composer.lock already exists, running install resolves and installs what composer.json lists but uses the exact versions in the lock. The documentation says some dependencies may therefore not be at the latest available version, and that this is by design, so your project does not break because of unexpected changes.

composer install

Composer also states that it enables reproducible builds by default: running the same command repeatedly produces a vendor directory with identical files, except timestamps, including the autoloader files.

A routine for applications

  1. Keep composer.json constraints loose enough for fixes: caret for most packages.
  2. Commit composer.lock in the same change as any constraint edit.
  3. Deploy with composer install, never update, on servers and in CI.
  4. Run update in a dedicated branch, read the lock diff, test, merge.
  5. Add the vendor directory to .gitignore if your team does not commit it. The documentation suggests this for projects using git.

What about libraries

A library's consumers do not use your lock file. They resolve from your composer.json constraints. So state honest ranges, and treat the lock file, if you keep one, as a tool for your own tests. Use a caret on dependencies you have tested against and do not leave them unbounded.

Where drift hides

The lock fixes the version and the project holds its range, but a dependency's release tag is still a mutable pointer in version control. Composer's own documentation notes that Composer versions are resolved from version control refs such as tags and branches. Review lock diffs for unexpected sources or branch references, not just version numbers.

Where Safeguard fits

Safeguard's dependency scanning and SBOM generation can check what composer.lock resolves against known vulnerabilities, and you can try a free scan at gold.safeguard.sh/scan.

Try it yourself

Open the demo and follow how a vulnerable package is flagged and tracked, then think about which lock file diff would have fixed it.

Click through it at your own pace, or open it full screen.

Quick check

Should a PHP library commit composer.lock?

The Composer documentation says it is not necessary for libraries, because consumers resolve from composer.json.

Why did composer install not give me the newest version?

Because composer.lock exists. Install uses the exact versions in the lock by design. Run update when you want to move them.

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.