Safeguard
Developer Tools•

Does setup-node cache node_modules? CI dependency caching and risk

The setup-node cache option stores package manager data keyed by your lockfile, not node_modules. See why that is safer and how cache poisoning fits in.

Safeguard Security Team
3 min read

No. The setup-node GitHub Action caches package manager data, such as downloaded packages, and does not cache node_modules. The cache key comes from a hash of your lockfile. That design is safer, because a restored cache of downloads still goes through a clean install and integrity checks.

What the action caches

The setup-node README says the cache input sets up caching of package manager data, with npm, yarn and pnpm supported. It uses the cache action under the hood for global packages data, and states that the action does not cache node_modules.

By default it looks for package-lock.json, npm-shrinkwrap.json or yarn.lock in the repository root and uses its hash as part of the key. A cache dependency path option covers multiple or non-root lockfiles. In version 6 and later, npm caching turns on automatically when package.json sets packageManager or devEngines.packageManager to npm and no cache input is given.

Why caching downloads is safer than caching node_modules

A cached node_modules directory is a finished product. If anything in it was altered, a later job runs it without question. A cache of downloaded package archives is an input. The install step still resolves from the lockfile, and the lockfile holds integrity hashes, so a modified archive fails verification.

Cache poisoning is a known class of problem in CI, where a job that can write to a shared cache plants content a later, more privileged job restores. See GitHub Actions cache poisoning for that attack class.

Safer caching steps

  1. Cache the package manager download cache, not node_modules.
  2. Key the cache on the lockfile hash so a changed dependency set gets a fresh cache.
  3. Keep untrusted pull request jobs away from caches that release jobs use.
  4. Use the strict install command so the lockfile and its hashes are enforced.
  5. Run release builds with a clean cache where practical.
What you cacheVerified on restore?Risk if tampered
Downloaded packagesYes, via lockfile integrityLower
node_modulesNoHigher
Build outputDepends on your pipelineDepends on what consumes it

Common mistakes

Teams sometimes add a second cache step that stores node_modules to save a few seconds, undoing the safer default. Others share one cache between pull request builds and release builds, which lets an untrusted job influence a trusted one. A third mistake is a cache key that ignores the lockfile, so a changed dependency set restores stale content. If speed is the concern, measure first. A package download cache with a strict install is usually fast enough, and it keeps verification in the path.

Quick check

What is the setup-node cache key based on?

A hash of the dependency file it finds, such as package-lock.json, npm-shrinkwrap.json or yarn.lock, unless you set a cache dependency path.

Why not cache node_modules?

It skips the verification a fresh install would do, so tampered contents would be used as is. The action does not cache it.

Safeguard scans dependencies from the lockfile and monitors for new advisories after merge. Try the free scan at gold.safeguard.sh/scan.

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.