Fix npm E403 403 Forbidden on install or publish: auth/token issues, registry config, proxy/VPN blocks, or missing publish/2FA permissions. Step-by-step fixes.
The E403 Forbidden error indicates that the npm registry server understood your request but is refusing to fulfill it. Unlike a 401 Unauthorized error (which means you need to authenticate), a 403 means the server can see your request but won't grant access to that resource or action. This error appears in several scenarios: when installing packages through a misconfigured proxy, VPN, or TLS-inspecting security appliance; when publishing a package without proper authentication; when trying to publish a package name that already exists or that you do not have write access to; when your account requires 2FA for publishing; or when corporate security policies block certain packages. The error message typically shows the specific URL and HTTP method that was denied. A `PUT` to `https://registry.npmjs.org/<package>` points to a publish/write problem, while a `GET` during install points to a read/network/registry-config problem. Pay attention to whether the error occurs during `npm install` or `npm publish`, because the fixes differ significantly.
Read the full error. The HTTP method tells you which path to follow:
- GET ... 403 during npm install -> usually registry config, proxy/VPN, or private-registry auth.
- PUT ... 403 during npm publish -> usually authentication, token scope, package-name ownership, or 2FA.
npm install --loglevel=verbose
# or
npm publish --loglevel=verboseNote the exact URL and method shown so you apply the right fix below.
Most publish-time 403s are stale or missing credentials. Check who you are, then log in again:
# Check if you're logged in
npm whoami
# If this errors or shows the wrong user, log in again
npm login
# Verify
npm whoaminpm whoami should print your npm username without errors. If your account has 2FA enabled for publishing, supply a one-time password:
npm publish --otp=123456A PUT ... 403 on publish often means the name is already taken or you don't have write access to it:
npm view <package-name>If the package exists and you are not an owner, you cannot publish to that name. Options:
1. Use a scoped package name (recommended):
{
"name": "@your-username/package-name"
}For a public scoped package, the first publish must allow public access:
npm publish --access public2. Choose a different, unused package name.
If you publish from CI using a token, a read-only token will produce a 403 on publish:
1. Go to https://www.npmjs.com/settings/~/tokens
2. Confirm the token type. For publishing you need an Automation or Publish token, not a read-only token.
3. Create a new token with the correct permission if needed:
npm token create4. Provide it via the NPM_TOKEN environment variable and reference it in .npmrc rather than hardcoding the secret:
//registry.npmjs.org/:_authToken=${NPM_TOKEN}Keep tokens in CI secret storage, never commit them to the repository.
npm requires a verified email address before you can publish packages:
1. Log in to https://www.npmjs.com
2. Open your account settings
3. Confirm your email is verified; if not, click "Resend verification email"
Once verified, try publishing again.
If installs fail only on a corporate network, the registry connection is likely being blocked or intercepted.
First, test by temporarily disconnecting from the VPN and retrying. If that works, configure npm to use your corporate proxy:
npm config set proxy http://your-proxy.company.com:8080
npm config set https-proxy http://your-proxy.company.com:8080If proxy settings were set incorrectly and are themselves the problem, remove them:
npm config delete proxy
npm config delete https-proxyTLS-inspecting tools (ZScaler, Cisco AnyConnect, etc.) re-sign HTTPS traffic and can trigger 403s. The correct fix is to trust your company's CA certificate, not to weaken transport security:
npm config set cafile /path/to/company-ca.pemDo not switch the registry to plain http:// — the public npm registry serves HTTPS only, so an http URL will fail, and disabling TLS validation (strict-ssl=false / NODE_TLS_REJECT_UNAUTHORIZED=0) exposes you to man-in-the-middle attacks. Use the proper CA certificate instead, or ask your network team to allowlist registry.npmjs.org.
Confirm npm is pointed at the right registry:
npm config get registryIt should show https://registry.npmjs.org/. If not, reset it:
npm config set registry https://registry.npmjs.org/A project-level .npmrc can override your user config (for example pointing at a private registry that returns 403). Inspect it:
cat .npmrcFor a private registry (Nexus, Artifactory, Verdaccio, GitHub Packages), make sure the scope is mapped to the right host and that valid credentials exist for it, e.g.:
@myscope:registry=https://npm.mycompany.com/
//npm.mycompany.com/:_authToken=${PRIVATE_NPM_TOKEN}A corrupted cache or a conflicting config file can produce confusing 403s. Clear the cache:
npm cache clean --forceThen review the full, layered configuration to spot stale tokens, wrong registries, or leftover proxy settings:
npm config list -lnpm merges config from command-line flags, the project .npmrc, the user ~/.npmrc, and the global /etc/npmrc, in that precedence order. If you find a bad setting, remove just that key (this is safer than wiping your whole config):
npm config delete proxy
npm config delete https-proxyBefore editing ~/.npmrc by hand, back it up so you can restore your auth tokens if needed:
cp ~/.npmrc ~/.npmrc.backup### GitHub Codespaces and cloud environments
GitHub Codespaces and some hosted dev environments apply network policies that can block certain registry traffic. Prefer asking the platform to allowlist registry.npmjs.org over changing registries. If you must temporarily install from a public mirror, do so over HTTPS and restore the default immediately afterward:
npm config set registry https://registry.yarnpkg.com
npm install <package>
npm config set registry https://registry.npmjs.org/ # restore afterNote that mirrors only help with installs (reads); publishing must go to your real registry.
### Private registry issues (Nexus, Artifactory, Verdaccio, GitHub Packages)
1. Ensure credentials exist for the exact host in .npmrc using a per-registry _authToken line.
2. Map the package scope to the registry: @scope:registry=https://your-registry/.
3. Confirm your account has read (install) or write (publish) permission for that repository — a 403 here is usually a permissions problem on the registry side, not an npm bug.
4. Avoid the legacy _auth (base64) field unless your registry specifically requires it; prefer _authToken.
### Why 403 vs 401 matters
A 401 means "authenticate" — running npm login or supplying a token usually fixes it. A 403 means "authenticated (or anonymous) but not allowed" — the fix is about *permissions*: token scope, package ownership, 2FA, or a network policy. Reading the method and URL in the error is the fastest way to tell which.
### Keep npm current
An outdated npm can mishandle auth headers or 2FA flows. Updating sometimes resolves stubborn publish failures:
npm install -g npm@latestnpm notice access token expired or revoked. Please try logging in again.
Token has expired - npm authentication failure
npm ERR! code EAI_AGAIN
How to fix "EAI_AGAIN" in npm
npm ERR! code EUSAGE npm ERR! Usage error
How to fix "npm ERR! code EUSAGE" in Node.js projects
npm ERR! code E401 npm ERR! 401 Unauthorized
How to fix "E401 Unauthorized" in npm
npm ERR! DEPTH_ZERO_SELF_SIGNED_CERT
How to fix "DEPTH_ZERO_SELF_SIGNED_CERT" in npm