直接在宝塔nginx的server块添加add_header strict-transport-security可能不生效,因其不继承父块配置,且子location块(如php、反向代理等)中若未重复设置或覆盖,会导致hsts头丢失;必须在所有location块内添加或使用always参数(nginx≥1.7.5)。

为什么直接在宝塔 Nginx 配置里加 add_header Strict-Transport-Security 可能不生效
因为 Nginx 的 add_header 指令**不会继承父块配置**,如果只在 server 块里加,但请求命中了 location /api 这类子块,且该子块里没重复写 add_header,HSTS 头就丢了。更常见的是:宝塔自动生成的 PHP 或反向代理 location 块里自带 add_header,会覆盖外层设置——结果是首页有 HSTS,API 接口或静态资源响应里根本没有。
- 必须把
add_header Strict-Transport-Security放在所有可能匹配请求的location块内,或统一用always参数(Nginx 1.7.5+)避免被覆盖 - 宝塔「网站设置 → 配置文件」里默认只有顶层
server块可编辑,手动加的头对子location无效 - 若站点启用了「强制 HTTPS」,也要确认 301 跳转响应本身是否带 HSTS——它不会自动继承,需单独在跳转配置里补上
在宝塔中安全启用 HSTS 的实操步骤
不要只改主配置文件,要覆盖所有出口路径。推荐两种方式,按实际 Nginx 版本选:
- 如果你的宝塔环境 Nginx ≥ 1.7.5(查法:
nginx -v),在「网站设置 → 配置文件」的server块末尾加这一行:add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
- 如果版本较老(如 CentOS 7 默认的 Nginx 1.12),必须逐个检查并修改
location块:找到所有location ~ \.php$、location /、location ^~ /static/等,每个里面都追加相同的add_header行(不含always) - 特别注意反向代理场景:如果用了「网站设置 → 反向代理」,宝塔会生成独立
location,需点击「配置」按钮,在弹出框里「发送响应头」区域手动填入 HSTS 值,而不是依赖主配置
HSTS 配置值里的三个关键参数不能乱设
max-age=31536000; includeSubDomains; preload 看似标准,但上线前得想清楚后果:
-
max-age=31536000(1 年)是常见值,但一旦浏览器收到,就会强制后续一年只走 HTTPS——如果中途证书过期、或你临时想降级 HTTP 调试,用户将无法访问,只能等时间过期或手动清浏览器 HSTS 缓存(Chrome 地址栏输chrome://net-internals/#hsts) -
includeSubDomains会让api.example.com、cdn.example.com全部强制 HTTPS,前提是这些子域也部署了有效证书;否则整个域名直接打不开 -
preload是加入浏览器预加载列表的前提,但提交前必须确保全站(含所有子域)100% 支持 HTTPS 且响应头完整,否则会被拒绝收录,且撤回极难
验证 HSTS 是否真正生效的快速方法
别只看 curl 主页响应头,要测真实路径:
- 用
curl -I https://yourdomain.com确认主页面返回了Strict-Transport-Security头 - 再测一个 PHP 接口:
curl -I https://yourdomain.com/api/test.php,看有没有同款头——这步漏掉,90% 的“已配置”都是假象 - 打开 Chrome 开发者工具 → Network 标签 → 刷新页面 → 点任意 HTTPS 请求 → Headers → Response Headers,搜索
strict-transport-security,确认存在且值正确 - 如果用了 CDN(如 Cloudflare),记得在 CDN 后台也开启 HSTS,否则用户看到的是 CDN 返回的响应头,不是你 Nginx 的
最麻烦的点在于:HSTS 生效后无法快速回滚,所有配置必须提前在测试环境跑通全部子路径和子域,生产环境第一次加建议先用 max-age=300(5 分钟)观察一天,没问题再调高。











