hsts预加载失败主因是域名不满足硬性条件,需依hstspreload.org提示逐项验证响应头、证书链、重定向及子域https覆盖,确保strict-transport-security含max-age=31536000; includesubdomains; preload,且全站无http暴露。

排查 HSTS 预加载(hstspreload.org)提交流程中的报错,核心不是查 Nginx 日志,而是验证域名是否满足预加载的硬性条件——浏览器在收录前会严格校验服务端响应、证书链、重定向逻辑和子域覆盖等,任何一项不达标都会被拒绝并返回具体错误。
一、先确认提交页面给出的明确拒绝原因
访问 https://www.php.cn/link/b9fcbc854cc45e5802618e283d3fe525/?domain=your-domain.com,输入域名后点击 Submit。若被拒,页面顶部会直接显示红色错误信息,例如:
- “No HSTS header”:Nginx 未在 HTTPS 响应中返回 Strict-Transport-Security 头,或 max-age
- “Invalid includeSubDomains directive”:头中写了 includeSubDomains,但子域名(如 www、api)未全部部署 HTTPS 或未返回相同 HSTS 头
-
“Invalid preload directive”:Strict-Transport-Security 头里缺少 preload 参数,必须是
max-age=31536000; includeSubDomains; preload - “Redirects to HTTP”:根域名(如 example.com)返回了 301/302 到 http://,或存在任何 HTTP 跳转链
- “Certificate chain issues”:SSL 证书链不完整(中间证书缺失),导致部分客户端无法建立可信连接
二、逐项验证服务端实际响应是否合规
别依赖本地测试,要用真实公网请求模拟浏览器预加载检查器的行为:
- 用
curl -I https://your-domain.com确认响应头含完整 HSTS:Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
注意:不能有多个 add_header 指令覆盖,也不能出现在 if 块中(Nginx 会忽略) - 用
openssl s_client -connect your-domain.com:443 -servername your-domain.com -showcerts 2>/dev/null | grep "subject="验证证书链至少包含两段:你的域名证书 + 中间 CA(如 Let's Encrypt R3),且不含根证书 - 用
curl -I http://your-domain.com和curl -I http://www.your-domain.com确保全部 301 跳转到 HTTPS 版本,且跳转链中无任何中间 HTTP 地址 - 对所有声明的子域(如 www、mail、api)单独执行上述 curl 和 openssl 检查,确保全部返回相同 HSTS 头、有效证书、无 HTTP 暴露
三、检查 Nginx 配置常见陷阱
即使逻辑正确,配置细节也可能导致预加载失败:
-
listen 指令漏写 ssl 参数:443 端口 server 块必须是
listen 443 ssl http2;,否则 HTTPS 响应不生效 - HSTS 头未全局生效:如果只在某个 location 块里加 add_header,根路径 / 可能不返回该头;应放在 server 或 http 块顶层
-
HTTP server 块未做 301 强制跳转:必须有独立的
server { listen 80; server_name ...; return 301 https://$host$request_uri; },且不能用 rewrite - 使用了 CDN 或反向代理但未透传协议:若前端有 Cloudflare、Nginx Proxy Manager 等,需确保它们把原始 HTTPS 协议和 HSTS 头原样透传给后端
四、提交后验证是否真正进入预加载列表
提交成功只是第一步,实际收录需数周。验证方式:
- 再次访问 hstspreload.org 查询页,状态变为 “Pending submission” 或 “In preload list”
- 在 Chrome 地址栏输入
chrome://net-internals/#hsts→ Query domain,若返回 Found 且 max-age 显示极大值(如 31536000),说明已本地缓存;但注意:这仅表示浏览器记住了,不代表已进官方列表 - 最权威验证:下载最新 Chromium 源码,查看 transport_security_state_static.json 文件中是否已包含你的域名











