Add "private": true to the package.json of any application that should never go to a registry. The npm docs say npm will then refuse to publish it. For packages you do publish, use the files field to list exactly what ships, so source, tests and secrets stay out.
Two different jobs
The npm package.json reference separates these controls:
private: setting it to true makes npm refuse to publish the package. The docs describe it as a guard against accidental publication. If you want publishing only to a specific registry, such as an internal one, usepublishConfigto override the registry at publish time instead.files: an array of patterns that decides what goes into the tarball when the package is installed as a dependency. It works like.gitignorein reverse, in that matching entries are included.
Why accidental publishing is a real risk
An internal service often holds configuration, test fixtures, internal hostnames and comments that were never meant for the public. Publishing it to the public registry exposes all of that, and it can also create a name that someone else could later confuse with an internal package. Removing a published version does not undo copies others made.
The same applies to libraries. If files is omitted, the docs say it defaults to ["*"], which includes everything not otherwise ignored. A stray .env.production or a folder of test data would ship.
What npm always leaves out, and always includes
From the docs:
- Always included:
package.json, the README, the LICENSE, themainfile and thebinfiles. - Always ignored by default:
.git,node_modules,.npmrc, and lockfiles such aspackage-lock.jsonandyarn.lock. - Most default ignored files can be re-included through
filespatterns, but.git,.npmrc,node_modulesand lockfiles cannot. - A root level
.npmignoredoes not overridefiles, but one in a subdirectory does. With no.npmignore, npm uses.gitignore.
Note that an env file you forgot to put in .gitignore is not on the always ignored list.
A checklist
- For every application, monorepo root and internal tool, set
"private": true. - For every published package, set
filesto an allow list such as the build output folder. - Run
npm pack --dry-runand read the file list before every first publish and after any packaging change. - Add that dry run to CI and fail if unexpected paths appear.
- Keep secrets out of the repo to begin with. See where a Node.js app should keep secrets.
- Use scoped names and a registry setting for internal packages, so a publish goes to the right place.
What this does not cover
private stops npm publish. It does not stop someone copying the code elsewhere, and it does not protect an internal name from being claimed on the public registry by someone else. For the second problem, read about dependency confusion defenses.
Safeguard dependency scanning and SBOM generation work on the packages you consume, which complements checking what you publish. A free scan is a quick start.
Quick check
Does private true stop npm install of the package?
No. It only makes npm refuse to publish it, per the npm docs.
What happens if I do not set the files field?
It defaults to everything, minus npm's always ignored items. Run npm pack --dry-run to see what would ship.
Sources
Last reviewed: 19 October 2026.