Node.js搭建全域名HTTP/HTTPS代理 如何解决SSL证书安全警告问题
实现方案
核心思路
利用你已经导入浏览器信任的自签根CA,收到HTTPS请求时动态为当前访问域名签发对应证书,无需提前配置所有域名,也不用重启代理服务。
具体实现步骤
- 第一步:安装依赖
执行npm install node-forge安装纯JS实现的SSL证书处理库,无需依赖系统OpenSSL。 - 第二步:复用你已经生成并导入浏览器信任的根CA证书和私钥,将其PEM格式内容保存在代理服务的配置文件中。
- 第三步:实现动态证书生成逻辑,增加缓存机制避免重复生成,核心代码示例:
const forge = require('node-forge'); const certCache = new Map(); // 域名证书缓存,可持久化到本地磁盘 // 替换为你自己生成的根CA证书、私钥的PEM格式内容 const rootCaCert = forge.pki.certificateFromPem('ROOT_CA_CERT_PEM_CONTENT'); const rootCaKey = forge.pki.privateKeyFromPem('ROOT_CA_KEY_PEM_CONTENT'); function generateCertForDomain(domain) { if (certCache.has(domain)) return certCache.get(domain); // 生成服务端密钥对 const keys = forge.pki.rsa.generateKeyPair(2048); const cert = forge.pki.createCertificate(); cert.publicKey = keys.publicKey; cert.serialNumber = Date.now().toString(); cert.validity.notBefore = new Date(); cert.validity.notAfter = new Date(); // 证书有效期设置为1年,可按需调整 cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1); // 配置证书主体信息 const attrs = [ { name: 'commonName', value: domain }, { name: 'organizationName', value: 'Local Proxy' } ]; cert.setSubject(attrs); cert.setIssuer(rootCaCert.subject.attributes); // 用根CA信息作为颁发者 // 核心配置:添加当前域名到subjectAltName字段 cert.setExtensions([ { name: 'basicConstraints', cA: false }, { name: 'keyUsage', digitalSignature: true, keyEncipherment: true, dataEncipherment: true }, { name: 'extKeyUsage', serverAuth: true }, { name: 'subjectAltName', altNames: [ { type: 2, value: domain } // type=2代表DNS类型记录 ] } ]); // 用根CA私钥签名证书 cert.sign(rootCaKey, forge.md.sha256.create()); const result = { key: forge.pki.privateKeyToPem(keys.privateKey), cert: forge.pki.certificateToPem(cert) }; certCache.set(domain, result); return result; }
- 第四步:改造HTTPS服务逻辑,通过SNI回调动态返回对应域名的证书,直接复用你原有处理HTTP请求的Express逻辑即可:
const https = require('https'); const tls = require('tls'); // yourExpressApp替换为你原来处理HTTP请求的Express实例 const httpsServer = https.createServer({ SNICallback: (domain, cb) => { const certInfo = generateCertForDomain(domain); cb(null, tls.createSecureContext({ key: certInfo.key, cert: certInfo.cert })); } }, yourExpressApp);
你原有CONNECT事件的处理逻辑不需要做太大调整,只要把请求转发到上面的HTTPS服务即可。
注意事项
- 单域名证书生成耗时仅几毫秒,完全不会导致服务 downtime
- 如果需要支持泛域名,可以在生成证书时给alt_names额外添加
*.${domain}的DNS记录,一次生成即可支持该域名的所有子域名 - 缓存可以持久化到本地磁盘,代理服务重启后不需要重新生成之前访问过的域名证书
内容的提问来源于stack exchange,提问作者3dprogramin
相关产品推荐
相关产品推荐

