
最近重新装了一次开发机把 Windows 下的本地 HTTPS 环境又完整搭了一遍。起因是一个项目里要调试摄像头和麦克风权限还有 Service Worker 离线缓存这些功能在 Chrome 里全都要求 secure context也就是必须走 HTTPS 或者 localhost 才能用。原本想着本地开发随便 HTTP 凑合一下就行结果被浏览器按在地上摩擦之后还是老老实实在 Windows 上生成本地 SSL 证书再用 HTTPS 访问本地 Nginx 服务器。这篇内容就是把我踩过的坑和验证过的方案整理出来给同样被这个问题卡住的同学参考。1. 为什么本地开发也要 HTTPS整体方案怎么选1.1 没有 HTTPS现代浏览器会挡你多少事很多人在本地开发时一直用 http://localhost 或者 http://局域网IP接口调试、页面预览都没问题觉得没必要上 HTTPS。但这个没必要的前提是你只用 Vue/React 这种普通脚手架页面里没有调用浏览器的高级 API。一旦涉及以下几个场景HTTP 就会立刻变成拦路虎摄像头、麦克风、地理位置权限Chrome 和 Edge 默认只在 HTTPS 或 localhost 下开放这些接口HTTP 下直接拿不到 media stream。Service Worker 和 PWA注册 Service Worker 必须要在 secure context 下否则报错 “Service Worker is disabled on insecure origins”。Web Crypto 里的部分方法、Clipboard API、Notifications API同样被安全策略限制。本地联调和 mock 拦截有些代理工具只对 HTTPS 流量做内容注入HTTP 流量会被浏览器策略挡得死死的。localhost 是例外Chrome 对 localhost 默认放行但问题是你平时大概率不只访问 localhost还会用局域网 IP 在手机上联调、用自定义域名测试多站点场景。一旦换成 IP 或域名HTTP 环境就彻底不灵了。就算只在 localhost 上调试也保不齐哪天产品让你在本地测一下 PWA直接在 HTTP 下折腾半天找不到原因最后发现是协议的问题。1.2 市面上几种本地 HTTPS 方案对比解决本地 HTTPS 的思路其实就一个让浏览器信任你签发的本地证书。只不过不同方案的信任成本不一样。我先后试过三种办法体验差距很大方案操作复杂度浏览器提示适用场景Nginx 自己前端做个自签名证书直接加载最低每次访问有警告证书不受信任临时凑合用生成根证书并安装到 Windows 信任库再签发站点证书中等完全无警告体验等同正规证书推荐日常开发用 mkcert 一键生成并自动安装低完全无警告快速入门、不想折腾用内网 DNS 配合某网关统一管理证书高完全无警告团队多人共享环境第一次接触这个的人最容易犯一个错直接拿一条命令生成一个自签名证书然后扔给 Nginx 用觉得自己已经搞定了。结果 Chrome 打开一看还是大红叉提示 NET::ERR_CERT_AUTHORITY_INVALID也就是 这个证书不是我信任的机构签发的。因为自签名证书本身就是它自己的根证书浏览器并不认识它。所以正确的姿势应该是模拟真实的证书颁发体系先自己扮演一次 CA证书颁发机构生成一个根证书。再用这个根证书签发一个站点证书SAN 里写上 localhost、127.0.0.1、局域网 IP、自定义域名。把根证书安装到 Windows 的“受信任的根证书颁发机构”列表。Nginx 加载站点证书浏览器访问时自动完成信任链校验。这套思路和真实 CA 的工作方式一模一样理解之后再看 mkcert 之类的工具本质也没变只是把步骤自动化了。2. 用 OpenSSL 在 Windows 上生成自签名证书2.1 Windows 下装 OpenSSL 的两种姿势生成证书最常用的工具就是 OpenSSL。Linux 上一般自带Windows 上默认没有得自己装。我用的第一种方式是直接装编译好的发行版官网入口有点难找而且文件体积大装完还总喜欢往 PATH 里塞一堆东西。如果你不想折腾用包管理器是最省事的。Windows 10 和 Windows 11 自带的 winget 可以直接装winget install OpenSSL.OpenSSL装完之后命令行里敲openssl version如果能输出类似OpenSSL 3.x.x的信息就说明环境变量已经配好了。如果提示找不到命令大概率是没刷新终端重开一个新的 CMD 或 PowerShell 再试。另一条路子是直接用 Git 自带的 OpenSSL。装了 Git for Windows 之后在C:\Program Files\Git\usr\bin\openssl.exe里就有 OpenSSL不用额外安装。这种方式胜在干净但命令路径太长我自己用还是喜欢把版本装进系统 PATH。2.2 生成根证书自己当一次 CA选定工作目录我一般会建一个单独的文件夹比如 D:\certs,然后所有证书文件都放这里别随手扔在桌面。证书的私钥属于敏感文件虽然只是本地开发级别但养成归档的习惯总没坏处。先生成根证书私钥openssl genrsa -out ca.key 2048如果没有特别严格的合规要求2048 位 RSA 就足够本地开发用了。4096 位加密强度更高但每次握手握手时要多做不少计算本地调试完全没必要。接着签发根证书openssl req -x509 -new -key ca.key -days 3650 -out ca.crt -subj /CCN/STBeijing/LBeijing/OLocalDev/CNLocal Dev Root CA这里-days 3650表示根证书有效期十年。CNLocal Dev Root CA是根证书自己的名称这个名称在之后安装证书时会显示在 Windows 的证书管理器里建议起一个一眼能认出来的名字。有一个细节值得注意根证书的有效期不能设太短因为之后用它签发的站点证书都会受根证书有效期限制。如果你根证书只签了一年那站点证书最长也就一年到时候全要重新签发麻烦得很。2.3 生成站点证书SAN 字段别偷懒生成完根证书就该签发站点证书了。这一步最容易出问题的地方就是 SAN 字段。很多老教程的方法是直接生成一个带 CN 的证书比如CNlocalhost然后往 Nginx 里一配。结果 Chrome 会报NET::ERR_CERT_COMMON_NAME_INVALID因为 Chrome 从 58 版本开始就完全忽略 CN 字段只看 SANSubject Alternative Name主题备用名称。也就是说你必须在证书里显式声明 这个证书可以用于哪些域名/IP浏览器才认账。先创建站点证书的私钥openssl genrsa -out localhost.key 2048然后需要一个配置文件把所有 SAN 都写进去。我习惯直接在当前目录建一个localhost.cnf[req] default_bits 2048 prompt no default_md sha256 distinguished_name dn [dn] C CN ST Beijing L Beijing O LocalDev CN localhost [req_ext] subjectAltName alt_names [alt_names] DNS.1 localhost DNS.2 dev.local IP.1 127.0.0.1 IP.2 192.168.1.100这里面的DNS.1、DNS.2是域名IP.1、IP.2是 IP。我强烈建议大家把以后可能会用到的域名和 IP 一次性全加进去。因为 SAN 字段在后面没法用命令行直接修改只能重新签发证书。我用dev.local举例是因为本地测试时经常会想模拟一个真实域名的环境比如 cookie 的 domain 属性、跨域配置等用 localhost 测不出来。提前在 SAN 里预留一个自定义域名后面想用直接用。接着生成证书签名请求并执行签发openssl req -new -key localhost.key -out localhost.csr -config localhost.cnf openssl x509 -req -in localhost.csr -CA ca.crt -CAkey ca.key -CAcreateserial \ -out localhost.crt -days 825 -sha256 -extensions req_ext -extfile localhost.cnf-CAcreateserial参数会在第一次签名时生成一个ca.srl序列号文件后续每次签发证书都会递增这个序列号。如果你手动删了它可能出现不同证书序列号重复的情况这会导致一些客户端校验失败所以这个文件要保留别乱删。签发完成后当前目录下应该至少有以下文件ca.key根证书私钥ca.crt根证书localhost.key站点证书私钥localhost.crt站点证书私钥文件的位置和安全要做好管理如果是团队共享环境更是如此别提交到 Git 仓库哪怕只是个本地开发证书也容易被人顺手拿去在恶搞环境里冒充你的服务。3. 让 Nginx 以 HTTPS 方式提供本地服务3.1 准备 NginxWindows 版下载与启动Nginx 在 Windows 上没有正式服务化安装包官方提供的只是一个压缩包。从官网下载 Windows 稳定版解压到一个没有空格的路径下比如D:\nginx-1.26.x\,这一点也重要路径里有空格容易在一些脚本里踩坑。打开D:\nginx-1.26.x\nginx.exe所在目录双击或者命令行直接运行默认情况下 Nginx 就会启动并在 80 端口监听。可以通过下面的命令确认进程状态tasklist | findstr nginx每次改动配置后需要重载配置用D:\nginx-1.26.x\nginx.exe -s reload如果语法有问题reload 会失败并打印错误。Windows 下配置 Nginx 调试某些场景时不像 Linux 那么顺滑但功能层面完全够用。要注意的是 Windows 版 Nginx 没有daemon后台运行的概念开着那个终端窗口它就在前台跑着关掉终端进程就会退出。3.2 配置 HTTPS server 块打开 Nginx 安装目录下的conf\nginx.conf找到http块在里面加一个新的 server 配置。我一般会在文件里额外加一个ssl模块的配置server { listen 443 ssl; server_name localhost dev.local 192.168.1.100; ssl_certificate D:/certs/localhost.crt; ssl_certificate_key D:/certs/localhost.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; location / { root html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location /50x.html { root html; } }ssl_certificate和ssl_certificate_key路径里用的是正斜杠Windows 下 Nginx 一般能兼容正斜杠而且不会像反斜杠那样容易在转义上出问题。ssl_protocols括号里的 TLSv1.2 TLSv1.3 是现在的主流协议版本老旧的 TLSv1.0 和 TLSv1.1 建议直接禁用本地开发也没必要为一个老浏览器降低安全级别。配置完成后跑D:\nginx-1.26.x\nginx.exe -t这个命令会检查配置文件是否有语法错误。看到syntax is ok和test is successful就说明配置没问题然后 reload 即可。3.3 添加 HTTP 强制跳转 HTTPS加了 443 端口之后如果你还希望访问http://dev.local时自动跳到 HTTPS需要再补一个只监听 80 端口的 server 块做跳转server { listen 80; server_name localhost dev.local 192.168.1.100; return 301 https://$host$request_uri; }这个操作在生产环境中很常见本地开发其实不是必须的但如果你用移动设备通过局域网 IP 访问手一滑很容易在 HTTP 版本上卡住配了这个跳转会省很多事。注意这里如果原来 Nginx 默认自带了一个listen 80; server_name localhost;的 server 块就要把它处理好别和新的跳转块冲突。最简单的方式是把里面原本的 root、index 配置迁移到 443 那个 server 块里原来那个 80 的就只保留跳转逻辑或者干脆注释掉。4. 把证书装进 Windows 信任库消除浏览器报错4.1 根证书安装路径证书生成完只是在文件层面完成了一半真正的关键一步是把根证书安装进 Windows 系统信任库。双击ca.crt文件Windows 会打开证书信息窗口。注意别直接点“安装证书”就一路下一步这里有几个关键的选项要选对。点击“安装证书”后在第二步“证书存储”里选择“将所有证书放入下列存储”然后点“浏览”选中“受信任的根证书颁发机构”。这一步非常重要很多人默认会装到“当前用户”看起来成功了但浏览器还是报错其实就是根证书没有进到“受信任的根证书颁发机构”这个特定分区里。在证书信息窗口里点开“颁发者”和“颁发给”两项确认内容是自己刚才创建证书时设置的Local Dev Root CA和localhost。之所以把根证书放进系统信任库而不是直接导入站点证书是因为后续你可能会签一堆不同的站点证书比如dev-api.local、dev-static.local不可能每个都手动装一遍。装了根证书之后所有由它签发的子证书自动被信任这才是模拟真实 CA 的意义。4.2 用 certlm.msc 管理证书除了双击证书安装Windows 还提供了一个更直观的证书管理工具。WinR 输入certlm.msc打开“本地计算机”的证书管理器。展开“受信任的根证书颁发机构”-“证书”在列表里就能看到刚才安装的Local Dev Root CA。如果在certlm.msc里能看到根证书但浏览器仍然报错别急着怀疑证书没装对先检查一下是不是浏览器的自动检测和你安装的是两份不同的根证书。有时候 Windows 缓存的证书信息会比较顽固最有效的办法是重启浏览器。Chrome 和 Edge 在启动时会读取一次系统证书库装完证书不重启的话页面会一直显示旧的验证结果这个坑我踩过太多次了。4.3 域名解析别忘配置 hosts如果你用的是自定义域名比如dev.local还要在 Windows 的 hosts 文件里把域名解析到 127.0.0.1。hosts 文件路径在C:\Windows\System32\drivers\etc\hosts用管理员身份打开记事本追加一行127.0.0.1 dev.local然后执行ipconfig /flushdns刷新 DNS 缓存。下次浏览器访问https://dev.local就会走本机解析配合证书里已经包含的dev.local这个 SAN整个信任链就完整了。这一步很多人漏掉。证书里的 SAN 只解决了 这个证书是合法的 问题但应用层还得知道自己该访问哪个 IP。浏览器在地址栏输域名解析不到对应 IP证书也白搭。5. 常见问题与排查技巧实录5.1 证书错误怎么判断是哪个环节出问题本地 HTTPS 报错一大半都集中在以下几个方面整理成表格方便对照排查浏览器报错常见原因解决方向NET::ERR_CERT_AUTHORITY_INVALID根证书没进受信任机构列表重新安装根证书检查安装位置NET::ERR_CERT_COMMON_NAME_INVALID站点证书 SAN 字段缺少对应域名/IP重新生成站点证书把目标域名或 IP 加进去NET::ERR_CERT_DATE_INVALID站点证书或根证书有效期超出范围检查系统时间是否准确用 openssl x509 -enddate 查看有效期ERR_SSL_PROTOCOL_ERRORNginx 配置协议版本和浏览器不兼容检查 ssl_protocols 是否包含了 TLSv1.2/TLSv1.3访问 IP 显示不安全证书 SAN 里没写 IP或者写了但没刷新重新签发证书并重启浏览器我把NET::ERR_CERT_AUTHORITY_INVALID放到第一位是因为它的出现频率最高。这通常不是证书生成的问题而是 Windows 没有正确信任根证书或者你正在用的浏览器比如 Firefox有自己独立的证书库不读 Windows 系统库。Firefox 是个特例它默认不信任 Windows 的证书存储。要让它也认账需要在 Firefox 设置里搜索证书-“查看证书”-“证书颁发机构”-“导入”手动导入刚刚生成的ca.crt。如果你平时主用 Chrome 或 Edge这个可以跳过。5.2 如何验证证书的 SAN 内容排查 SAN 问题最直接的手段openssl x509 -in localhost.crt -noout -text看输出中间的X509v3 Subject Alternative Name部分里面会列出所有已经写入的域名和 IP。如果这里没有你想要访问的地址那证书重新签一遍也没办法。还有一个容易犯的错误是签名时忘了带-extensions req_ext -extfile localhost.cnf参数。我之前就是因为偷懒想省事直接跑了openssl x509 -req -in localhost.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out localhost.crt -days 825结果生成了证书里没有任何 SAN 字段。如果重新确认后还是提示错误多半是写错了配置文件名或者路径不对。5.3 Nginx 启动失败和端口占用Windows 启动 Nginx 最常遇到的报错就是端口被占用。你可以在第一次启动前先检查一下 443 和 80 端口是否已经被其他程序使用netstat -ano | findstr :443 netstat -ano | findstr :80如果看到LISTENING状态的进程再看最后一列的 PID用tasklist | findstr PID找到是哪个进程占用的。有时候是 IIS有时候是 SQL Server 报表服务也可能是之前跑的 Docker 容器偷偷映射了端口。找到占用进程后要么关掉对应服务要么改掉 Nginx 的监听端口。比如改到 8443listen 8443 ssl;然后访问的时候就要带上端口号https://localhost:8443/。这里要注意证书里的 SAN 填了localhost和 IP依然有效端口不影响证书的校验。5.4 局域网手机访问失败怎么办电脑上用浏览器调试没问题手机通过局域网访问却报不可用这种情况几乎都是防火墙和 IP 绑定问题。先在 PC 上确认 Nginx 确实监听了外部可访问的端口然后确认证书 SAN 里已经加入了电脑当前的局域网 IP。注意 WiFi 网络下 IP 会动态变化今天配的是192.168.1.100明天可能变成192.168.1.105一旦变了证书就失效了这种情况下我建议干脆配一个静态 IP。最后在 Windows 防火墙里放行 443 端口。打开“控制面板”-“Windows Defender 防火墙”-“高级设置”-“入站规则”-“新建规则”选择“端口”填 443允许连接即可。这一步如果没做手机上访问时大概率直接超时连报错都不给你看。还有一点手机访问时手机会校验证书信任链但手机的受信任证书库和你电脑的 Windows 库不是同一个。如果想在手机上完全不报错还得把ca.crt通过邮件或网盘传到手机上安装。Android 的信任设置比较繁琐很多系统 App 不会信任用户安装的证书但至少 Chrome 可以用。6. 我的实操心得这套流程如果只是能用大概二十分钟就能走完真正耗时间的往往是非常不显眼的小细节。我记忆比较深的一次是在一个老项目里前端所有接口都写死了 HTTP 路径CI 脚本里又硬编码了几个用http://的检查。本地 HTTPS 配好之后页面能打开但接口全部请求失败因为浏览器默认把所有跨域 HTTP 请求从安全的 HTTPS 页面里拦了下来。解决方式很粗暴把所有接口地址改成了相对路径或者动态判断协议但那波改动牵连了不少配置文件十分被动。所以我现在只要新起一个项目第一步就会把本地 HTTPS 环境先搭好让开发环境从一开始就在 HTTPS 下跑而不是等出问题再补。另外一个小技巧是如果你经常需要创建多个开发证书可以考虑写一个小脚本来完成全部流程避免重复输入命令。我会在D:\certs\下放一个简单的批处理文件每次需要为新项目生成证书时改一下dev.local之类的主机名再运行脚本它会自动生成根证书、签发站点证书、打印安装提示全程不到一分钟。归根结底工具链熟练了之后如果你有条件可以体验一下 mkcert它把根证书的安装步骤也自动化掉了。不过就算用了工具理解底层的证书链路也还是关键因为总会有工具覆盖不到的场景比如给某个老设备安装根证书、给自定义域名补 SAN等等。说到底本地 SSL 证书这件事本身不难难的是把所有环节串在一起之后遇到的那些看似莫名其妙的小故障。这篇文章如果能帮你从浏览器红色报警一路顺利走到绿色锁头那就有价值了。