It should not. A theme or plugin that uses npm for its build should ship only the compiled CSS, JavaScript and images to production. If node_modules, package.json or build scripts reach the live server, you have added code and attack surface that the site does not need.
This is a common pattern in agency work. The theme is built with a bundler, and someone zips the whole folder and uploads it.
Why it matters
Build tools run code on your machine or CI server when you install them. They also pull in a deep tree of transitive packages. None of that is needed to serve a page. On a live server it is only files that can be found, read or misused, and files that nobody patches because nobody knows they are there.
A pre-launch checklist
- Run the build in CI from a clean checkout using npm ci. The npm documentation describes it as an install meant for automated environments, requiring an existing lockfile and exiting with an error if the lockfile does not match package.json.
- Package the output, not the project folder. Include built assets, PHP templates and the files WordPress needs.
- Exclude node_modules, test folders, source maps you do not want public, and package manager files from the artifact.
- Deploy the artifact, not a copy of the working directory.
- Check the live server: list the theme directory and confirm nothing from the build tree is present.
What to check on the server
| Item | Present on production? | Action if yes |
|---|---|---|
| node_modules directory | Should be no | Remove and fix the deploy |
| package.json and lockfile | Usually no | Remove from artifact unless needed |
| Source maps | Decide on purpose | Remove if they expose private source |
| .git directory | Should be no | Remove and block access |
| Build config files | Usually no | Exclude from artifact |
Lockfiles still belong in the repository
Keep package-lock.json in version control even if it does not ship. It is how CI and teammates get the same versions. Composer's documentation makes the same argument for composer.lock for applications: everyone runs the same dependencies, and reinstalling in six months gives the same result.
Scan the right thing
A scan of the repository tells you what the build uses. A scan of the live folder tells you what you shipped. Do both occasionally. If the live folder contains a dependency tree, that is a finding by itself.
Handling install scripts
Packages can run scripts during install. Consider whether your CI needs them, and review what a new package adds before merging. This applies to build time, not production, so the main protection is a clean separation between build and serve.
Try it yourself
Click through it at your own pace, or open it full screen. Look at how many packages sit under a handful of direct dependencies, which is why build trees should not be uploaded.
Safeguard scans package manifests and generates an SBOM, and the free scan is a way to see the tree for a theme repository.
Quick check
Should package-lock.json be committed for a theme?
Yes, in the repository. It does not need to be uploaded to the live server.
Is it a problem if node_modules is on the server but not linked anywhere?
It is still unnecessary code on disk. Remove it, since unlinked files can often be requested directly.
Sources
Last reviewed: 19 October 2026.