安全响应头加了但有些响应里没有,状态码条件和层级继承怎么查
为什么加了安全响应头,有些响应里却没有带上?多数情况不是没生效,而是这两件事之一:那个响应的状态码不在加头指令的生效范围内,或者定义被写在另一个配置层级里、上层整组没有继承下来。排查要按缺头的那个具体响应去复现,而不是按正常页面检查。
先确认缺头的是哪一类响应
最常见的报告是「首页检查全绿,报错页面却缺头」。原因在文档里写得很明确:加头指令只在响应码等于 200、201、204、206、301、302、303、304、307 或 308 时才把字段加进响应。四百段与五百段的返回不在这张表里,包括常见的禁止访问、内部错误,以及静态资源找不到时的返回。也就是说,检查工具打在正常页面上的通过结论,代表不了异常路径。
生效条件怎么绕过
同一份文档给出解法:加 always 参数,此时不论响应码为何都会添加该字段。这个参数从 1.7.5 版本起提供。取舍要讲清楚——带 always 会让所有返回都带上这组头,包括那些由错误页或调试入口生成的响应,好处是覆盖完整,代价是一旦某条头写错,影响面也覆盖到全部响应。安全类头通常希望无例外生效,因此这个参数在多数场景值得用。
| 现象 | 大概率原因 | 怎么验证 | 常见改法 |
|---|---|---|---|
| 正常页面有、错误页没有 | 状态码不在生效列表 | 手工请求一个不存在的地址看响应 | 加 always 参数 |
| 上层写了、某个子块里全部消失 | 同层定义了加头指令,上层整组不继承 | 逐层比对配置里该指令出现的位置 | 在子块重写整组,或用继承参数 |
| 本地复现全有、线上一部分没有 | 前置缓存或压缩层改写了响应 | 绕过缓存带随机参数请求 | 在生成该响应的那一层补 |
| 跳转响应里缺头 | 三百段中只有列表内的码生效 | 请求一个已知会跳转的旧地址 | 把跳转改成内部重写,或在目标页补 |
层级继承是最容易踩的一条
文档里的这条规则值得逐字读:这些指令当且仅当当前层级没有定义任何加头指令时,才从上一层级继承。它不是合并,而是整组替换。典型事故是:在站点主配置写好一组安全头,后来在某个子路径块里为了加一条缓存相关的头写了一行加头指令,结果上层那一整组在这个子路径下全部消失。检查时不要只问「这个子块写了什么」,要问「这一层有没有写过」——只要写过一次,上层就整组失效。较新的版本提供了重定义继承规则的指令,名为 add_header_inherit,出现在 1.29.3 版本,可以让下层在上层定义的基础上追加而不是替换。
定位是哪一层生成的
响应头缺失的另一个来源是:这个响应根本不是站点程序返回的。反向代理、缓存层、负载均衡在站点之前就能独立产出返回,比如缓存命中的响应、代理超时产生的错误页。区分办法是看响应里带没带程序自己会写的那些字段,再绕过前置层直接请求站点服务端口复现一次。AnQiCMS 默认监听 8001 端口,部署时通常由前置服务器反代到它,两边都各自有配置层的响应头设置,逐层对比比整体重启更快找到差异。
上线后的复核动作
改完之后建议按四条路径各取一次响应:正常页面、跳转地址、不存在的地址、需要鉴权的入口。这四条覆盖了生效列表内与外的两类状态码,也覆盖了继承规则会被触发的位置。把每次响应的头字段记下来对比,比在检查网站上重复跑一次分数的信息量大得多——检查站点通常只测首页。
常见问题
问:只在首页检查通过,可以认为配置没问题吗? 答:不能。首页是二百段成功响应,正好落在生效列表里,异常路径的表现看不到。
问:子配置里只写一条头,为什么会带走一组头? 答:因为同层一旦定义了加头指令,上层定义就不再继承,替换是整组发生的。
问:改了配置为什么线上仍然缺头? 答:先确认这个响应是不是由缓存或代理层直接返回,那一层的配置需要单独补。