Nginx中启用includeSubDomains强制所有子域HTTPS,核心是确保每个子域均部署有效HTTPS服务并配置含always参数的完整HSTS头,否则任一子域证书错误或缺失将导致主站被浏览器标记为不安全。

要在 Nginx 中通过 includeSubDomains 实现所有子域名强制 HTTPS,核心不是“加个参数就完事”,而是确保整个域名生态已具备 HTTPS 基础能力。这个参数一旦生效,浏览器会拒绝加载任何未 HTTPS 的子域名(如 api.example.com、cdn.example.com、shop.example.com),哪怕只有一处证书错误或响应异常,用户就可能打不开整个主站。
必须在 HTTPS server 块中配置,且带 always 参数
该头只能出现在监听 443 端口、启用 SSL 的 server 块里,不能写在 http 块、location 块或 HTTP(80 端口)块中:
- 错误示例:
add_header放在 80 端口 server 或全局 http 块 → 浏览器直接忽略 - 正确写法(关键含
always):add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; -
always不可省略:确保 404、500、304 等非 2xx 响应也携带 HSTS 头,避免策略中断
启用前必须逐个验证所有子域名 HTTPS 可用
includeSubDomains 是硬性约束,不是可选功能。上线前需确认以下每项都满足:
- 每个子域名(包括
www、api、static、test等)都配置了独立的 HTTPS server 块,且监听 443 端口 - 每个子域名的证书有效、域名匹配、无链路问题(可用
curl -I https://sub.example.com检查是否返回 200 且无SSL certificate problem) - 每个子域名的 HTTPS server 块中,同样添加了完整 HSTS 头(含
includeSubDomains和相同max-age) - 检查 Nginx 错误日志,搜索
ssl_handshake_failed、no suitable certificate等关键词
preload 不是必须,但加了就得长期负责
如果目标是加入浏览器预加载列表(Chrome/Firefox/Safari 内置),preload 必须配合 includeSubDomains 和 ≥1 年 max-age,且全站不能有任何 HTTP 回退路径。但注意:
- 提交到 hstspreload.org 后,审核通过即写入浏览器发布版,移除需数月周期
- 若子域尚未全部 HTTPS(比如测试环境仍用 HTTP),先去掉
preload,仅保留includeSubDomains+ 短期 max-age(如 300 秒)灰度验证 - 灰度期间用不同设备访问各子域名,确认无混合内容警告、无证书告警、无跳转失败
别忘了 HTTP 重定向也要全覆盖
HSTS 解决的是“首次之后”,而 HTTP → HTTPS 跳转解决的是“第一次”。二者必须协同:
- 80 端口 server 块中,对所有已知子域名做精准
return 301,例如:server_name example.com www.example.com api.example.com cdn.example.com; - 避免用通配符
server_name _或模糊匹配,否则未列明的子域名可能走 HTTP 且不跳转,破坏 HSTS 信任链 - ACME 验证路径(
/.well-known/acme-challenge/)需单独放行,防止 Let’s Encrypt 续签失败











