广告位 · 页头(后台-模块管理-新建模块后替换此区域)

如何在CDN中正确配置HSTS头部并提交预加载列表?

如今大部分网站都部署了HTTPS,但单纯安装证书并不能完全避免明文HTTP请求。用户直接在地址栏输入域名时,浏览器默认会先访问80端口,即使服务器做了301跳转,第一次连接仍然是明文,存在被劫持的风险。HSTS(HTTP Strict Transport Security)正是一套用来告诉浏览器只允许通过HTTPS访问当前域名的机制。将HSTS响应头放在CDN边缘节点统一配置,既能减少源站改造,也能让所有命中缓存的响应都带上安全策略。本文从头部字段的实际含义出发,梳理在CDN中添加HSTS的配置路径,再说明提交浏览器预加载列表的条件与撤销难度,帮助你在安全性和灵活性之间做好平衡。

如何在CDN中正确配置HSTS头部并提交预加载列表?

一、HSTS头部字段与生效条件

HSTS的核心响应头字段名是 Strict-Transport-Security,典型值如下:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

三个指令中,max-age 是必填项,单位是秒,表示浏览器在收到响应后的多长时间内自动把该域名的HTTP请求升级为HTTPS。includeSubDomains 是可选项,表示策略同时作用于当前域名及其子域名。preload 用于声明域名希望进入HSTS预加载列表,但不是加上它就自动生效,必须通过 hstspreload.org 提交并等待浏览器更新。

理解HSTS的生效条件非常重要。浏览器只会认可通过HTTPS连接返回的 Strict-Transport-Security 响应头。如果该头出现在HTTP响应中,浏览器会直接忽略,这是为了避免攻击者在第一次明文连接中注入假的HSTS策略。所以配置HSTS前,必须保证域名已经具备可用的证书,并且HTTPS访问正常,否则一旦策略生效用户可能无法访问。CDN层如果存在HTTP到HTTPS的重定向,重定向之前的HTTP响应不应携带HSTS,因为它也不会被处理。

在CDN配置HSTS时,要避免源站和CDN同时输出多个相同或冲突的响应头。浏览器处理多个Strict-Transport-Security头部时,标准没有定义合并规则,部分浏览器采用第一个,部分采用最后一个,这会造成策略不一致。实际建议只在最外层网关统一添加,例如CDN节点或负载均衡器,源站尽量不再重复设置。若必须多级设置,至少保证每一层的值完全一致,且开启回源HTTPS,避免边缘节点通过明文HTTP回源。

二、CDN与源站中的配置示例

主流CDN控制台都支持添加自定义响应头。以云厂商的对象存储或CDN加速域名为例,可以在域名管理的HTTP头设置中新增一组响应头,名称填 Strict-Transport-Security,值填 max-age=31536000; includeSubDomains。如果只是内部测试,建议先设置 max-age=604800 观察一周,确认所有HTTPS页面、接口、子域都没有问题后再调整到一年。比较常见的错误是把HSTS写在HTML的 <meta http-equiv> 标签里,这种方式不会创建HSTS策略,因为规范只认可HTTP响应头。

如果源站使用Nginx,可在443端口对应的server块中添加:

