Safeguard
Developer Tools•

What does the caret mean in package.json versions?

A caret in package.json lets npm install newer compatible versions. Here is what it allows, why the lockfile matters, and when a new developer should pin.

Safeguard Security Team
3 min read

A caret such as ^1.5.2 in package.json means "compatible with" that version. npm may install any newer version that keeps the same leftmost non-zero part, so 1.9.0 qualifies but 2.0.0 does not. The range is a permission, not a pin. The lockfile decides what you actually get.

Ranges in one table

The npm package.json documentation lists the common range operators. Here is how the ones a new developer meets first behave.

EntryMeaningExample match
1.5.2Exactly this version1.5.2 only
^1.5.2Compatible with 1.5.21.5.2 up to below 2.0.0
~1.5.2Approximately equivalent1.5.x patches
1.5.xAny patch of 1.51.5.0, 1.5.1
*Any versioneverything

The caret is the default npm writes when you add a package, unless you ask for an exact version. The documentation for npm install describes a save-exact flag for that: saved dependencies get an exact version instead of npm's default range operator.

Why this matters for security

A range is a standing invitation. If a maintainer publishes 1.9.0 tomorrow, a fresh install without a lockfile can pick it up. Most of the time that is a bug fix you wanted. Occasionally it is a compromised release. The range cannot tell the difference.

That is why the lockfile exists. The npm documentation says package-lock.json describes the exact tree that was generated, so later installs produce identical trees regardless of intermediate dependency updates. With a committed lockfile, the caret only matters when someone deliberately updates.

What a new developer should do

  1. Leave the existing ranges alone unless your team has a pin policy.
  2. Commit package-lock.json with every dependency change, in the same pull request.
  3. Use npm ci on a fresh clone so the lockfile is honored instead of rewritten.
  4. When you add a package, read what npm wrote to package.json and ask whether the team prefers exact versions.
  5. Never use * for a dependency. It removes all control.

There is a real tradeoff between ranges and exact pins. Exact pins give predictability but make you responsible for every update, which is easy to forget. Ranges plus a lockfile plus a regular update routine is the arrangement many teams settle on. Our post on dependency pinning versus ranges goes deeper.

Where Safeguard fits

Safeguard can do the inventory and watching for you. Its SBOM and dependency scanning lists what a repository actually pulls in, reachability analysis helps separate reachable findings from noise, and policies can gate a pull request. You can try a free scan on a repository to see the output.

Quick check

Does ^0.2.3 behave like ^1.2.3?

No. For versions below 1.0.0, the leftmost non-zero part is the minor number, so the caret range is narrower. Check the semver notes linked in the npm docs before relying on it.

If I have a lockfile, can a caret still hurt me?

Only at update time. Running npm update or deleting the lockfile lets npm pick newer versions inside the range. Review that diff like any other code change.

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.