前言

npm install 跑了十分钟还在转圈, 最后报错 ETIMEDOUT 或 ECONNRESET—— 这种事几乎每个国内开发者都遇到过。

网上搜一圈, 会看到各种答案: 换镜像源、清缓存、关SSL、调超时…… 但这些答案大多是碎片化的, 而且有些互相矛盾(比如让你 strict-ssl=false, 这实际上是在关掉安全检查)。

这篇想做的事是: 给一个有序的排查框架, 让你知道每一步在解决什么问题, 而不是照抄命令碰运气。

一、先定位: 你到底遇到的是哪一类问题

不同症状对应不同原因, 乱试只会浪费时间。

症状 大概率原因 该跳到
一直卡住不动, 无报错 源不可达 / DNS 问题 第二节
ETIMEDOUT 网络超时, 源慢或被墙 第三节
ECONNRESET / ECONNREFUSED 连接被重置, 代理冲突 第五节
ERR_PNPM_META_FETCH_FAIL pnpm 取元数据失败 第六节
CERT_HAS_EXPIRED 证书问题 第七节
ENOTFOUND DNS 解析失败 第二节
EACCES / EPERM 权限问题 第八节

先确认你属于哪一类, 再往下看。 这能省掉大半的无效尝试。

三个诊断命令

1
2
3
4
5
6
7
8
# 1. 看当前实际用的源(不要凭记忆, 看实际配置)
npm config get registry

# 2. 测试源是否可达 + 耗时
curl -s -o /dev/null -w "HTTP码: %{http_code} 耗时: %{time_total}s\n" -m 20 https://registry.npmmirror.com/react

# 3. 测试 npm 能不能拿到包元数据
npm view react version

npm view react version 这一条最关键—— 它只取元数据, 不下载 tarball, 所以能最快判断”源本身通不通”。

如果这条命令就卡住或报错, 问题在源和网络, 与项目无关。

二、镜像源: 怎么选, 以及最重要的”失效检测”

常见镜像源现状(2026 年实测)

我在写这篇时实测了各源的状态, 结果如下:

1
2
3
4
5
6
7
8
9
10
11
# 老淘宝源
curl -o /dev/null -w "%{http_code}\n" -m 20 https://registry.npm.taobao.org/react
# 输出: 000 ← 已完全不可用

# npmmirror(淘宝系新域名)
curl -o /dev/null -w "%{http_code}\n" -m 20 https://registry.npmmirror.com/react
# 输出: 200 ← 正常

# 官方源
curl -o /dev/null -w "%{http_code}\n" -m 20 https://registry.npmjs.org/react
# 输出: 200 ← 正常但国内慢

重点: registry.npm.taobao.org 这个老地址已经停止服务。 很多人的 .npmrc 里还留着它(这是几年前的教程留下的习惯), 结果所有 npm 命令都在超时。

如果你现在的 npm config get registry 返回的是 registry.npm.taobao.org, 恭喜你找到了一个大概率就是你现在问题的原因。 直接换成 registry.npmmirror.com 即可。

镜像源对比

源 地址 国内速度 可用性
npmmirror https://registry.npmmirror.com 快 ✅ 推荐
腾讯云 https://mirrors.cloud.tencent.com/npm/ 快 ✅ 备选
华为云 https://repo.huaweicloud.com/repository/npm/ 中 ✅ 备选
官方 https://registry.npmjs.org 慢 ✅ 保底

建议备 2 个,主源挂了自动切备用。

配置方法

用户级(推荐, 全局生效)

1
2
3
npm config set registry https://registry.npmmirror.com/
npm config get registry
# 确认输出正确

项目级(团队项目建议用这个)

在项目根目录建 .npmrc:

1
2
3
4
5
registry=https://registry.npmmirror.com/
fetch-retries=3
fetch-retry-mintimeout=10000
fetch-retry-maxtimeout=60000
fetch-timeout=120000

项目级优先于用户级, 这样新人 clone 项目后不用配就能用。

检测源是否失效(建议每月做一次)

1
2
# 这个命令返回 000 就说明源挂了
curl -s -o /dev/null -w "%{http_code}\n" -m 15 https://registry.npmmirror.com/react

技巧: 很多”莫名其妙开始变慢”的情况, 都是某个镜像源悄悄挂了或限流了。 养成定期检测的习惯, 或者在 CI 里加一个健康检查。

三、超时相关参数: 该调哪个, 调多少

npm 的四个关键参数

1
2
3
4
5
# 拿到这些值的当前设置
npm config get fetch-retries
npm config get fetch-retry-mintimeout
npm config get fetch-retry-maxtimeout
npm config get fetch-timeout

