PHP中设置HSTS头须在HTTPS下且响应未输出前调用header(),推荐置于入口文件顶部或中间件;正确写法为header('Strict-Transport-Security: max-age=31536000; includeSubDomains; preload');Nginx/Apache配置更可靠,因在PHP执行前注入且覆盖所有响应。

PHP中设置HSTS头的正确方式
直接在PHP脚本里用 header() 发送 Strict-Transport-Security 是可行的,但必须满足两个前提:HTTPS已生效、且响应尚未输出。否则会触发 Warning: Cannot modify header information 错误。
常见错误是把 header('Strict-Transport-Security: ...') 放在 echo 或 HTML 输出之后,或者放在 session_start() 之后却没控制好输出缓冲。
- 必须在任何输出(包括空格、BOM、
print、var_dump)前调用header() - 推荐统一放在入口文件(如
index.php)顶部,或框架的全局中间件/前置钩子中 - 若使用输出缓冲,确保
ob_start()在最开始,且未被意外清空
header() 中 HSTS 参数的关键取值含义
HSTS头不是“开了就行”,max-age、includeSubDomains、preload 各自影响浏览器行为和实际安全性,配置不当反而可能引发问题。
-
max-age=31536000表示一年,这是主流推荐值;设为0可主动撤销策略(需用户访问一次HTTPS才生效) -
includeSubDomains会让所有子域名强制走HTTPS,但前提是所有子域都已部署有效证书,否则会导致子域完全无法访问 -
preload不是HTTP头标准字段,而是提交给浏览器厂商预加载列表的标记;单独加它无效,必须配合足够长的max-age和includeSubDomains,且后续去 hstspreload.org 提交
典型安全写法:header('Strict-Transport-Security: max-age=31536000; includeSubDomains; preload');
立即学习“PHP免费学习笔记(深入)”;
为什么Nginx/Apache配置比PHP更可靠
PHP层加HSTS本质是“补救”——只要PHP执行了,就说明请求已经进来了,而攻击者可能在连接建立初期劫持。Web服务器层(如Nginx)在SSL握手完成后、转发给PHP前就注入头,更早、更稳、不依赖PHP逻辑。
- Nginx配置示例:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;,注意always参数,否则重定向状态码(如301)下该头不会发送 - Apache需启用
headers_module,再在虚拟主机配置中写:Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" - PHP方式无法覆盖404、500等错误页的响应头,而服务器配置可以统一生效
验证HSTS是否生效及常见失效原因
浏览器开发者工具的 Network → Response Headers 里找不到 Strict-Transport-Security,不代表没配——它只在HTTPS响应中生效,HTTP响应里绝不会出现;且首次访问时不会立即生效,需要第二次访问才被浏览器记住。
- 用
curl -I https://yoursite.com检查响应头,确认有该字段且值符合预期 - Chrome访问
chrome://net-internals/#hsts可查询当前域名是否已被记录、max-age剩余时间 - 如果用了CDN(如Cloudflare),要确认CDN没有剥离或覆盖该头;部分CDN需在控制台显式开启HSTS开关
- 本地开发用
localhost测试时,HSTS默认不生效(Chrome/Firefox对 localhost 有特殊豁免)
真正容易被忽略的是:HSTS一旦生效,即使你后来删掉配置,用户浏览器仍会按原 max-age 时间强制跳转HTTPS——改错配置的成本远高于设对配置。



















