HTTPS 证书问题排查:从「不安全」警告到证书链完整
配好 HTTPS 只是开始。浏览器地址栏出现红色警告时,别慌,按现象分五类,每类都有固定的排查路径。
一、先做通用体检
# 1. 看证书有效期与颁发者
echo | openssl s_client -connect www.bzii.cn:443 -servername www.bzii.cn 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
# 2. 看完整证书链(关键:chain 里有没有中间证书)
openssl s_client -connect www.bzii.cn:443 -servername www.bzii.cn -showcerts </dev/null 2>/dev/null
# 3. 验证结果
openssl s_client -connect www.bzii.cn:443 -servername www.bzii.cn </dev/null 2>/dev/null | grep -E "Verify|depth"
# 4. HTTP 状态码
curl -o /dev/null -s -w "%{http_code}\n" https://www.bzii.cn二、现象一:证书链不完整(部分设备报不安全)
表现:电脑浏览器正常,手机或某些安卓设备提示证书不受信任。
原因:Nginx 只配了服务器证书,没配中间证书(intermediate)。
# 错误写法(只有站点证书)
ssl_certificate /etc/letsencrypt/live/www.bzii.cn/cert.pem;
# 正确写法(fullchain = 站点证书 + 中间证书)
ssl_certificate /etc/letsencrypt/live/www.bzii.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.bzii.cn/privkey.pem;certbot 生成的目录里四个文件的区别:
| 文件 | 内容 |
|---|---|
cert.pem | 只有站点证书 |
chain.pem | 只有中间证书 |
fullchain.pem | 站点 + 中间(用这个) |
privkey.pem | 私钥 |
三、现象二:域名不匹配(NET::ERR_CERT_COMMON_NAME_INVALID)
原因:证书里没有你访问的那个域名。比如证书只签了 www.bzii.cn,你访问 www.bzii.cn。
# 看证书覆盖了哪些域名(SAN 列表)
echo | openssl s_client -connect www.bzii.cn:443 -servername www.bzii.cn 2>/dev/null \
| openssl x509 -noout -text | grep -A1 "Subject Alternative Name"解法:签发时把所有要用的域名都带上,或直接用通配符:
# 多域名
sudo certbot --nginx -d www.bzii.cn -d www.bzii.cn -d api.bzii.cn
# 通配符(必须用 DNS-01 校验)
sudo certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials ~/.cloudflare.ini \
-d "*.www.bzii.cn" -d www.bzii.cn四、现象三:混合内容(Mixed Content)
表现:地址栏锁是灰的或带感叹号,控制台报 Mixed Content: loaded over HTTP。
原因:HTTPS 页面里引用了 http:// 的图片、脚本、样式。
# 找出页面里的 http 资源
curl -s https://www.bzii.cn | grep -o "http://[^"]*" | sort -u解法优先级:
- 改成相对协议或全站 https:
src="//cdn.example.com/a.js"或直接https://; - 图片等静态资源搬到自己的域名下;
- 加
upgrade-insecure-requests兜底(强制把 http 请求升级为 https):
add_header Content-Security-Policy "upgrade-insecure-requests";五、现象四:证书过期
# 手动续期
sudo certbot renew
sudo systemctl reload nginx
# 看为什么自动续期没成功
systemctl status certbot.timer
journalctl -u certbot.service
sudo certbot renew --dry-run自动续期失败的三个常见原因:
- 80 端口被挡:HTTP-01 校验需要 80 端口可达,防火墙/安全组要放行;
- CDN 挡住:Cloudflare 橙色云会拦截校验路径,临时改灰或改用 DNS-01;
- Nginx 没重载:证书更新了但 Nginx 还加载旧文件,加个 deploy hook:
echo "reload hook" > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
cat > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh <<EOF
#!/bin/sh
systemctl reload nginx
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh六、现象五:HSTS 导致的「删不掉」的报错
表现:证书修好了,浏览器还是强制 HTTPS 并报错。
原因:之前访问时服务器下发过 HSTS 头,浏览器缓存了(最长一年)。
解法:
- Chrome 访问
chrome://net-internals/#hsts,在 Delete domain 里输入域名删除; - 或者等 max-age 过期(如果你设了 31536000,那就是一年)。
这也说明 HSTS 别乱加:确认 HTTPS 全站稳定了再加,建议先用小 max-age(
max-age=300)测试。
七、一份排查顺序
curl看状态码 → 确认是证书问题还是服务问题;openssl看有效期和 SAN → 排除过期/域名不匹配;-showcerts看链 → 排除中间证书缺失;- 页面源码搜
http://→ 排除混合内容; - 还不行 → SSL Labs 完整评测,它会明确指出问题。