server {
    listen 443 ssl http2;
    server_name www.ipipp.com;
    ssl_certificate     /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

这里的 always 参数表示在错误响应、重定向响应中同样返回该头部,避免部分浏览器只在200响应上看到策略。若要启用预加载,可将值改为 max-age=31536000; includeSubDomains; preload。Apache环境的等价配置为:

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

注意Apache需要启用mod_headers模块。CDN边缘配置与源站配置最好只保留一处。若CDN已添加HSTS,源站就不要通过add_header再输出,否则可能出现两个头。对于多级CDN或自建Nginx作为边缘节点的情况,可以在 location 块中处理,但要记住 add_header 会继承,处理不好可能重复定义。另一个建议是HTTPS回源:在CDN控制台把回源协议设置为HTTPS,而不是先HTTP再跳转,这样源站到CDN这一段也能保持加密。对于图片、字体、API等子域,只要 includeSubDomains 开启,就必须保证所有子域都配置了有效证书。

三、提交HSTS预加载列表的完整流程

HSTS策略依赖浏览器至少成功访问过一次HTTPS站点。第一次访问仍然可能通过HTTP发起,因此不能抵御初次访问的降级攻击。为了解决这个窗口期,Chrome、Firefox、Edge等浏览器维护了一份HSTS预加载列表,将某些域名硬编码在浏览器内,所有请求从第一次开始就强制走HTTPS。预加载列表由 hstspreload.org 统一维护,提交域名需要同时满足以下条件:主域和所有子域均提供有效HTTPS;重定向工作正常;HSTS头包含 max-age=31536000、includeSubDomains 和 preload 三个指令;证书不能使用过期或不受信任的链。

加入预加载列表前,建议先完成以下动作:用 curl 检查主域和几个子域是否都返回正常的HSTS头;检查所有页面和接口在HTTPS下是否存在混合内容;确认没有业务依赖明文HTTP内部跳转。测试命令如下:

curl -I https://www.ipipp.com | grep -i strict

如果输出中能看到 strict-transport-security: max-age=31536000; includeSubDomains; preload,说明响应头返回正常。之后打开 hstspreload.org,输入域名,系统会自动扫描是否满足条件,满足后提交即可。浏览器更新预加载列表不是实时的,可能需要几周到数月时间。

预加载列表的撤销成本很高,必须谨慎。一旦域名进入列表,即使用户从未访问过你的网站,浏览器也会强制使用HTTPS,无法通过删除响应头或缩短max-age立即解除。需要从列表中移除时,仍要在HTTPS响应中返回 max-age=0 的Strict-Transport-Security头,再到 hstspreload.org 提交移除申请,等待浏览器版本更新。这个过程可能持续数月,期间如果某个子域证书过期或未配置HTTPS,用户将无法访问。因此,预加载适用于已经全面HTTPS化且长期不会回退的业务,对临时性、内部系统、部分子域不明确的网站不建议开启。

四、常见配置错误与排查思路

第一个典型错误是希望通过HTML标签代替HTTP响应头。例如下面这段代码:

<meta http-equiv="Strict-Transport-Security" content="max-age=31536000">

它看起来很像安全头部,但浏览器不会据此建立HSTS策略。正确做法一定是通过服务器或CDN返回 Strict-Transport-Security 响应头。如果在CDN缓存内容中夹杂了这个meta标签,客户端也只会把它当作普通元数据忽略。

第二个常见错误是max-age设置过长后证书维护跟不上。证书到期后如果忘记续期,而HSTS策略仍在有效期内,用户浏览器会强制升级到HTTPS,但证书已失效,结果是访问完全中断。解决办法是配置自动续期、及时告警,并且在测试阶段使用较短max-age。部署初期如果对子域HTTPS覆盖没有十足把握,不要加includeSubDomains,等主域稳定后再逐步扩展。

第三个错误是响应头重复或位置不当。例如CDN和源站各自返回一个不同max-age的HSTS头,浏览器行为不可控。可以用浏览器开发者工具查看单个URL的响应头,如果出现两条 Strict-Transport-Security,则需要删除源站或CDN其中一处。对于Nginx的add_header,需要理解它的继承规则,在server块和location块同时定义时,location会覆盖server的add_header,而不是追加,这种机制容易导致头部丢失。排查时优先使用 curl -I 查看最终边缘响应,因为浏览器可能缓存旧的HSTS策略,干扰判断。

HSTS是HTTPS部署中的基础安全策略,但配置重点并不在于单纯添加一行响应头,而是要理解max-age、includeSubDomains、preload三者之间的关系,以及CDN缓存和回源协议如何影响策略下发。把测试周期、证书监控、子域覆盖这些动作提前做好,再考虑是否提交预加载列表,才能在获得安全收益的同时避免把自己锁死在HTTPS上无法回退。

广告位 · 文章底部(后台-模块管理替换此区域)