Is there an existing issue for this?
This issue exists in the latest npm version
Current Behavior
With allow-remote set to none or root (the none default on npm 12), a npm install fails with EALLOWREMOTE on ordinary registry dependencies whenever the configured registry origin differs from the resolved URL origin recorded in the lockfile:
npm error code EALLOWREMOTE
npm error Fetching packages of type "remote" have been disabled
This is the common proxy/mirror case: a committed package-lock.json whose resolved URLs point to https://registry.npmjs.org/..., while the machine (or CI) is configured to use a private registry proxy/mirror with a different origin. Every registry tarball is then misclassified as a genuine remote tarball and blocked.
It affects both the hoisted and the linked install strategy — it is not strategy specific. The same install succeeds when allow-remote=all, or when the configured registry origin matches the resolved origin.
Expected Behavior
A tarball whose resolved URL is a registry-mediated URL (e.g. https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz) should be exempt from the allow-remote gate, identically to when the configured registry happens to share that origin. Configuring a private proxy/mirror should not turn every committed-lockfile registry dependency into a blocked remote tarball.
Steps To Reproduce
REPRO=/tmp/allowremote-proxy-repro
rm -rf "$REPRO" && mkdir -p "$REPRO" && cd "$REPRO"
cat > package.json << 'EOF'
{ "name": "x", "version": "1.0.0", "dependencies": { "minimatch": "3.1.5" } }
EOF
# 1) Generate the lockfile against the public registry, so resolved -> registry.npmjs.org
npm install --registry=https://registry.npmjs.org/ --allow-remote=none --no-audit --no-fund
# -> added 4 packages (configured origin == resolved origin, so the exemption fires)
# 2) Keep the lockfile, drop only node_modules, reinstall against a different registry origin.
# The committed resolved URLs still point at registry.npmjs.org while the configured
# registry origin is the mirror -> EALLOWREMOTE.
rm -rf node_modules
npm install --registry=https://registry.npmmirror.com/ --allow-remote=none --no-audit --no-fund
# -> npm error code EALLOWREMOTE
# 3) allow-remote=all bypasses it (confirms the cause):
rm -rf node_modules
npm install --registry=https://registry.npmmirror.com/ --allow-remote=all --no-audit --no-fund
# -> added 4 packages
Important: do not delete package-lock.json before step 2 — regenerating it against the mirror rewrites resolved to the mirror origin, which removes the mismatch and hides the bug. The mismatch requires a lockfile whose resolved origin differs from the configured registry origin (the normal committed-lockfile + proxy/mirror case). The mirror does not need to actually serve the tarball — the gate fires at extract time before/at fetch, purely from the origin comparison.
Root Cause
The registry-tarball exemption at reify.js:716 only overrides allowRemote: 'all' when #isRegistryResolvedTarball(node) returns true. That check at reify.js:849 compares the resolved URL origin against the configured registry origin:
#isRegistryResolvedTarball (node) {
if (!node.resolved || !node.isRegistryDependency) {
return false
}
try {
const resolved = new URL(node.resolved)
const registry = new URL(pickRegistry(npa(node.name), this.options))
const registryPath = registry.pathname.replace(/\/?$/, '/')
return resolved.origin === registry.origin &&
(registryPath === '/' || resolved.pathname.startsWith(registryPath))
} catch {
return false
}
}
When a proxy/mirror is configured, pickRegistry() returns the proxy origin while node.resolved is the canonical registry.npmjs.org URL from the lockfile, so resolved.origin === registry.origin is false. The override is skipped, pacote re-parses the name@URL spec as type=remote, and canUse() (pacote/lib/fetcher.js) throws EALLOWREMOTE.
Verified with a temporary probe in #isRegistryResolvedTarball (since reverted): for a hoisted minimatch install with a proxy registry configured, isRegistryDependency=true, resolvedOrigin=https://registry.npmjs.org, registryOrigin=<proxy origin> -> returns false. Switching the configured registry to https://registry.npmjs.org/ flips it to true and the install succeeds.
This is distinct from #9494/#9495 (store nodes lacking isRegistryDependency) and #9509 (genuine remote root tarballs). Here isRegistryDependency is correctly true; the failure is purely the configured-vs-resolved origin comparison.
Environment
- npm: 12.0.0-pre.0 (
latest); also present on 11.x when allow-remote is set to none/root (default all masks it)
- Node.js: v24.15.0
- OS Name: Darwin 25.5.0 (macOS, arm64)
- System Model Name: Mac17,6
- Relevant config:
registry = "https://<private-proxy-host>/<path>/" ; origin differs from lockfile resolved URLs
allow-remote = none ; default on npm 12
Is there an existing issue for this?
This issue exists in the latest npm version
Current Behavior
With
allow-remoteset tononeorroot(thenonedefault on npm 12), anpm installfails withEALLOWREMOTEon ordinary registry dependencies whenever the configured registry origin differs from theresolvedURL origin recorded in the lockfile:This is the common proxy/mirror case: a committed
package-lock.jsonwhoseresolvedURLs point tohttps://registry.npmjs.org/..., while the machine (or CI) is configured to use a private registry proxy/mirror with a different origin. Every registry tarball is then misclassified as a genuineremotetarball and blocked.It affects both the hoisted and the linked install strategy — it is not strategy specific. The same install succeeds when
allow-remote=all, or when the configured registry origin matches theresolvedorigin.Expected Behavior
A tarball whose
resolvedURL is a registry-mediated URL (e.g.https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz) should be exempt from theallow-remotegate, identically to when the configured registry happens to share that origin. Configuring a private proxy/mirror should not turn every committed-lockfile registry dependency into a blockedremotetarball.Steps To Reproduce
Important: do not delete
package-lock.jsonbefore step 2 — regenerating it against the mirror rewritesresolvedto the mirror origin, which removes the mismatch and hides the bug. The mismatch requires a lockfile whoseresolvedorigin differs from the configured registry origin (the normal committed-lockfile + proxy/mirror case). The mirror does not need to actually serve the tarball — the gate fires at extract time before/at fetch, purely from the origin comparison.Root Cause
The registry-tarball exemption at reify.js:716 only overrides
allowRemote: 'all'when#isRegistryResolvedTarball(node)returns true. That check at reify.js:849 compares theresolvedURL origin against the configured registry origin:When a proxy/mirror is configured,
pickRegistry()returns the proxy origin whilenode.resolvedis the canonicalregistry.npmjs.orgURL from the lockfile, soresolved.origin === registry.originis false. The override is skipped, pacote re-parses thename@URLspec astype=remote, andcanUse()(pacote/lib/fetcher.js) throwsEALLOWREMOTE.Verified with a temporary probe in
#isRegistryResolvedTarball(since reverted): for a hoistedminimatchinstall with a proxy registry configured,isRegistryDependency=true,resolvedOrigin=https://registry.npmjs.org,registryOrigin=<proxy origin>-> returnsfalse. Switching the configured registry tohttps://registry.npmjs.org/flips it totrueand the install succeeds.This is distinct from #9494/#9495 (store nodes lacking
isRegistryDependency) and #9509 (genuine remote root tarballs). HereisRegistryDependencyis correctlytrue; the failure is purely the configured-vs-resolved origin comparison.Environment
latest); also present on 11.x whenallow-remoteis set tonone/root(defaultallmasks it)