含义和推荐值:

参数 作用 推荐值 说明
fetch-retries 重试次数 3 太少容易失败, 太多浪费时间
fetch-retry-mintimeout 首次重试等待 10000ms 太短会在网络抖动时很快放弃
fetch-retry-maxtimeout 重试等待上限 60000ms
fetch-timeout 单次请求超时 120000ms 默认 5 分钟, 通常够用

一次性设好:

1
2
3
4
npm config set fetch-retries 3
npm config set fetch-retry-mintimeout 10000
npm config set fetch-retry-maxtimeout 60000
npm config set fetch-timeout 120000

一条重要的调参原则

不要一上来就疯狂调大超时。 顺序应该是:

  1. 先换源(成本最低, 效果最明显)
  2. 再调重试参数
  3. 最后才考虑加超时

因为如果是源本身有问题, 把超时加到 10 分钟只会让你等得更久才失败。

什么时候该调大超时

  • 你在跨洲网络(比如国内访问海外 CI)
  • 依赖里有大包(如electron、chromium)
  • 你在低带宽环境(公司内网、虚拟机)

这时候可以专门给某个命令加超时:

1
npm install --fetch-timeout=600000

四、DNS 与网络层排查

如果换源后依然超时, 问题可能在更底层。

判断是不是 DNS 问题

1
2
3
4
5
# 测试域名解析
nslookup registry.npmmirror.com

# 测试连接耗时
curl -w "连接耗时: %{time_connect}s 总耗时: %{time_total}s\n" -o /dev/null https://registry.npmmirror.com/react

time_connect 很大(比如超过 1 秒)说明建连本身慢, 可能需要检查网络路径。

换 DNS

Windows:

1
2
# 查看当前 DNS
nslookup | more

如果解析到的 IP 不合理(比如指向了失效的CDN), 可以改成公共 DNS: 223.5.5.5(阿里)、119.29.29.29(腾讯)。

排查是否走代理

这是最容易漏掉的一项。

1
2
3
4
5
6
7
# 看环境变量里有没有代理
echo $HTTP_PROXY
echo $HTTPS_PROXY

# 看 npm 自己配置的代理
npm config get proxy
npm config get https-proxy

代理与镜像源的冲突是个经典坑:

  • 代理正常工作时, npm 走代理, 不需要镜像源
  • 但如果代理部分失效(比如内网走代理、外网不走), 就会时好时坏
  • 更糟的情况: 配置了镜像源 + 代理, 请求可能走了两次转发, 反而更慢

排查方法:

1
2
# 临时禁用代理再试
npm install --no-proxy

如果禁用后变快, 说明问题在代理配置。

五、pnpm: 报错信息完全不同

pnpm 的错误码和 npm 不一样, 而且 pnpm 默认使用严格模式, 一些 npm 下能容忍的问题在 pnpm 下会直接报错。

pnpm 的常见错误

ERR_PNPM_META_FETCH_FAIL

取包元数据失败, 通常是网络或源配置问题。

排查顺序:

  1. pnpm config get registry 确认源地址
  2. curl 测试该源是否可达
  3. 确认没被代理干扰(pnpm config get proxy)

ERR_PNPM_FETCH_404

包在仓库里不存在。 常见于:

  • 包名拼错
  • 包已unpublish 或改名
  • scoped 包在镜像源上缺失(如@myorg/pkg)

ERR_PNPM_NO_MATCHING_VERSION

指定版本不存在。

1
2
3
4
# 查包的所有版本
pnpm view <包名> versions

# 如果镜像源没有某个版本, 给 scoped 包单独配源

ERR_PNPM_NO_OFFLINE_META

离线模式下缓存里没有元数据。 解决: 先联网装一次, 之后才能离线。

pnpm 配置镜像源

1
2
3
pnpm config set registry https://registry.npmmirror.com
pnpm config set fetch-retries 3
pnpm config set fetch-timeout 120000

项目级写在 package.json 或 .npmrc:

1
2
3
registry=https://registry.npmmirror.com/
fetch-retries=3
fetch-timeout=120000

scoped 包要单独配

这是镜像源最常见的坑: 私有或小众的 scoped 包, 镜像源可能没有同步。

1
2
registry=https://registry.npmmirror.com/
@yourorg:registry=https://registry.npmjs.org/

含义: @yourorg/ 开头的包走官方源, 其他走镜像。

pnpm vs npm 的选择

pnpm 有一个显著优势: 硬链接机制, 相同版本的包在磁盘上只存一份。

