上周三下午两点,客户突然打电话过来说网站被浏览器报"不安全",地址栏左边是个红色三角形。我打开Chrome一看,果然报了"您的连接不是私密连接"。排查了半天才发现是HTTPS证书链不完整加安全响应头缺失导致的。这篇把排查过程和关键配置记下来。
一、先定位:浏览器报的是什么
打开Chrome的开发者工具,Security面板显示:证书已过期但实际证书是有效的,问题出在中间证书没配置。另外Console里有几条Mixed Content警告——页面是HTTPS但引用了HTTP的图片资源。
排查清单:
1. Security面板看证书链是否完整
2. Console搜"Mixed Content"
3. 用curl -I看响应头
4. SSL Labs测试评级
二、安全响应头:CSP、HSTS、X-Frame-Options
光有HTTPS不够,浏览器还需要几个安全响应头来防止点击劫持、MIME嗅探和协议降级。在Nginx配置里加上:
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Content-Security-Policy "default-src 'self'; img-src 'self' https: data:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'" always;
HSTS的max-age设了63072000秒(两年),includeSubDomains表示所有子域名也强制HTTPS。CSP里img-src允许https:和data:,因为文章里有内嵌图片。
踩坑提醒:CSP配太严会把正常的JS和CSS也拦了,建议先只报告模式(Content-Security-Policy-Report-Only)观察一周,再切到强制模式。
三、证书链补全:中间证书不能漏
客户用的是Let's Encrypt证书,Nginx配置里只指定了fullchain.crt但服务器上这个文件其实是域名证书,没有拼中间证书。浏览器在某些环境下不会自动补全中间证书链,就会报"证书不受信任"。
正确做法是把域名证书和中间证书按顺序拼在一个文件里:
# fullchain.pem 内容顺序:
# 1. 域名证书 (-----BEGIN CERTIFICATE----- ... 域名证书 ...)
# 2. 中间证书 (-----BEGIN CERTIFICATE----- ... 中间证书 ...)
# 不要加CA根证书,根证书由浏览器内置
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
配置完用openssl s_client -connect 域名:443 -showcerts验证,返回的证书链应该有2个证书(域名+中间),不是只有1个。
四、实际踩过的5个坑
- HSTS加了max-age=300,第二天想切回HTTP测试发现浏览器强制跳HTTPS回不去,后来等了5分钟才恢复——max-age不要一开始就设太大。
- CSP里漏了data:,导致内嵌的base64图片全部加载失败,Console报Refused to load。
- X-Frame-Options设成了DENY,但页面里有个同域的iframe嵌入客服窗口,结果iframe白屏,改成SAMEORIGIN就好了。
- 证书文件权限设成了777,Nginx启动直接报错——privkey.pem必须是600,fullchain.pem可以是644。
- Mixed Content里有个统计脚本引用了http://,改成//协议相对URL还不行,必须写成https://。
这次网站安全头配置是在乔拓云的建站产品上做的,我主要在Nginx层补了证书链和安全响应头,浏览器安全评级从B升到了A。
复盘要点
- HTTPS不只是装证书,证书链必须包含中间证书。
- 安全响应头至少配HSTS、X-Frame-Options、X-Content-Type-Options三个。
- CSP先用Report-Only模式观察,不要一上来就强制。
以上是个人实践记录,各平台具体功能以官方实时信息为准。
留个问题:你们生产环境CSP是用Report-Only还是强制模式?HSTS的max-age一般设多少?