Safeguard
Developer Tools•

How do I point an npm scope at a private registry?

Add one line to .npmrc to send an npm scope to your private registry, scope the auth token correctly, and check that nothing falls back to the public one.

Safeguard Security Team
3 min read

Add a line of the form @yourscope:registry=https://your-registry.example to the project's .npmrc. npm then fetches every package in that scope from that URL, not from the public registry. Scope the auth token to the same host so credentials are never sent elsewhere.

The configuration

The npm documentation gives this form for a scoped registry:

# .npmrc in the project root
@acme:registry=https://registry.internal.example/
//registry.internal.example/:_authToken=${ACME_NPM_TOKEN}

Line one maps the scope. Line two supplies a token for that host only, read from an environment variable so the secret is not committed.

The documentation says credential settings must be scoped to a specific registry, and shows a bare token setting as a bad configuration. A path prefix limits a token further: a token at the host level applies to every scope on that host, while one at a path applies only to that path's scope.

Where the file lives

npm reads, in the order the documentation lists them, a per-project file next to package.json, a per-user file in your home directory, a global file, and a built-in one. Settings resolve by priority, and a user setting overrides the same global one.

Put the scope mapping in the project file and commit it. That way CI and every developer resolve the scope the same way. Keep the token itself out of the repository.

Why this blocks one kind of confusion

Without a mapping, a request for @acme/billing goes to the default registry. With it, the request for any @acme package goes only to your registry. An attacker cannot win by publishing a same-named package publicly, because npm never asks there.

Note what it does not cover:

  • Unscoped internal names. The mapping applies to a scope. Move internal packages under a scope.
  • Lockfile hosts. The config page describes a setting for replacing the registry host recorded in a lockfile. Review how your lockfile and registry host settings interact so you know where installs really come from.
  • Other ecosystems. Each has its own control. See how to protect your package names.

Verify it works

npm config get @acme:registry
npm view @acme/billing dist.tarball

The second command prints the tarball URL. It should point at your registry host. Then block the private host in a test environment and confirm the install fails rather than falling back to the public registry.

A short rollout plan

  1. Move any unscoped internal package under your scope first. Rename it, publish it to your registry, and update the imports.
  2. Commit the scope mapping and the token reference to the template your new repositories start from.
  3. Add a CI step that prints the configured registry for your scope and fails if it is not your host.
  4. Audit existing lockfiles for packages in your scope whose resolved URL points at the public registry.

Do this once and new projects inherit the safe default.

Quick check

Can I commit the .npmrc file?

Commit the scope mapping. Do not commit a literal token. Reference an environment variable, as in the example above.

Does this replace claiming my scope publicly?

No. Mapping protects your builds. Claiming the scope on the public registry stops others from using it at all. Do both.

To check where your dependencies resolve from, run a free scan. Safeguard does SBOM and dependency scanning for npm projects.

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.