实测对比(同一项目): npm 的 node_modules 通常 300-500MB, pnpm 可能只有 100-200MB, 安装速度也更快。

但注意: pnpm 用的是 node_modules/.pnpm/ 结构, 有些工具(尤其是老项目)不兼容, 遇到奇怪的模块找不到问题, 可以先用 npm 验证。

六、Docker 场景的同类问题

Docker 拉镜像慢或失败, 根因和 npm 高度相似。

镜像拉取失败

pull access denied

排查顺序:

1
2
3
4
5
6
7
8
9
10
11
# 1. 确认镜像名和标签存在
docker search <镜像名>
docker manifest inspect <镜像名>:<标签>

# 2. 确认没超出Docker Hub 匿名拉取限额
# 匿名用户: 100 次/6 小时(按 IP 算)
# 登录免费账号: 200 次/6 小时
docker login

# 3. 确认网络能通
docker pull hello-world

配置国内镜像加速器

Docker Desktop: Settings → Docker Engine, 加:

1
2
3
4
5
6
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy.com"
]
}

Linux 上配在 /etc/docker/daemon.json, 然后 systemctl restart docker。

改完一定要重启 Docker Desktop 才生效, 这是最常见的”我配了但没用”。

验证加速器是否生效

1
docker info | grep -A3 "Registry Mirrors"

有输出说明配置生效; 空的话说明没生效。

七、证书相关问题

CERT_HAS_EXPIRED 或 UNABLE_TO_VERIFY_LEAF_SIGNATURE。

正确做法(按优先级)

1. 更新根证书(推荐)

老版本的 Windows 或 Node 可能没有新的根证书。

1
npm install -g nss-updates  # 或在系统层面更新证书

或直接更新 Node.js 到 LTS 版本。

2. 换源

如果是特定镜像的证书问题, 换个源可能就绕过了。

3. 临时禁用 SSL 校验(不推荐, 但有时是唯一办法)

1
npm config set strict-ssl false

但要知道代价:

strict-ssl=false 会让npm 不再验证 HTTPS 证书, 这意味着你无法确认下载的包是否被中间人篡改。 在公共 WiFi、公司网络或有代理的环境下, 这是实打实的供应链安全风险。

我的建议是把它当作最后的手段, 且只在临时使用, 解决后立刻改回true。

npm config set strict-ssl true 改回来。

顺带说一句: 很多老教程会让你直接关SSL, 这在当年网络环境复杂时有其道理, 但现在镜像源的证书普遍是正常的, 关SSL 的弊大于利。

八、权限问题

Linux/macOS 下 EACCES 或 EPERM:

不要用 sudo npm install —— 这会让文件属主变成 root, 后面更麻烦。

正确做法:

1
2
3
4
5
6
# 改 Node 的全局安装目录
sudo mkdir -p /usr/local/lib/node_modules
sudo chown -R $(whoami) /usr/local/lib/node_modules

# 然后正常安装
npm install -g <包名>

或者用 nvm 管理 Node, 从根本上避免权限问题。

九、一个通用的排查流程

把上面的内容浓缩成一张流程图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 第 0 步: 确认症状类型
npm install2>&1 | tee install.log
grep -oE "ETIMEDOUT|ECONNRESET|ENOTFOUND|CERT_HAS_EXPIRED|EACCES|ERR_PNPM_[A-Z_]+" install.log

# 第 1 步: 确认源可达(最常见原因)
npm config get registry
curl -s -o /dev/null -w "%{http_code}\n" -m 15 $(npm config get registry)react

# 第 2 步: 确认无代理干扰
echo $HTTP_PROXY; npm config get proxy

# 第 3 步: 最小化验证
npm view react version

# 第 4 步: 清理缓存重试
npm cache clean --force
npm install

# 第 5 步: 换源重试
npm config set registry https://registry.npmmirror.com/
npm install

按顺序走, 不要跳步。 大多数情况在第 1 或第 5 步就解决了。

十、小结

最核心的三条:

  1. 先诊断再动手。 npm config get registry + npm view react version 这两条命令能定位八成问题。

  2. 换源优先于调参数。 源不可达的时候, 把超时调到 10 分钟只是让你多等。

  3. 别随手关 SSL。 那是用安全换 convenience, 记得到手改回来。

一个长期建议: 在项目里放一个 .npmrc(而不是只在全局配), 这样新人 clone 下来就能用, 也不会因为某台机器配置过期而卡住。

最后一个实用技巧: 遇到 npm 问题时, 先试试 pnpm install。 有时候不是 npm 坏了, 而是 npm 和这个项目的依赖树合不来。


参考: