Windows本地HTTPS证书配置:mkcert与OpenSSL实战指南
2026/9/14 18:53:44 网站建设 项目流程

1. 为什么要在 Windows 上折腾本地 HTTPS 证书

先说说我自己的使用场景。日常开发和调试中,很多功能在http://localhost下跑着没问题,一到联调阶段就出幺蛾子:Service Worker 注册失败、Cookie 带不上 SameSite 属性、浏览器直接拦截摄像头和麦克风权限、第三方登录回调地址要求必须是 HTTPS……这些坑每一个都足够让人怀疑人生。后来我干脆把所有本地开发环境全部切到 HTTPS,提前把线上可能出现的问题在本地暴露出来,省得每次都是部署到测试环境才被发现。

所谓“本地 HTTPS 证书”,说白了就是给localhost127.0.0.1或者局域网内某台机器的 IP 签发一张 SSL/TLS 证书,让本机的 Web 服务(Nginx、Node.js、IIS、Tomcat 等都行)能以 HTTPS 协议对外提供服务。它解决的痛点是:浏览器默认信任哪些证书、默认拦截哪些请求、把哪些请求当作“不安全内容”处理,这些规则是写死在浏览器内核里的。你绕不过去,只能正面应对。

这篇文章主要写给三类人:一是前端开发,需要在本机调试 PWA、支付回调、音视频权限这类强依赖 HTTPS 的场景;二是后端或运维,需要在 Windows 机器上临时搭一个带证书的测试环境;三是刚接触 HTTPS 证书机制、想搞清楚证书信任链到底是怎么回事的同学。我会把两种主流方案——mkcert一键生成和OpenSSL手搓证书——都完整走一遍,中间穿插原理说明和踩坑记录,尽量让你看完就能直接照做。

2. 方案选型:mkcert 还是 OpenSSL

先说结论:如果你只是想让本地开发环境快速跑起来,用mkcert,它就是为了这个场景设计的;如果你想搞清楚证书签发体系、或者需要为局域网多台机器、容器、IoT 设备批量签发证书,用OpenSSL手搓更灵活。

对比维度mkcertOpenSSL 手动签发
上手难度极低,两条命令出证书中等,需要理解 CSR、私钥、扩展字段
证书信任自动安装本地 CA 到系统信任库需要手动导入 CA 证书
自定义能力只能按域名/IP 签发可自定义有效期、扩展项、签发策略
适用场景个人电脑本地开发调试局域网多设备、容器、学习 PKI 原理
跨平台Windows/macOS/Linux 都能用全平台,只要有 OpenSSL

为什么我不建议直接用openssl一条命令生成自签名证书然后硬塞给浏览器?因为在 2020 年之后的 Chrome、Edge、Firefox 版本里,自签名证书基本都被视为“无效证书”,浏览器会直接整页拦截,而且不再允许用户点击“继续访问”绕过。这是浏览器厂商对安全的硬性要求,不是配置能改回来的。真正能被本地信任的证书,必须由一台“被本机信任的 CA(证书颁发机构)”来签发。mkcert的精髓就是:它在本地创建了一个私有 CA,并把这个 CA 自动装进系统的“受信任的根证书颁发机构”列表,然后用这个 CA 给localhost等地址签发证书。对系统来说,这套信任链是完整闭合的,浏览器自然就认了。

OpenSSL 方案则适合想搞明白“CA 是什么、证书是怎么签出来的”的人。你自己扮演 CA,自己签发服务器证书,然后把你的 CA 公钥导入系统信任库。整个过程和真实世界的Let's Encrypt签发流程逻辑一致,只是不用花钱、不用验证域名。理解了这套流程,后续再看服务端证书配置、双向 TLS、证书续期这些概念都会轻松很多。

3. 五分钟跑通 mkcert:本地 HTTPS 的最快路径

3.1 安装 mkcert

Windows 上有三种常见安装方式,任选其一:

# 方式一:用 winget(Windows 10 1709+ 自带) winget install FiloSottile.mkcert # 方式二:用 Chocolatey choco install mkcert # 方式三:用 Scoop scoop bucket add extras scoop install mkcert

如果你机器上没装包管理器,也可以直接去 GitHub 的FiloSottile/mkcert仓库下载编译好的可执行文件,放到一个加入了 PATH 的目录里,比如C:\Windows\System32或者你自己建的C:\tools并配置环境变量。我个人的建议是优先用 winget,它帮你处理了下载、解压、配置 PATH 这些脏活,换机器重装时也方便。装完在 PowerShell 里输入mkcert -version能输出版本号就算成功了。

3.2 安装本地 CA 并签发证书

安装好之后,先初始化本地 CA:

mkcert -install

这一步会在本地生成一个私有的根证书,并把它写入 Windows 的“当前用户 - 受信任的根证书颁发机构”存储区。命令执行成功后,PowerShell 里会提示The local CA is now installed in the system trust store!

然后签发证书。假设你要给localhost127.0.0.1,以及局域网地址192.168.1.100都签一张证书:

mkcert localhost 127.0.0.1 192.168.1.100

执行完成后,当前目录会多出两个文件:localhost+2.pemlocalhost+2-key.pem。前者是证书公钥,后者是私钥。你可以按需把它改名成更直观的名字,比如dev-cert.pemdev-key.pem。mkcert 默认把证书有效期设为两年多(825 天),到期前记得重签。

3.3 让你的服务用上证书

证书文件拿到手,接下来就是让 Web 服务加载它们。以 Node.js 的https模块为例:

const https = require('https'); const fs = require('fs'); const options = { key: fs.readFileSync('dev-key.pem'), cert: fs.readFileSync('dev-cert.pem') }; https.createServer(options, (req, res) => { res.writeHead(200); res.end('hello https'); }).listen(8443, () => { console.log('https server running at https://localhost:8443'); });

Nginx 的配置则是把证书路径填进server块:

server { listen 443 ssl; server_name localhost; ssl_certificate C:/certs/dev-cert.pem; ssl_certificate_key C:/certs/dev-key.pem; location / { proxy_pass http://127.0.0.1:8080; } }

注意:Nginx 在 Windows 上加载证书时,路径里的反斜杠可能会引起解析问题,建议统一使用正斜杠/或者转义后的C:\\certs\\dev-cert.pem。这是我在实际配置中踩过的一个坑,文件明明存在,Nginx 却一直报cannot load certificate

3.4 mkcert 的常用维护命令

命令作用
mkcert -install安装本地 CA 到系统信任库
mkcert -uninstall从系统信任库移除本地 CA
mkcert -CAROOT显示 CA 根证书和私钥的存放目录
mkcert localhost 127.0.0.1签发多个域名/IP 的证书
mkcert -pkcs12导出 PKCS#12 格式证书(常用于 IIS)

如果你或团队里的其他人经常需要在多台机器上统一信任同一个 CA,可以把mkcert -CAROOT指向的rootCA.pem文件分发给同事,让他们双击导入系统信任库,这样大家机器上信任的 CA 就是同一个,证书换机器也不用重新签发。这个技巧在团队开发时特别实用,省得每个人都各自生成一套 CA,导致不同机器之间互相不认。

4. OpenSSL 手搓证书:自己当一次 CA

如果你用的是不太方便装第三方工具的离线环境,或者想彻底理解证书体系,OpenSSL方案值得完整走一遍。Git for Windows 自带 OpenSSL,你也可以单独安装Win64 OpenSSL发行版。

4.1 生成 CA 根证书

CA 的角色就像签证中心:它声明“我证明这个人的身份是可信的”。我们得先造一个自己的签证中心出来。

# 生成 CA 私钥,aes256 加密,2048 位 openssl genrsa -aes256 -out ca-key.pem 2048 # 根据私钥生成 CA 自签名证书 openssl req -x509 -new -nodes -key ca-key.pem -sha256 -days 3650 -out ca-cert.pem

执行第二条命令时,会交互式询问国家、省份、组织名称等信息。这里的Common Name建议填一个有意义的名字,比如My Local Dev CA,后续你在证书管理器里查找它时靠这个字段识别。-days 3650表示根证书有效期 10 年,作为根 CA 有效期长一点没有关系,因为真正对外使用的是下一层签出来的服务器证书。

4.2 生成服务器证书并签名

服务器证书必须由 CA 来签,不能自己签完就算数。这里面有一个关键概念叫SAN(Subject Alternative Name,主题备用名称)。从 Chrome 58 开始,浏览器就不再检查证书里的Common Name了,只认 SAN 字段里的域名和 IP。也就是说,哪怕你证书的 CN 配对了,只要 SAN 里没有对应域名,浏览器照样报错。这也是很多新手自签证书失败的最常见原因。

先准备一个配置文件server.conf,把要签发的域名和 IP 写进去:

[req] distinguished_name = req_distinguished_name req_extensions = v3_req prompt = no [req_distinguished_name] countryName = CN stateOrProvinceName = Beijing localityName = Beijing organizationName = Dev Team commonName = localhost [v3_req] keyUsage = keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = localhost DNS.2 = *.localhost IP.1 = 127.0.0.1 IP.2 = 192.168.1.100

然后按顺序执行:

# 1. 生成服务器私钥 openssl genrsa -out server-key.pem 2048 # 2. 生成证书签名请求(CSR) openssl req -new -key server-key.pem -out server.csr -config server.conf # 3. 用 CA 给 CSR 签名,生成正式证书 openssl x509 -req -in server.csr -CA ca-cert.pem -CAkey ca-key.pem \ -CAcreateserial -out server-cert.pem -days 825 \ -sha256 -extensions v3_req -extfile server.conf

第三步执行完,你会得到server-cert.pem。同时目录里会出现一个ca-cert.srl文件,这是 CA 的序列号记录文件,每次签发证书序列号都会递增,不要删除它。

验证一下证书是否签对了:

openssl x509 -in server-cert.pem -text -noout

重点看两个地方:一是Issuer字段是否等于前面 CA 证书的Subject,二是X509v3 Subject Alternative Name是否包含你配置的 localhost 和 IP。只要这两项正确,这张证书就算签成功了。然后把ca-cert.pem导入系统“受信任的根证书颁发机构”,把server-cert.pem+server-key.pem配给 Nginx、Node.js 使用即可。

4.3 为什么推荐用配置文件而不是交互式输入

很多人习惯用一行openssl req -x509 ...直接回车交互输入,但这在生成带 SAN 的证书时行不通,因为你没法在交互模式里填 SAN 扩展字段。我的做法是始终维护一份server.conf放在项目里,需要给新域名签发时改一下alt_names就行,可重复、可审计。团队协作时别人拿到这份配置也知道证书是怎么签出来的,不会出现“证书是哪来的、参数是什么”这种说不清道不明的情况。

5. Windows 证书信任机制与导入姿势

5.1 证书存储区:CurrentUser 与 LocalMachine

Windows 的证书存储分为两部分:当前用户存储区本地计算机存储区。用mkcert -install时,默认写入的是当前用户存储区,只对当前登录用户生效。如果你想让这台机器上的所有用户、或者 Windows 服务(比如 IIS 应用程序池)都能信任这个 CA,就必须把根证书导入到“本地计算机”的对应存储区。

如何查看和管理?按Win + R输入certmgr.msc打开的是当前用户证书管理器;输入certlm.msc打开的是本地计算机证书管理器。注意:certlm.msc需要管理员权限,弹 UAC 确认后才能真正查看。很多人在certmgr.msc里看不到 IIS 跑着的证书,就是因为 IIS 用的是本地计算机存储区。

  • 当前用户:证书 - 当前用户 - 受信任的根证书颁发机构 - 证书
  • 本地计算机:证书(本地计算机)- 受信任的根证书颁发机构 - 证书

5.2 手动导入 CA 的两种方式

方式一:直接双击ca-cert.pem文件,在弹出的证书窗口里点击“安装证书”,存储位置选“本地计算机”,然后选择“将所有证书都放入下列存储”,浏览选“受信任的根证书颁发机构”,一路下一步。

方式二:用certutil命令行导入,适合批量操作或写脚本:

:: 导入到当前用户 certutil -user -addstore Root ca-cert.pem :: 导入到本地计算机(需要管理员权限) certutil -addstore Root ca-cert.pem

导入之后建议把浏览器完全关闭再重新打开,因为浏览器会缓存证书信任列表。我还遇到过一种情况:Chrome 开着的时候导入了根证书,但新开的标签页还是提示NET::ERR_CERT_AUTHORITY_INVALID,只有退出 Chrome 进程再启动才恢复。这个“关干净浏览器”的操作,属于很多人容易忽略的细节。

5.3 删除或替换 CA 的注意事项

开发环境里 CA 过期、泄露或者想换一套时,需要先删除旧 CA 再导入新的。步骤是:打开certmgr.msc,进入“受信任的根证书颁发机构 - 证书”,按颁发者名称找到你的本地 CA,右键删除。删除之后,之前签发的所有证书都会立刻失效,浏览器会重新报错。这一点要特别小心,不要在还有其他服务依赖旧证书的时候贸然删 CA。

另外,导入的根证书默认是“颁发者受信任”的完整信任级别。如果只是做开发调试,建议保持默认;给生产环境或给其他同事分发时,不要随意把私有 CA 装进生产机器的根信任库,一旦这个 CA 的私钥泄露,攻击者就能用它签发任意域名的证书,你的所有 HTTPS 流量都可能被中间人解密,这比证书过期严重得多。

6. 常见问题与排查技巧实录

6.1 浏览器报 NET::ERR_CERT_AUTHORITY_INVALID

这个错误的意思是“证书的颁发者不可信”。排查顺序建议从前往后:

  1. 确认签发证书的 CA 根证书已经被导入到当前浏览器使用的证书库里。Chrome 和 Edge 在 Windows 上使用系统证书库,Firefox 自己有独立的证书库(mkcert会自动装到 Firefox 的证书库,但 OpenSSL 手搓的 CA 需要手动在 Firefox 设置里导入)。
  2. 确认导入的位置正确。很多人在当前用户存储区导入了 CA,但服务是以管理员权限跑在系统账户下的,浏览器也可能以不同的上下文运行,导致信任链断裂。
  3. 检查证书链。双击server-cert.pem,查看“证书路径”选项卡,确认根证书节点没有黄色感叹号。

这个错误还有一个容易被忽略的原因:系统里有多个同名的根证书,新导入的 CA 和旧 CA 重名,Windows 在验证时可能选中了旧的、不符合要求的那个。解决办法是先把旧证书删干净再导入。

6.2 浏览器报 NET::ERR_CERT_COMMON_NAME_INVALID

这个错误在 2017 年之前基本是“域名填错了”,但在现代浏览器里,它绝大多数情况是因为证书缺少 SAN 扩展。检查方法前面已经说过:openssl x509 -in server-cert.pem -text -noout,看X509v3 Subject Alternative Name里有没有你访问的域名或 IP。没有就重新生成带着alt_names的证书。

6.3 IIS 绑定证书时找不到证书

IIS 管理器里给站点绑定 HTTPS 时,证书下拉列表只显示本地计算机的“个人”存储区里的证书。从 mkcert 生成的.pem文件没法直接被 IIS 识别,需要先转成 PKCS#12 格式(.pfx):

mkcert -pkcs12 localhost 127.0.0.1

生成的localhost+2.p12文件,双击导入到“本地计算机 - 个人 - 证书”,然后再去 IIS 里绑定就能看到了。如果用 OpenSSL 手搓的方案,转换命令是:

openssl pkcs12 -export -out server.pfx -inkey server-key.pem -in server-cert.pem -certfile ca-cert.pem

导入时会让你设置一个导出密码,这个密码可以留空,但 IIS 之后配置时可能要求填写,建议设一个自己记得住的。

6.4 证书过期或域名变更

证书不是永久的,mkcert 默认签 825 天,OpenSSL 手搓时-days参数决定时长。过期前的表现通常是浏览器报NET::ERR_CERT_DATE_INVALID。解决办法很简单:重新执行签发命令,覆盖原来的.pem文件,然后重启加载证书的服务。注意 mkcert 重签时如果域名列表没变,它不会生成新的文件名,而是直接覆盖;如果你把证书复制到了项目目录,记得同步覆盖项目里的副本。

域名变了也是同理,重新指定新的域名列表签名即可。有一个小技巧:每次重签证书时建议把命令记到一个文本文件里,比如renew.bat,到期时双击运行就完事,不用每次回忆域名清单。

6.5 混合内容:页面是 HTTPS,接口是 HTTP

证书本身没问题,但页面打开后控制台报Mixed Content: The page at 'https://...' was loaded over HTTPS, but requested an insecure resource 'http://...'。这是浏览器拦截了 HTTPS 页面里的 HTTP 资源请求,不管是脚本、图片还是接口调用,都会拦截。解决办法是让所有后端接口也走 HTTPS,或者在开发阶段使用支持 HTTPS 的代理服务。不少人在这一步才发现自己根本没法用 “只给前端上 HTTPS” 这种方式来偷懒,倒逼着把整个本地开发链路全部切换成 HTTPS——这其实也是本地生成 HTTPS 证书最有价值的地方:提前暴露混合内容问题。

6.6 常见问题速查表

报错信息含义处理方式
NET::ERR_CERT_AUTHORITY_INVALID证书颁发者不受信任导入 CA 根证书到受信任的根证书颁发机构
NET::ERR_CERT_COMMON_NAME_INVALID域名与证书不匹配检查 SAN,重新签发
NET::ERR_CERT_DATE_INVALID证书已过期重新签发证书
NET::ERR_CERT_WEAK_SIGNATURE_ALGORITHM签名算法太弱使用 SHA-256 及以上算法签名
ERR_SSL_PROTOCOL_ERROR服务端没正确配置证书检查服务日志、证书路径和私钥是否匹配
Mixed Content页面存在 HTTP 资源将子资源统一切换到 HTTPS

7. 一些实操心得

本地 HTTPS 证书这件事,纯粹是“知易行难”的典型:原理说起来就是 CA、证书、SAN 三个词,但真正落地时会碰到稀奇古怪的环境问题。我自己在 Windows 上折腾这个主题的时间跨度很长,从最早在 Chrome 上点“高级 - 继续前往”强行绕过,到后来用 OpenSSL 手动签发自签名证书,再到现在固定使用 mkcert 配合统一 CA 分发给同事,算是把各种弯路都走了一遍。最大的体会是:工具可以省力,但原理不能不懂。mkcert 让你一条命令拿到可用证书,但如果你不知道 SAN 是什么,不知道证书存储区在哪,遇到报错还是两眼一抹黑。

最后再分享一个小技巧:在 Windows 上调试时,不要只在localhost上测试证书。把局域网 IP 也加进 SAN 里,用手机或其他设备通过https://192.168.x.x访问一下你的服务,很多证书问题在移动端浏览器上的表现和桌面端完全不同。提前把 IP 加进去,省得真到演示那天才发现手机打不开。这个习惯救过我很多次,也希望对你有所帮助。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询