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
- Cache the package manager download cache, not node_modules.
- Key the cache on the lockfile hash so a changed dependency set gets a fresh cache.
- Keep untrusted pull request jobs away from caches that release jobs use.
- Use the strict install command so the lockfile and its hashes are enforced.
- Run release builds with a clean cache where practical.
| What you cache | Verified on restore? | Risk if tampered |
|---|---|---|
| Downloaded packages | Yes, via lockfile integrity | Lower |
| node_modules | No | Higher |
| Build output | Depends on your pipeline | Depends 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.