Nginx 配置了 HTTPS 但 Chrome 仍然报 Not Secure
为什么 Nginx 配置了 HTTPS,Chrome 仍显示 “Not Secure”
很多开发者在为 Nginx 配置好 SSL 证书后,满以为浏览器会立刻挂上安全的小锁,结果 Chrome 依旧固执地显示 “Not Secure”。这通常不是 Nginx 本身的配置失败,而是页面加载过程中存在不安全的元素,或者证书链不完整、安全策略未被完全实施所致。
本教程将系统性地梳理所有可能原因,并提供清晰的排查路径与解决方案。即使你是刚接触 HTTPS 的新手,也能一步步把安全标识找回来。
1. 首先确认你的 Nginx SSL 基础配置无误
在深入其他问题之前,先确保 Nginx 的 HTTPS 监听、证书路径及密钥都正确。
1.1 检查 server 块与端口监听
你应该有一个类似下面这样监听 443 端口的 server 块:
server {
listen 443 ssl http2; # 开启 ssl 和 http2
server_name example.com www.example.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem; # 证书文件(包含中间证书)
ssl_certificate_key /etc/nginx/ssl/privkey.pem; # 私钥文件
# 其他配置...
}
关键点:
listen指令必须包含ssl,否则端口 443 仅为普通 HTTP。- 使用
http2可提升性能,但不影响安全标识,此处可选项。 server_name必须与证书颁发的域名一致(支持通配符证书时也需匹配)。
1.2 确保证书链完整
许多情况下,缺少中间证书会导致部分浏览器(尤其是 Chrome)无法建立完整的信任链。你可以在命令行测试:
openssl s_client -connect example.com:443 -servername example.com
观察输出中的 Certificate chain 部分,应至少包含服务器证书和中间 CA 证书。若只看到服务器证书,表明 Nginx 配置的证书文件中缺少中间证书。
解决:
- 将你的服务器证书文件与中间证书文件按顺序拼接成一个完整的链文件(通常命名为
fullchain.pem),然后在ssl_certificate中指定它。 - 如果你使用的是 Let's Encrypt 等自动化工具,它通常会生成
fullchain.pem和privkey.pem,确保你使用的是fullchain.pem而不是仅cert.pem。
重新加载 Nginx:
nginx -t && systemctl reload nginx
2. 排查混合内容 (Mixed Content):Chrome “Not Secure” 的最常见元凶
若你的证书配置完全正确,但页面仍然显示 “Not Secure”,几乎可以断定问题出在页面内部仍然通过 http:// 加载某些资源上。这就是所谓的混合内容。
2.1 什么是混合内容?
- 被动混合内容:
<img>、<audio>、<video>等通过http://加载的资源,虽然不会直接修改页面 DOM,但会泄漏用户信息,Chrome 会直接标记页面为 “Not Secure”。 - 主动混合内容:
<script>、<link>(CSS)、<iframe>、XMLHttpRequest 等通过http://加载,Chrome 会阻止这些请求并同样显示不安全。
2.2 如何快速发现混合内容?
打开 Chrome 开发者工具(F12),切换到 Console(控制台) 标签,你会看到类似如下的警告:
Mixed Content: The page at 'https://example.com/' was loaded over HTTPS, but requested an insecure image 'http://cdn.example.com/logo.png'. This request has been blocked; the content must be served over HTTPS.
同时,审查 Security(安全) 面板(如果看不到该面板,点击 DevTools 右上角的 >> 展开),也会明确显示 “Mixed content” 提示及具体资源。
2.3 解决混合内容的几种方案
方案一:将资源链接全部改为 https://
最彻底的做法。直接修改 HTML 或 CMS 模板中的绝对路径:
<!-- 错误 -->
<img src="http://cdn.example.com/logo.png" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<!-- 正确 -->
<img src="https://cdn.example.com/logo.png" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
方案二:使用协议相对 URL(Protocol-relative URL)
将 http:// 或 https:// 省略,改为 //,浏览器会自动使用当前页面的协议:
<script src="//ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
但需注意,如果第三方资源仅支持 HTTP,这种写法仍可能导致请求以 HTTP 发出。因此推荐直接指定 https。
方案三:通过 Nginx 的 Content-Security-Policy 头自动升级请求
这是对动态网站极为高效的补救措施。在 Nginx 配置中添加以下响应头:
add_header Content-Security-Policy "upgrade-insecure-requests;";
这个策略会告诉浏览器,将页面中所有 http:// 资源请求自动转为 https://。但要注意:此头会强制升级所有请求,如果某些资源确实无法通过 HTTPS 访问,就会加载失败。仅适合确认所有外部资源都已支持 HTTPS 的情况下作为临时或长期方案。
在 server 或 location 块中配置:
server {
listen 443 ssl;
...
add_header Content-Security-Policy "upgrade-insecure-requests;";
}
2.4 特别提醒:外部嵌入内容
如果你的网页通过 <iframe> 嵌入了其他 HTTP 网站,也会导致 “Not Secure”。检查所有 iframe 的 src 属性。
3. 检查证书本身的有效性和域名匹配
即使 Nginx 加载了证书,但如果证书本身已过期、被撤销,或与访问的域名不匹配,Chrome 会直接显示 “Not Secure” 并在地址栏划掉锁。
3.1 验证证书有效期
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
输出示例:
notBefore=Jun 1 00:00:00 2023 GMT
notAfter=Aug 30 12:00:00 2023 GMT
确保当前系统时间在有效期内。
3.2 确认证书主题与域名一致
openssl s_client -connect example.com:443 -servername example.com | openssl x509 -noout -subject -ext subjectAltName
观察返回的 DNS 条目,必须包含你访问的域名(例如 example.com、www.example.com)。如果访问的是子域名但证书不包含该子域名,也会被判定不安全。
4. 检查 HTTP Strict Transport Security (HSTS) 是否配置错误
HSTS 是一把双刃剑。如果已经配置了 HSTS,但证书有问题或网站存在混合内容,Chrome 会强制升级所有请求为 HTTPS 并阻止不安全连接,从而可能掩盖真正原因。同时,HSTS 配置本身错误也可能干扰调试。
重新审视 Nginx HSTS 头
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
如果当前证书有问题,而你仍想诊断页面,可以临时删除该配置行并重启 Nginx,然后清除 Chrome 的 HSTS 缓存(在地址栏输入 chrome://net-internals/#hsts,在 “Delete domain security policies” 中输入域名并删除)。
5. TLS 版本与加密套件不兼容旧客户端(但 Chrome 一般不会)
虽然现代 Chrome 支持较新的 TLS 1.2/1.3,但若你的 Nginx 仅启用了过于古老的协议(如 TLS 1.0)或使用了不安全的加密套件,其他浏览器或旧版浏览器会提示不安全。Chrome 通常只会给出较弱的提示,但仍建议优化。
推荐的安全配置:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
6. 检查 Chrome 的缓存与安全策略残留
有时候问题仅仅是因为你之前访问过该网站的 HTTP 版本,浏览器缓存了重定向或 HSTS 信息。
清除方法:
- 在 Chrome 中打开 DevTools → Application 标签 → Clear storage,清除站点数据。
- 或者直接使用无痕模式访问,看是否依然显示 “Not Secure”。
- 同时检查
chrome://settings/security确保没有手动将该网站加入不信任列表。
7. 最终验证 Checklist 总结
完成修改后,按以下清单逐项确认:
- 服务器监听 443 端口并启用 ssl
- 证书文件包含完整证书链(fullchain.pem)
-
openssl s_client显示证书信任链完整、未过期、域名匹配 - 开发者工具 Console 中无混合内容警告
- 所有资源(图片、脚本、CSS、字体、iframe)均为
https://或相对路径 - 未对不可升级的资源使用
upgrade-insecure-requests - HSTS 头没有干扰调试(临时移除测试)
- TLS 版本与加密套件符合现代标准
- 清理浏览器缓存/使用无痕模式测试
遵循以上步骤,你网站的 HTTPS 安全锁一定会重新出现在 Chrome 地址栏中。
通过这个从基础配置到混合内容再到证书链的完整排查,你已经能够应对绝大多数 “配置了 HTTPS 仍显示 Not Secure” 的场景。始终记住:安全的页面不仅要证书有效,更要所有加载的资源都走安全通道。