NPM代理搭建指南
2024年11月16日
最近在家里开发项目的时候,时常碰到npm安装超时。想了一下手上有好多服务器,应该能解决这个问题,于是我搭建了一个代理服务器。
技术方案概述
npm registry有两个主要地址:
需要让npm客户端访问这两个域名的时候走自己的服务器,并突破封锁。
本方案概要:
- 使用Nginx搭建反向代理服务器
- 自签名SSL证书并设置信任
- 使用TailScale VPN实现网络互通
- hosts文件配置域名解析
其中TailScale是一个基于WireGuard的现代VPN解决方案,它是本方案中不可或缺的组件,因为它能够帮助我们:
- 通过零配置组网快速建立安全通道
- 利用 WireGuard 的高性能加密通信确保数据安全
- 实现 NAT 穿透,突破网络封锁
- 支持多平台部署
为什么不直接使用梯子:一方面是因为梯子流量有限,安装npm包会消耗大量流量;另一方面是我主要使用浏览器插件来分流,因此梯子没有配置按域名分流,无法针对npm的请求进行分流。
为什么不使用npm镜像:因为通过npm镜像安装的话,lock文件也会使用镜像地址,这样在CI/CD环境中可能会出现新的问题。
SSL证书配置与信任
为了确保npm客户端信任我们自己搭的反向代理,我们需要配置并信任自签名证书。整个过程分为三步:生成根证书(CA)、生成服务器证书、添加证书信任。
生成根证书(CA)
这个脚本完成以下工作:
- 生成 2048 位的 CA 私钥
- 创建证书签名请求(CSR)
- 设置基本约束和密钥用途
- 生成有效期为10年的CA证书
#!/bin/bash
set -o errexit
# Generate CA private key
openssl genrsa -out ca.key 2048
# Generate CA certificate signing request
openssl req -new -key ca.key -out ca.csr -sha256 \
-subj "/C=CN/ST=GuangDong/L=Shenzhen/O=Tinkink/OU=Tinkink/CN=tinkink.net"
# Create ca.ext file
echo "basicConstraints=CA:TRUE
keyUsage=keyCertSign,cRLSign" > ca.ext
# Generate CA certificate
openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt \
-extfile ca.ext -sha256 -days 3650
#!/bin/bash
set -o errexit
# Generate CA private key
openssl genrsa -out ca.key 2048
# Generate CA certificate signing request
openssl req -new -key ca.key -out ca.csr -sha256 \
-subj "/C=CN/ST=GuangDong/L=Shenzhen/O=Tinkink/OU=Tinkink/CN=tinkink.net"
# Create ca.ext file
echo "basicConstraints=CA:TRUE
keyUsage=keyCertSign,cRLSign" > ca.ext
# Generate CA certificate
openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt \
-extfile ca.ext -sha256 -days 3650
生成服务器证书
这个脚本完成以下工作:
- 生成服务器私钥
- 创建服务器证书签名请求
- 配置证书扩展信息,包括多域名支持
- 使用之前创建的 CA 证书签发服务器证书
#!/bin/bash
# Generate private key for npm
openssl genrsa -out npm.key 2048
# Generate certificate signing request
openssl req -new -key npm.key -out npm.csr -sha256 \
-subj "/C=CN/ST=GuangDong/L=Shenzhen/O=Tinkink/OU=Tinkink/CN=registry.npmjs.org"
# Create npm.ext file
echo "authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage=digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName=@alt_names
[alt_names]
DNS.1=registry.npmjs.org
DNS.2=registry.npmjs.com" > npm.ext
# Generate certificate
openssl x509 -req -in npm.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out npm.crt -extfile npm.ext -sha256 -days 720
#!/bin/bash
# Generate private key for npm
openssl genrsa -out npm.key 2048
# Generate certificate signing request
openssl req -new -key npm.key -out npm.csr -sha256 \
-subj "/C=CN/ST=GuangDong/L=Shenzhen/O=Tinkink/OU=Tinkink/CN=registry.npmjs.org"
# Create npm.ext file
echo "authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage=digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName=@alt_names
[alt_names]
DNS.1=registry.npmjs.org
DNS.2=registry.npmjs.com" > npm.ext
# Generate certificate
openssl x509 -req -in npm.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out npm.crt -extfile npm.ext -sha256 -days 720
证书信任配置
生成证书后,需要将 CA 证书(ca.crt)添加到系统的信任存储中:
Windows:
- 双击证书文件
- 选择"安装证书" -> "本地计算机"(需要管理员权限)
- 选择"受信任的根证书颁发机构"
- 也可以使用管理员权限运行 PowerShell 命令:
Import-Certificate -FilePath "ca.crt" -CertStoreLocation Cert:\LocalMachine\Root
Import-Certificate -FilePath "ca.crt" -CertStoreLocation Cert:\LocalMachine\Root
Mac:
- 双击证书文件,添加到钥匙串访问
- 在钥匙串访问中找到证书,双击展开
- 展开"信任"选项,将"使用此证书时"设置为"始终信任"
Linux:
sudo cp ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
sudo cp ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
Nginx 反向代理配置
在服务器上先配置好TailScale VPN,确保本地和服务器在同一个网络中。然后使用以下 Nginx 配置文件:
server {
listen 80;
server_name registry.npmjs.com registry.npmjs.org;
# Redirect all HTTP requests to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name registry.npmjs.com registry.npmjs.org;
ssl_certificate /etc/nginx/ssl/npm.crt;
ssl_certificate_key /etc/nginx/ssl/npm.key;
ssl_protocols TLSv1.2 TLSv1.3;
# Define the DNS resolver
resolver 8.8.8.8 8.8.4.4 valid=30s; # You can use Google's public DNS or any resolver you prefer
resolver_timeout 5s;
# Proxy requests to npm registry
location / {
proxy_pass https://$host;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Proxy settings to handle large requests and timeouts
proxy_read_timeout 90;
proxy_connect_timeout 90;
proxy_redirect off;
# Optionally, if you need to handle large response or request sizes
client_max_body_size 50M;
}
}
server {
listen 80;
server_name registry.npmjs.com registry.npmjs.org;
# Redirect all HTTP requests to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name registry.npmjs.com registry.npmjs.org;
ssl_certificate /etc/nginx/ssl/npm.crt;
ssl_certificate_key /etc/nginx/ssl/npm.key;
ssl_protocols TLSv1.2 TLSv1.3;
# Define the DNS resolver
resolver 8.8.8.8 8.8.4.4 valid=30s; # You can use Google's public DNS or any resolver you prefer
resolver_timeout 5s;
# Proxy requests to npm registry
location / {
proxy_pass https://$host;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Proxy settings to handle large requests and timeouts
proxy_read_timeout 90;
proxy_connect_timeout 90;
proxy_redirect off;
# Optionally, if you need to handle large response or request sizes
client_max_body_size 50M;
}
}
这里重点说明两个关键配置:
resolver
配置用来指定当nginx访问npm registry时的DNS解析服务器,这里使用了Google的公共DNS服务器,如果不设置的话会报错proxy_pass https://$host
:使用$host
变量而不是硬编码域名,这样可以支持多个npm registry域名
hosts 文件配置
要让本地请求指向代理服务器,需要修改 hosts 文件。推荐使用SwitchHosts。但需要注意,大部分系统下,修改Hosts都需要管理员权限,有些系统还需要专门添加可写权限才能修改成功。
# NPM registry proxy
10.x.x.x registry.npmjs.com registry.npmjs.org
# NPM registry proxy
10.x.x.x registry.npmjs.com registry.npmjs.org
结语
通过以上配置,我们就可以使用自己的服务器来代理npm包的下载,目前我已经使用了一段时间,下载速度明显提升,推荐有这个问题且不方便用梯子的也尝试一下。
本文部分内容由Cursor(AI驱动的代码编辑器)协助编写和润色。