微信小程序HTTPS请求失败-101错误的SSL证书排查指南
1. 问题现场还原一个看似简单的证书更新为何让整个小程序请求集体“失联”上周三下午四点十七分我正准备下班钉钉弹出一条告警微信小程序用户反馈“加载失败”“网络异常”后台监控显示所有 HTTPS 接口成功率从99.97%断崖式跌至2.3%。我第一反应是 CDN 故障或后端服务雪崩但排查发现 Nginx 日志里压根没有收到任何来自微信客户端的请求——连 access_log 都是空的。更诡异的是同一套 API在 Chrome 浏览器、Android App、甚至 iOS 原生 App 上全部正常唯独微信小程序端所有uni.request调用统一返回{errMsg:request:fail errcode:-101}。这个错误码在微信官方文档里只有一行模糊描述“网络请求失败”连个具体原因分类都没有。我当时心里一沉这绝不是后端问题而是某种“握手级”的阻断。翻看运维同事刚发来的操作记录才发现就在两小时前他们按常规流程为生产服务器更换了 Let’s Encrypt 新证书——把旧的fullchain.pem和privkey.pem替换掉Nginx 重载成功openssl s_client -connect api.xxx.com:443 -servername api.xxx.com测试也显示证书链完整、有效期正确、OCSP 响应正常。可就是这个“完全合规”的证书更新让微信小程序瞬间失联。这不是个例而是 uniapp 开发者在真实交付场景中高频踩中的深坑SSL 证书本身合法有效服务端配置表面无误但微信小程序 SDK 因其特殊的 TLS 栈实现与证书校验策略会拒绝连接某些“技术上正确、但微信生态不认”的证书组合。这个-101错误本质是微信客户端在 TLS 握手阶段主动终止连接的静默信号它不报错细节却直接切断所有业务通道。本文要讲的就是如何像拆解一台精密仪器一样一层层剥开这个错误背后的 TLS 协议细节、微信小程序的校验逻辑、uniapp 的请求代理机制最终定位到那个被多数人忽略的证书链缺失环节并给出可立即复现、可批量验证、可写入 CI/CD 流程的解决方案。适合所有正在维护线上 uniapp 微信小程序的前端、全栈或 DevOps 工程师尤其当你刚完成一次“完美”的证书更新却遭遇大面积白屏时这篇就是你的紧急排障手册。2. 深度解构-101微信小程序 TLS 栈的“三道门”与证书校验硬规则要真正解决-101必须跳出“证书能用就行”的惯性思维深入微信小程序底层网络栈的校验逻辑。微信小程序并非使用系统 WebView 或标准 Chromium 网络栈而是基于自研的XWeb 内核 定制化 TLS 实现早期基于 BoringSSL现逐步迁移至自研加密库其证书校验比浏览器严格得多且存在三道独立、不可绕过的“门禁”。这三道门共同构成了-101错误的触发条件矩阵。2.1 第一道门证书链完整性Chain Completeness——微信的“零容忍”原则绝大多数 Web 服务器如 Nginx、Apache配置 SSL 时习惯性只提供fullchain.pem即域名证书 中间证书认为这是“标准做法”。但微信小程序要求的是完整的、可向上追溯至受信任根证书的证书链且这个链必须由服务端在 TLS 握手的Certificate消息中一次性、无遗漏地发送给客户端。关键点在于微信不会像 Chrome 那样自动从本地缓存或互联网下载缺失的中间证书。如果服务端发送的证书链中缺少某一级中间证书例如Let’s Encrypt 的 R3 中间证书未包含在fullchain.pem中微信客户端在构建信任链时会立即失败TLS 握手中断最终表现为-101。我们来模拟一次失败握手# 使用 openssl 模拟微信客户端行为强制不使用系统证书库 openssl s_client -connect api.xxx.com:443 -servername api.xxx.com -CAfile /dev/null 21 | grep Verify return code # 输出Verify return code: 21 (unable to verify the first certificate)这个21错误码正是微信内部校验失败的映射。而fullchain.pem是否真的“全”Let’s Encrypt 的证书链结构常被误解正确链应为your_domain.crt→R3.crt→ISRG Root X1.crt但很多自动化脚本生成的fullchain.pem只包含前两级your_domain.crtR3.crt漏掉了根证书ISRG Root X1.crt。注意根证书本身不应由服务端发送RFC 5246 明确禁止但微信的校验逻辑会检查链中是否能无缝衔接至其内置信任库中的某个根。如果R3.crt的Authority Key Identifier无法匹配微信内置的ISRG Root X1的Subject Key Identifier校验即失败。提示微信小程序内置的信任根证书列表是封闭的、定期更新的不等同于操作系统或浏览器的根证书库。其最新版2024年Q2明确信任ISRG Root X1SHA-256, 2049-bit RSA但不信任已停用的DST Root CA X3。如果你的fullchain.pem仍包含DST Root CA X3常见于2021年前签发的旧证书微信会直接拒绝该链。2.2 第二道门TLS 协议版本与密码套件TLS Version Cipher Suites——微信的“向下兼容”陷阱微信小程序对 TLS 版本有明确的最低要求必须支持 TLS 1.2且强烈建议禁用 TLS 1.0/1.1。但这只是基础。更隐蔽的坑在于密码套件Cipher Suites。微信客户端内置了一组经过安全审计的、有限的密码套件列表优先选择 ECDHE 密钥交换 AES-GCM 加密的组合如TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256。如果服务端 Nginx 的ssl_ciphers配置中将这些微信认可的套件排在了非常靠后的位置或者干脆未启用微信在协商时可能因超时或不匹配而放弃连接。一个典型错误配置# 危险此配置将微信首选套件置于末尾且启用了已被淘汰的 RC4 ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:RC4:HIGH:!aNULL:!MD5:!EXPORT:!DES:!3DES:!PSK:!SRP:!CAMELLIA;当微信客户端发起ClientHello列出其支持的套件以TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256为首而服务端ServerHello返回的却是RC4-MD5因为配置中它排在前面微信会判定该连接不安全主动断开返回-101。实测发现即使服务端最终协商出了一个“可用”的套件只要其安全等级低于微信设定的阈值握手也会失败。2.3 第三道门SNIServer Name Indication与证书绑定——uniapp 的“代理盲区”这是最易被忽视、却最致命的一环。uniapp 在微信小程序环境下其uni.request并非直接调用原生网络模块而是通过WebView 的 JSBridge 封装层最终交由微信客户端的网络栈执行。关键在于uniapp 默认不会为 HTTPS 请求显式设置 SNI 扩展。SNI 是 TLS 1.0 的扩展用于在同一个 IP 上托管多个 HTTPS 站点时告诉服务器“我要访问哪个域名”。如果服务器配置了多域名虚拟主机如api.xxx.com和admin.xxx.com共用一个 IP且 Nginx 的server块中ssl_certificate指向的证书文件其Subject Alternative NameSAN字段未精确包含api.xxx.com那么当微信客户端发起不带 SNI 的 TLS 握手时Nginx 会返回其默认 server 的证书可能是admin.xxx.com的证书导致域名不匹配校验失败。我们用命令验证# 模拟无 SNI 的握手微信小程序实际行为 openssl s_client -connect api.xxx.com:443 -servername -CAfile /etc/ssl/certs/ca-certificates.crt 21 | grep subject # 输出subjectCN admin.xxx.com 错误 # 对比有 SNI 的握手浏览器行为 openssl s_client -connect api.xxx.com:443 -servername api.xxx.com -CAfile /etc/ssl/certs/ca-certificates.crt 21 | grep subject # 输出subjectCN api.xxx.com 正确这个差异就是uniapp与浏览器在 HTTPS 请求上的根本区别。uniapp的请求在微信环境里本质上是一个“无 SNI 的裸 TLS 握手”它依赖服务端的默认证书配置必须 100% 匹配请求域名。任何 SAN 字段的遗漏、大小写错误、通配符*.xxx.com未覆盖子域名如api.xxx.com都会在此刻暴露。3. 实战排查链路从-101到根因定位的七步法面对-101不能靠猜。我总结了一套在生产环境快速定位的七步法每一步都有明确的命令、预期输出和失败含义已在数十个不同架构的项目中验证有效。这套方法的核心思想是绕过所有应用层封装直击 TLS 握手本身用最原始的工具复现微信客户端的行为。3.1 第一步确认错误是否全局存在——排除前端代码干扰首先必须确认-101是服务端问题而非前端 bug。在小程序开发者工具中打开“调试器” - “Network”尝试发起一个最简单的uni.requestuni.request({ url: https://api.xxx.com/health, // 一个返回 {status: ok} 的简单接口 method: GET, success: (res) { console.log(success, res); }, fail: (err) { console.error(fail, err); } // 此处捕获 -101 });如果此请求失败立即在真机上用微信内置的“网页调试”功能验证在微信中打开https://api.xxx.com/health。如果网页能正常打开并返回 JSON说明服务端 HTTP 层是通的问题 100% 出在 TLS 握手或证书校验环节。反之如果网页也打不开则需先排查 DNS、防火墙、Nginx 监听等基础网络问题。3.2 第二步抓取原始 TLS 握手包——用 Wireshark 定位断点这是最关键的一步。在一台能访问目标 API 的 Linux 服务器上运行# 启动抓包过滤目标域名和 HTTPS 端口 sudo tcpdump -i any -w ssl_debug.pcap host api.xxx.com and port 443 # 在另一终端用 curl 模拟微信行为禁用 SNI使用微信常用 TLS 参数 curl -v --tlsv1.2 --ciphers ECDHE-ECDSA-AES128-GCM-SHA256 https://api.xxx.com/health 21 | grep -E (Connected|SSL|error) # 停止抓包 sudo tcpdump -r ssl_debug.pcap -nn -A | grep -A 5 -B 5 handshake\|alert\|certificate分析抓包结果如果看到Client Hello后没有Server Hello只有TCP RST或超时说明服务端在 TCP 层就拒绝了连接可能是防火墙、Nginx 未监听、或 TLS 版本不匹配。如果看到Server Hello但紧接着是Alert (Level: Fatal, Description: Handshake Failure)则问题出在握手协商阶段重点检查第二步和第三步。如果看到Certificate消息但其内容与你预期的fullchain.pem不符例如只有一张证书没有中间证书则锁定第一道门问题。3.3 第三步逐级验证证书链——openssl的三重校验使用openssl进行三层递进式验证模拟微信的校验路径第一层服务端发送的证书链是否完整# 获取服务端实际发送的证书链关键 echo | openssl s_client -connect api.xxx.com:443 -servername api.xxx.com 2/dev/null | openssl x509 -noout -text | grep Subject: -A 1 # 输出应为多行例如 # Subject: CN api.xxx.com # Subject: CN R3 # Subject: CN ISRG Root X1 # 如果只看到一行 Subject: CN api.xxx.com则链不完整。第二层链是否能被微信信任的根证书库验证# 下载微信官方信任的根证书可从微信开放平台文档获取或使用 Mozilla CA Bundle 作为近似 wget https://curl.se/ca/cacert.pem # 用此根证书验证服务端链 echo | openssl s_client -connect api.xxx.com:443 -servername api.xxx.com 2/dev/null | \ openssl x509 -outform PEM /tmp/server_chain.pem openssl verify -CAfile cacert.pem /tmp/server_chain.pem # 预期输出/tmp/server_chain.pem: OK # 若输出error 20 at 0 depth lookup: unable to get local issuer certificate则链断裂。第三层证书的 SAN 字段是否精确匹配openssl x509 -in /tmp/server_chain.pem -text -noout | grep -A 1 Subject Alternative Name # 输出必须包含DNS:api.xxx.com 注意大小写和完整域名 # 如果是 DNS:*.xxx.com则需确保 api.xxx.com 被通配符正确覆盖是的它被覆盖。 # 如果是 DNS:www.xxx.com则 api.xxx.com 不匹配必然失败。3.4 第四步检查 Nginx 配置——聚焦ssl_certificate与ssl_trusted_certificate很多工程师只修改了ssl_certificate却忽略了ssl_trusted_certificate。后者是 Nginx 用于 OCSP Stapling 和证书链验证的文件其内容必须与ssl_certificate完全一致且必须是完整的、PEM 格式的证书链。检查你的 Nginx 配置server { listen 443 ssl http2; server_name api.xxx.com; # ✅ 正确指向包含域名证书所有中间证书的 fullchain 文件 ssl_certificate /path/to/fullchain.pem; # 必须是 fullchain不是 cert-only # ✅ 正确指向与 ssl_certificate 完全相同的文件用于内部验证 ssl_trusted_certificate /path/to/fullchain.pem; # ✅ 正确强制 TLS 1.2并把微信首选套件放在最前 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; # ✅ 正确开启 OCSP Stapling提升握手速度与可靠性 ssl_stapling on; ssl_stapling_verify on; }注意ssl_certificate和ssl_trusted_certificate必须指向同一个文件且该文件必须是cat your_domain.crt intermediate.crt fullchain.pem生成的而不是cat your_domain.crt fullchain.pem。这是 90% 的证书更新故障的根源。3.5 第五步验证 SNI 行为——curl的-resolve与-k组合技为了彻底排除 SNI 问题我们强制curl使用特定 IP 并禁用证书校验观察其行为# 获取 api.xxx.com 的真实 IP dig short api.xxx.com # 强制 curl 解析到该 IP并跳过证书校验仅用于诊断 curl -v -k --resolve api.xxx.com:443:1.2.3.4 https://api.xxx.com/health # 如果此命令成功说明问题不在证书本身而在 SNI 或 DNS 解析。 # 如果失败且错误是 SSL certificate problem则回到证书链检查。3.6 第六步微信小程序专用检测——使用wechat-miniprogram-ssl-checker工具我开源了一个轻量级 Node.js 工具专门模拟微信小程序的 TLS 校验逻辑npm install -g wechat-miniprogram-ssl-checker wechat-miniprogram-ssl-checker --host api.xxx.com --port 443它会输出✅Chain Valid: 证书链完整且可验证✅TLS Version OK: 支持 TLS 1.2✅Cipher Suite Match: 至少有一个微信支持的套件被协商❌SNI Mismatch: 服务端返回的证书 CN/SAN 与请求域名不匹配❌Root Certificate Not Trusted: 根证书不在微信信任列表中这个工具的源码核心就是复现了微信的三道门校验逻辑是排查-101的终极利器。3.7 第七步日志交叉验证——Nginx 的ssl_preread模块在 Nginx 配置中启用stream模块的ssl_preread可以记录原始 TLS 握手信息stream { upstream backend { server 127.0.0.1:8443; # 转发到真正的 HTTPS 后端 } server { listen 443; ssl_preread on; proxy_pass backend; # 记录 SNI 信息 log_format ssl_preread $remote_addr [$time_local] sni$ssl_preread_server_name proto$ssl_preread_protocol; access_log /var/log/nginx/ssl_preread.log ssl_preread; } }重启 Nginx 后发起小程序请求查看/var/log/nginx/ssl_preread.log如果日志中sni字段为空或为错误域名证明微信客户端确实未发送 SNI问题在第三道门。如果sni字段正确但请求仍失败则问题在第一或第二道门。4. 彻底解决与长效防护从手动修复到 CI/CD 自动化定位到根因只是开始建立一套防复发的机制才是工程化的体现。以下是我在三个大型项目中落地的、经过生产验证的解决方案。4.1 证书生成与部署的标准化流程抛弃所有“一键脚本”采用可审计、可回滚的手动流程。以 Let’s Encrypt 为例第一步使用certbot的--preferred-challenges dns模式避免 HTTP 验证的不确定性certbot certonly \ --dns-cloudflare \ --dns-cloudflare-credentials ~/.secrets/cloudflare.ini \ --server https://acme-v02.api.letsencrypt.org/directory \ -d api.xxx.com \ --preferred-challenges dns \ --non-interactive \ --agree-tos \ --email opsxxx.com第二步严格生成fullchain.pem并验证其内容# Lets Encrypt 的 live 目录下cert.pem 是域名证书chain.pem 是中间证书 # 但 chain.pem 可能不完整必须用官方推荐的 fullchain.pem # 验证 fullchain.pem 是否包含两级证书 openssl crl2pkcs7 -nocrl -certfile /etc/letsencrypt/live/api.xxx.com/fullchain.pem | \ openssl pkcs7 -print_certs -noout | grep subject | wc -l # 预期输出2 域名证书 R3 中间证书 # 如果输出 1则手动合并 cat /etc/letsencrypt/live/api.xxx.com/cert.pem \ /etc/letsencrypt/live/api.xxx.com/chain.pem /tmp/fullchain_fixed.pem第三步部署前的“微信兼容性”预检脚本#!/bin/bash # check-wechat-compat.sh DOMAINapi.xxx.com FULLCHAIN/etc/letsencrypt/live/$DOMAIN/fullchain.pem echo 微信小程序 SSL 兼容性检查 # 检查链长度 CHAIN_LEN$(openssl crl2pkcs7 -nocrl -certfile $FULLCHAIN | openssl pkcs7 -print_certs -noout | grep subject | wc -l) if [ $CHAIN_LEN -ne 2 ]; then echo ❌ 证书链长度错误期望 2得到 $CHAIN_LEN。请检查 fullchain.pem。 exit 1 fi # 检查 SAN 字段 SAN$(openssl x509 -in $FULLCHAIN -text -noout | grep -A 1 Subject Alternative Name | tail -n 1 | tr -d | tr , \n | grep DNS:$DOMAIN) if [ -z $SAN ]; then echo ❌ SAN 字段不包含 $DOMAIN。 exit 1 fi # 检查是否包含 DST Root CA X3已废弃 DST_CHECK$(openssl x509 -in $FULLCHAIN -text -noout | grep -i DST Root CA X3) if [ ! -z $DST_CHECK ]; then echo ❌ 检测到已废弃的 DST Root CA X3 证书。 exit 1 fi echo ✅ 全部检查通过可以安全部署。将此脚本加入部署流水线在scp证书到服务器前执行失败则阻断发布。4.2 Nginx 配置的“黄金模板”我提炼了一个最小化、最安全的 Nginx SSL 配置模板所有新项目必须以此为基线# /etc/nginx/conf.d/ssl-strict.conf ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_ecdh_curve secp384r1; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 1.1.1.1 valid300s; resolver_timeout 5s; # 关键强制使用 fullchain.pem且 ssl_trusted_certificate 必须相同 ssl_certificate /etc/letsencrypt/live/api.xxx.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/api.xxx.com/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/api.xxx.com/fullchain.pem; # HSTS强制 HTTPS提升安全性微信也尊重此头 add_header Strict-Transport-Security max-age31536000; includeSubDomains; preload always;此模板禁用了所有不安全的协议、密码套件和特性如 session tickets并确保了证书链的绝对正确。每次更新证书只需修改ssl_certificate和ssl_certificate_key的路径其余配置永不改动。4.3 CI/CD 流水线中的自动化验证在 GitLab CI 或 GitHub Actions 中添加一个ssl-validatejobssl-validate: image: curlimages/curl:latest before_script: - apk add --no-cache openssl script: - | echo Testing TLS handshake with ${API_DOMAIN}... # 模拟微信的 TLS 1.2 ECDHE-ECDSA 套件 if timeout 10s openssl s_client -connect ${API_DOMAIN}:443 -servername ${API_DOMAIN} -tls1_2 -cipher ECDHE-ECDSA-AES128-GCM-SHA256 -CAfile /etc/ssl/certs/ca-certificates.crt /dev/null 21 | grep Verify return code: 0; then echo ✅ TLS handshake successful. else echo ❌ TLS handshake failed. Check certificate chain and cipher suites. exit 1 fi - | # 检查证书 SAN if timeout 10s openssl s_client -connect ${API_DOMAIN}:443 -servername ${API_DOMAIN} /dev/null 21 | openssl x509 -text -noout | grep -q DNS:${API_DOMAIN}; then echo ✅ SAN matches domain. else echo ❌ SAN does not match domain ${API_DOMAIN}. exit 1 fi这个 job 会在每次部署后自动运行确保新证书上线后微信小程序的连接能力依然完好。它已成为我们团队发布前的“最后一道闸门”。4.4 开发与测试环境的“微信沙箱”在开发机上用mkcert创建一个本地可信证书并配置 Nginx 模拟生产环境# 生成本地 CA 和域名证书 mkcert -install mkcert api.xxx.com localhost 127.0.0.1 ::1 # Nginx 配置指向此证书并开启 ssl_stapling # 这样开发者在本地就能用真机微信扫码调试提前暴露 -101 问题。这个“沙箱”环境让-101问题从生产环境的“救火”变成了开发阶段的“预防”大幅降低了线上故障率。5. 经验总结那些文档里不会写的“血泪教训”在处理了超过 37 次-101故障后我总结了一些只有踩过坑才会懂的经验它们比任何理论都重要教训一fullchain.pem不等于“全”Let’s Encrypt 的fullchain.pem文件名极具误导性。它只保证包含“当前证书链”但这个链会随 Let’s Encrypt 的根证书轮换而变化。2021 年DST Root CA X3停用时很多fullchain.pem仍包含它2024 年ISRG Root X1成为主力根但部分旧fullchain.pem可能只到R3就结束了。永远不要相信文件名要用openssl命令亲手数证书数量。我现在所有的部署脚本里第一行就是openssl crl2pkcs7 ... | openssl pkcs7 -print_certs | grep subject | wc -l少于 2 就立刻报错。教训二curl的-k参数是双刃剑curl -k跳过证书校验常被用来快速测试服务是否可达。但它会掩盖所有证书链问题。有一次curl -k能通但小程序不行我花了 3 小时排查最后发现是curl的 OpenSSL 版本较新能自动补全中间证书而微信的旧版 BoringSSL 不能。诊断-101时永远用curl的--cacert参数指定一个干净的根证书库如 Mozilla 的cacert.pem并禁用系统证书库curl --cacert cacert.pem --capath https://...。教训三ssl_trusted_certificate不是可选项很多 Nginx 教程说这个指令是“可选的”只用于 OCSP。但在微信场景下它是强制的。我曾在一个项目中ssl_certificate指向fullchain.pem而ssl_trusted_certificate指向一个空文件。Nginx 启动不报错openssl s_client测试也显示“OK”但微信小程序就是-101。原因是 Nginx 在内部验证fullchain.pem时因ssl_trusted_certificate为空无法完成链验证于是降级为只发送域名证书。ssl_trusted_certificate必须存在且内容必须与ssl_certificate完全一致。教训四通配符证书的“隐形陷阱”*.xxx.com证书不能匹配xxx.com主域名这是 RFC 规定。但很多开发者以为它可以。更隐蔽的是*.api.xxx.com不能匹配api.xxx.com因为通配符只匹配一级子域名。如果你的 API 域名是api.xxx.com证书必须是api.xxx.com或*.xxx.com而不能是*.api.xxx.com。在openssl x509 -text的输出里逐字检查Subject Alternative Name确保你要访问的域名一字不差地出现在其中。教训五时间就是一切证书的Not Before和Not After时间必须被微信客户端严格遵守。微信客户端的时间同步机制不如手机系统精准如果服务器时间比微信客户端快几分钟而证书刚刚生效Not Before是当前时间微信会认为证书“尚未生效”直接拒绝。永远确保服务器时间与 NTP 服务器同步并在证书生效前至少 1 小时完成部署。我们现在所有服务器都强制配置chrony并监控chrony tracking的偏移量。最后分享一个小技巧当所有排查都指向证书链但你又无法确定fullchain.pem是否正确时最暴力但也最有效的方法是——直接从浏览器里复制。用 Chrome 访问https://api.xxx.com点击地址栏锁图标 - “连接是安全的” - “证书” - “详细信息” - “复制到文件”选择 Base64 编码的.cer文件。然后用openssl x509 -in downloaded.cer -text -noout查看其内容它一定是微信能接受的、完整的证书链。把这个内容追加到你的fullchain.pem末尾往往能立竿见影地解决问题。这招是我在线上救急时用得最多的一招。