HTTP响应头有248个死字段,多数不是你写的
本文目录
- 响应头里那些没有接收端的字段,你还在发吗?
- 配错和过期,是两种不同的问题
- 接收端消失有三种形态
- 这批数据对一个中小站主意味着什么
- 同样的结构在别的地方也有
- 为什么这件事值得单独量一遍
- 142个站的响应头,是怎么取的?
- 请求方式与筛选条件
- 211个域名里,为什么只有142个进了统计
- 为什么不直接用在线检测工具的结论
- 响应头里最大的一块,其实是Cookie
- 只看最后一跳,不看中间跳
- 抓取和存档是怎么做的
- 字段名一共233种,头部长度分布很宽
- 浏览器七年前移除的那个安全头,为什么还有六成站在发?
- 90个站在发X-XSS-Protection
- 它对应的浏览器功能,2019年就开始被移除
- 那两个发0的站,情况最有意思
- 那个只发1不带阻止模式的站
- 覆盖率相等,不等于质量相等
- 那还该发什么
- 那三个头为什么总是一起出现?
- 要么三个都有,要么三个都没有
- 用共现关系判断字段来源,是个通用方法
- 它们来自同一个平台
- 自建的站为什么头更干净
- 再看零头那50个站,构成完全不同
- 这对自查意味着什么
- 给一个已经停运的插件发跨域策略,还有意义吗?
- 这个头本来是给谁看的
- 顺带说一个反面情况:确实还需要它的站
- 主要接收端在2020年底停运
- 取值几乎都一样
- X-Download-Options这个头,是写给谁看的?
- 只有一个浏览器读过它
- 拿它跟连接安全那个头比一下
- 那个浏览器已经在2022年退役
- 这批头当年为什么会被一起推荐
- 同样是被新头取代,为什么这一个反而该留
- 它和另一个同源头的区别
- 报告链断在中间:64个站的错误报告,发得出去吗?
- 64个站配了网络错误上报
- 但上报地址靠的是一个已经进入弃用状态的头
- 新版写法长什么样
- 这不等于报告一定发不出去,但状态很脆弱
- 这套机制能看到的东西,日志里确实看不到
- 更容易被忽略的是没人看上报数据
- 剩下那几个死头,各还剩多少个站在发?
- 四个字段的实测数量
- 四个字段各自的时间线
- Pragma这一行为什么还有19个站在发
- 那唯一一个Expires单独有效的站
- 顺带看一个相关的三头组合
- 这几个字段在别的场景里还有活的吗
- P3P那两个站,值一模一样
- Feature-Policy一个都不剩,这说明什么?
- 零的两种可能,得分清
- 继任者只有6个站在用
- 新头没人配,原因不是不知道
- 那什么情况下它才会被推动
- 这个对照才是本文最该记住的数字
- 那这类新头到底该不该配
- 覆盖率高低,基本等于有没有人做成默认
- 这些多余的字段,代价到底在哪里?
- 字节层面:中位数102字节,占响应头的1.74%
- 那真正的代价是什么
- 什么时候值得主动去查一遍
- 审计规则那一层,怎么改才有效
- 什么时候它会变成真问题
- 怎么判断一个响应头今天还有没有接收端?
- 三个可查的信源,按可靠度排序
- 一个更快的反向判据
- 本文所有字段的判断结果汇总
- 把判断做成一段能重复跑的检查
- 遇到查不到的私有字段,怎么处理
- 这张表本身也会过期
- 清理的时候,该按什么顺序动手?
- 第一步:先分层,弄清每个字段是谁加的
- 第二步:先补,再删
- 第三步:删除按确定性分批
- 平台加的那些,能做什么
- 这件事多久做一次合适
- 一次真实的清理是什么样的
- 一份可以直接用的检查清单
- 回到最初那个问题
- 常见问题解答
- 权威参考资料
摘要:142个海外品牌独立站的响应头逐字段拆开,100个站(70.4%)至少发着一个没有接收端的字段,合计248个。最普遍的是X-XSS-Protection,90个站在发,而浏览器2019年就把对应功能移除了。分布更说明问题:这类头里有三个,65个站同时发全部三个,50个站三个都没有,只有3个站发了其中两个——不是一条条加的,是一个包一起来的。而那65个站里62个带着同一个电商平台的特征头。
HTTP响应头是个很奇怪的地方:它是全站每一次请求都会带上的东西,却几乎没有人定期看一眼。
原因不难理解。它不像页面内容那样有人反馈,不像状态码那样会报警,也不像加载速度那样有工具打分。它只是安静地跟着每个响应发出去,几十个字段,几百个字节,一天几十万次。
这次把142个海外品牌独立站的响应头全抓下来逐字段拆开,想看的是一件很具体的事:这些字段里,有多少还有人在读。
响应头里那些没有接收端的字段,你还在发吗?
先把这个问题的性质说清楚,它跟常说的响应头配置错误不是一回事。
服务器这一层的配置项该逐条核,影响SEO的20项服务器配置清单可以照着走。
这一层能做的事比想象中多,X-Robots、缓存和Vary几个头的实际机制值得先整体过一遍。
配错和过期,是两种不同的问题
配错是指值写得不对,比如缓存时间设成了负数、跨域来源写了通配符结果被浏览器拒绝。这类问题有反馈——浏览器控制台会报错,或者行为明显不对。
配错了通常立刻能看出来,伪静态和跳转的现代写法里那些错误都会直接表现出来。
配错的后果有时相当直接,照着加固清单加的一行响应头把地图和支付按钮变成了摆设就是真实案例。
过期是另一种:字段名和值都完全正确,语法无懈可击,只是读它的那个客户端已经不存在了。这类没有任何反馈。浏览器收到一个自己不认识的响应头,会直接忽略它,不报错也不提示——这是规范要求的行为,不然任何一家服务器加一个自定义头,所有浏览器都会集体报错。
所以过期的字段可以在响应里待很多年,每一次请求都发一遍,谁都不会说什么。
接收端消失有三种形态
把这批数据摊开之后,接收端消失的方式其实分得很清楚。
头部那些自动输出的东西也值得清,去掉静态文件版本号的四种做法是同一类整理。
服务退役不会有人通知你,某个缓存服务下线之后还能怎么查快照是同一类处境。
第一种是功能被移除。字段还在规范文档里有记载,但浏览器把对应的实现删掉了。这类最彻底——现在发它和发一个随手编的字段名,效果完全一样。
第二种是接收端产品退役。这个头本来是给某个特定软件看的,那个软件已经停止运营。字段本身没被谁废弃,只是它的读者不在了。
第三种是规范换代。旧字段被新字段取代,旧的进入弃用状态,新的成为标准写法。这一种最微妙,因为旧字段往往还能被部分实现识别,处在一个靠向后兼容维持的中间状态。
这批数据对一个中小站主意味着什么
先回答一个很实际的问题:这些百分比跟一个只有几百个页面的独立站有什么关系。
这类问题集中在几个固定位置,电商站十二类高频坑的诊断修复可以照单排查。
这类失分往往在第一年就埋下了,建站第一年最容易失分的十二项配置是同一批经验的汇总。
关系在于这批样本正好就是这类站。142个站全是海外品牌的独立站,多数用现成的电商平台建站,团队规模从几个人到几十人。也就是说,本文量出来的不是大厂的配置水平,是这个行业的普遍状态。
更具体一点:如果你用的是主流电商平台建的店,那么你的响应头里几乎肯定有本文说的那三个字段——65个三头全有的站里62个是同一个平台。你不需要去查也能猜到结果,因为那不是你配的。
而如果你是自建站或者用静态托管,情况就完全不同:本文那50个一个死头都没有的站基本都属于这一类。这时候响应头里有什么全看你自己配了什么,需要真的去查一遍。
同样的结构在别的地方也有
先把这个问题的形状抽出来,因为它不只出现在响应头里。
页面头部同样堆着没人用的东西,那几行预解析和表情代码怎么去掉是最常见的一例。
结构化数据同样存在写了没人读的情况,这些标记对AI搜索到底有没有用里有官方说法和实测。
响应头之外页面头部也在堆东西,关掉那几个默认加载的头部输出是同一类瘦身。
凡是符合两个条件的配置,都会长成这样:一是你写下的内容由别人的实现来解释,二是对方不认识的时候会静默忽略而不是报错。满足这两条,配置就会只增不减地积累。
符合这个描述的地方不少。域名的解析记录里可以留着指向早已停用服务的条目;邮件的发信策略记录里可以留着几年前用过的第三方服务商;Cookie上的属性各家浏览器支持程度不同,写了不支持的属性不会有任何提示;页面里的结构化数据可以标注一堆没有任何搜索引擎会消费的字段。
这些场景共享同一套判断办法,也共享同一个陷阱:因为没有报错,所以没有人知道该去看它。本文用响应头做样本,是因为它最容易批量取到,量出来的比例也最有代表性。
为什么这件事值得单独量一遍
可能会有人觉得这不算问题:多发几个字节而已,又不影响功能。
真正的加固在权限这一层,文件与目录权限的完整实战比多发几个头有用。
交付标准写清楚能省很多争论,达标定义与违约责任怎么写有几个真实纠纷案例。
这类核查最好放进整体框架,从抓取到AI可见度的完整诊断框架可以拿来当清单。
直接影响确实很小,这一点后面会专门算。真正的问题在别处:安全审计工具会把这些头当成加固措施打勾,检查清单会把它们列成必配项,而实际上它们什么都没做。这就形成了一份看起来很扎实、实际上有窟窿的防护记录。
更麻烦的是它掩盖了真正该配的东西。本文后面会看到一个对照:那些浏览器还在读的现代安全头,覆盖率只有个位数百分比;而这些已经没人读的老头,覆盖率超过六成。
142个站的响应头,是怎么取的?
先说样本和口径,因为响应头这个东西取法不同结果差别很大。
取法不同结论会反过来,用HEAD查说没配缓存头、换成GET那五个头全在就是这么来的。
请求方式与筛选条件
抓取对象是211个海外品牌独立站的首页,用普通浏览器的标识发GET请求,跟随重定向,记录完整的响应头。
同一批站上还看过移动端表现,响应式到核心指标的三类站改造对比可以对照。
同一批站上还量过响应速度,多层缓存怎么同时影响体验指标与抓取是配套的一篇。
为什么用GET而不是只取头部:因为有些服务端对不同请求方法返回的头并不一致,只查头部会漏掉实际存在的字段。这一点在做这类测量时经常出错,代价是得出一个反向的结论。
筛选条件是最终响应为2xx且有正文,符合的142个站进入统计。停在重定向的、返回4xx或5xx的都排除掉了——它们的响应头往往由边缘防护生成,不代表站点本身的配置。
211个域名里,为什么只有142个进了统计
把被排除的那69个交代清楚,因为分母决定了所有百分比。
被判成可疑之后还得能申诉,三大平台的申诉入口和文案可以备着。
被防护层挡住这件事很普遍,防火墙到底拦不拦得住爬虫实测出的答案挺意外。
排除的原因分三类。第一类是最终响应不是2xx:有些站对来自服务器机房的请求直接返回验证挑战或者拒绝,回来的是403、429这类状态码,而这些响应的头往往由边缘防护生成,反映的是防护配置而不是站点配置。第二类是请求根本没完成:连接超时、域名解析失败。第三类是停在重定向上没有走到终点。
这69个站被排除不代表它们的响应头有问题,只代表这次没取到可用的样本。所以本文所有比例的准确表述是:在能取到正常响应的142个站里,某个字段占多少。
顺带说一句,这种测量被防护层挡住的情况在这类工作里非常普遍,比例通常在两三成。做类似测量时最好一开始就预留这个损耗,别指望样本能全部覆盖。
为什么不直接用在线检测工具的结论
响应头这件事有大量现成的在线检测工具,输入域名就出一份评分报告。既然如此,为什么要自己抓。
外部工具的结论都该先校准,第三方数据准不准的六步校准法能挡掉大部分噪声。
两个原因。第一是评分规则本身可能过期——本文后面会看到,不少工具的规则里还留着已经失效的头,发了加分,没发提示缺失。用一个过期的尺子量,结果自然是错的。
第二是它们通常只给结论不给原始数据。而本文要回答的问题需要原始数据:这个字段是谁加的、和哪些字段一起出现、取值是什么。这些信息在评分报告里没有。
所以顺序应该是先自己取一份完整的响应头存下来,再用工具做辅助判断。反过来做,你拿到的是别人的结论,而那个结论的判据你看不见。
响应头里最大的一块,其实是Cookie
顺带说一个跟本文主题相关但方向不同的观察。142个站的响应头里,Set-Cookie合计568行,是所有字段里行数最多的,平均每个站四行,有的站一次发几十行。
要减字节先看大头,免插件压缩HTML与压缩叠加给了顺序。
Cookie堆多了还会直接报错,老用户打不开的那个页面,工具全都返回200是典型盲区。
Cookie的字节代价在请求侧更明显,每个请求都把同一份Cookie重发一遍算过一笔账。
这跟死头是同一类问题的另一种形态:Cookie也只有加入的通道,没有退出的通道。一个第三方服务接进来会写几个Cookie,服务停用之后写Cookie的代码往往还留着,或者标签管理里的那个配置没人敢删。
差别在于代价的量级完全不同。死头一共占响应头字节的1.74%,而Cookie往往占一半以上——它不但每次响应都要发下去,浏览器还会在后续每个请求里把它们再带回来。所以如果目标是减字节,该看的是Cookie而不是那几个老头。
本文不展开这一块,只是提醒一句:查响应头的时候顺手数一下Cookie行数,收益可能比清理死头大得多。
只看最后一跳,不看中间跳
还有个细节要说明:一次请求经过重定向,会产生好几组响应头,本文只取最后那一组。
跳转链本身也常出问题,不同工具报的死链从来不是同一批解释了差异来源。
中间跳的状态码本身也有讲究,301、302、404和410分别该在什么场合用是一张速查表。
这个选择是有取舍的。中间跳的响应头有时也有意义——比如某个跳转是由边缘层生成的,它带的头能说明边缘层配了什么。但要判断站点本身发什么,最后一跳才是对的对象。
抓取和存档是怎么做的
把方法说完整,便于自己复现。
看原始数据免不了跟各种表示法打交道,二进制到十六进制与权限位一次理清是常备工具。
原始数据留住才能改口径,把日志收成结构化可检索的形式是同一个思路。
抓取用的是普通命令行工具,每个域名发一次请求,跟随重定向,把完整的响应头连同状态行一起存成一个文本文件。一个域名一个文件,文件名用域名,这样后续可以随时回到原始数据重新统计。
这一步的关键是保留全部原始响应,不要在抓取阶段做任何筛选或者提取。本文中途改过好几次统计口径——只取最后一跳、剔除非2xx、区分平台加的和自己加的,每次改动都是拿同一批原始文件重跑。如果抓取的时候就只存了自己当时关心的几个字段,后面每次改口径都要重新抓一遍。
这个习惯在这类测量里回报很高:原始数据一次抓够,口径可以改一百遍。
字段名一共233种,头部长度分布很宽
142个站的响应头里,出现过的字段名去重有233种。这个数字比预想的大得多,因为里面有大量各家平台的私有字段。
一台机器上跑多个站时头会更乱,子目录映射与等价配置讲了怎么隔开。
头部字段多了压缩层会吃力,同样是4096的表,两种协议差678字节量过这件事。
出现在最多站上的几个是:Set-Cookie(合计568行,一个站可以发几十行)、Vary(179行)、Content-Type、Content-Encoding、Strict-Transport-Security(126站)、Server(125站)、X-Content-Type-Options(110站)。
值得一提的是Server头:125个站发了它,其中87个的值就是一个云服务商的名字。这本身也是一条信息——超过六成的站,响应头的最后一站是同一家边缘网络。这个事实会在后面解释一个非常整齐的分布。
浏览器七年前移除的那个安全头,为什么还有六成站在发?
先看最普遍的那一个。
真正有效的加固在别的层,从版本隐藏、目录权限到访问控制是一份可执行的表。
90个站在发X-XSS-Protection
142个站里有90个(63.4%)在响应头里发X-XSS-Protection,取值分布是这样:
真正挡住执行的是权限设置,五种环境禁掉执行权限的写法是直接有效的一层。
真正的攻击流量要另一套手段,从识别到应急拦截讲的是量级更大的情况。
真正的加固要分层来,三层拦截手段的选型框架比多发几个头实在。
| 取值 | 站数 | 本意 |
|---|---|---|
| 1; mode=block | 85 | 检测到疑似脚本注入就阻止整页渲染 |
| 1;mode=block | 2 | 同上,只是少了个空格 |
| 1 | 1 | 检测到就尝试过滤 |
| 0 | 2 | 明确关闭这个功能 |
它对应的浏览器功能,2019年就开始被移除
这个头是当年为了配合浏览器内置的脚本注入检测机制而设计的。那个机制的想法是:浏览器观察请求参数里有没有出现在页面里被当成脚本执行的内容,发现疑似情况就拦下来。
各家服务器的写法都在换代,某种服务器上强制加密跳转的五步是现在的版本。
浏览器侧的规则一直在收紧,证书有效期正在砍向47天是最近的一例。
问题是这个机制本身产生了不少误判,还被发现可以被利用来做别的攻击。于是各家陆续把它拿掉了:主流浏览器在2019年下半年默认关闭并随后移除,另一家浏览器从头到尾就没有实现过它,苹果那边的实现也在之后被移除。
结果是2026年的今天,这个头对任何一个主流浏览器都不产生作用。它在文档里的状态是非标准且已弃用。
那两个发0的站,情况最有意思
90个站里有2个发的是0,意思是明确关闭这个功能。
关掉一个已经不存在的东西并不罕见,老代码里那个已被移除的函数是同一种情形。
这个动作在当年是有道理的——既然那套检测机制会误判、还可能被利用,那么明确关掉它比开着更安全,安全指南里也这么建议过。
但放到今天,这两个站是在明确关闭一个已经不存在的功能。发0和发1,效果完全相同,都是零。这大概是这批数据里最能说明问题的一个细节:连规避风险的那个动作,也已经失去了对象。
那个只发1不带阻止模式的站
取值分布里还有个细节:有1个站发的是不带阻止模式的写法。
取值停在哪一年往往说明流程,四类流程漏洞怎么修讲的是同一层问题。
这两种写法在当年是有区别的:不带阻止模式意味着浏览器检测到疑似注入时尝试改写页面内容把它去掉,带阻止模式则是直接不渲染整个页面。安全建议普遍推荐后者,因为改写页面本身也被发现能被利用。
所以这个站的写法在当年属于不够严格的那一档。而放到今天,严格和不严格的区别已经不存在了——两种写法的效果都是零。
这个细节值得留一眼,因为它说明这类配置的年代:这个站的响应头很可能是在那份安全建议广泛流传之前就配好的,之后一直没动过。响应头的取值有时候比字段名更能说明它有多久没被人看过。
覆盖率相等,不等于质量相等
刚才说内容安全策略的覆盖率是64.8%,和这个死头几乎一样。但这里得补一句,免得给人一种已经配好了的印象。
有没有配和配得好是两件事,模板和广告稀释主体内容的七个陷阱也是这个道理。
这个头的难点在于它的质量差异极大。同样是发了这个头,一个写得严格的策略确实能挡住脚本注入;而一个为了不影响业务而写成大范围放开的策略,防护效果接近于零。样本里有相当一部分策略里包含了极其宽松的来源声明,这在实践中很常见——因为收紧它需要逐个梳理页面上所有第三方脚本,而那是一件很费时间的事。
所以这两个数字虽然相等,含义完全不同:那63.4%是确定无效,这64.8%是效果取决于内容。前者可以一刀切地判断,后者必须逐个看。
这也提醒一件事:本文这类按字段名统计覆盖率的做法,只能回答有没有配,回答不了配得对不对。看到覆盖率高不要放心,那是两个层次的问题。
那还该发什么
脚本注入这件事本身当然还在,只是防护手段换了。今天真正起作用的是内容安全策略,也就是通过一份白名单明确规定页面能加载和执行哪些来源的脚本。
批量改配置或字段时要防注入,安全地批量改一批字段的做法能省掉风险。
安全这块要成体系,从审查到权限与提示注入防御给了一份完整清单。
这批样本里,92个站(64.8%)发了内容安全策略头,覆盖率跟X-XSS-Protection几乎一样。所以对多数站来说,这不是缺不缺手段的问题,只是那个老头留着没删。
那三个头为什么总是一起出现?
接着看这批数据里最整齐的一个分布,它直接解释了这些头是从哪来的。
默认配置互相叠加会出问题,插件与主题各输出一套标记怎么归一是同一种病。
要么三个都有,要么三个都没有
把X-XSS-Protection、X-Download-Options、X-Permitted-Cross-Domain-Policies这三个头放在一起统计每个站发了几个:
默认配置里一个字符就能决定行为,一个字符改对缓存判断是很小但很典型的例子。
规则该写在哪一层常被搞反,两种手段各管哪一段有清楚的分工。
分布形状能揪出测量问题,量出74%的差异、补上对照组后只剩2.2%就是靠这个发现的。
| 发了几个 | 站数 | 占比 |
|---|---|---|
| 三个都有 | 65 | 45.8% |
| 只有两个 | 3 | 2.1% |
| 只有一个 | 24 | 16.9% |
| 三个都没有 | 50 | 35.2% |
这是一个双峰分布:两端各占四成上下,中间几乎是空的。只有3个站发了其中两个。
自然写出来的配置不会长这样。如果这三个头是人一条条加的,应该看到各种组合——有人加了两个,有人只加一个最重要的,比例会平缓分布。双峰意味着它们是一次性一起出现的。
用共现关系判断字段来源,是个通用方法
这个双峰分布的发现过程本身有方法价值,值得单独说。
对比法在别处一样好用,对比爬虫和用户看到的页面也是靠两组结果相减。
判断一批配置是人写的还是自动生成的,最快的办法不是看内容,是看组合关系。人写的配置组合是发散的——每个人的判断不同,会出现各种搭配;自动生成的配置组合是收敛的,要么全有要么全无。
所以拿到一批样本,把几个相关字段的共现情况列出来,看分布形状:平缓分布说明是人在逐个决策,双峰分布说明有一个统一的来源。这个判据不需要知道任何平台的内部实现,只看数字。
这个方法在别处也好用。比如判断一批站的结构化数据是手写的还是插件生成的,判断一批页面的元信息是编辑填的还是模板套的,都可以用同样的共现分析。发散是人的痕迹,收敛是机器的痕迹。
它们来自同一个平台
顺着这条线索往下查,答案很干脆:那65个三个都有的站里,62个带着同一个电商平台的特征字段;Server头的值有64个是同一家云服务商。
平台还替你决定了别的事,从命名到压缩的完整清单里能看到哪些归自己管。
换成自建的电商程序又是另一套默认,索引器、缓存与调优讲透可以对照看。
平台给的东西该不该接管有判据,平台内置、通用工具与专属应用的三层框架能帮你划清边界。
换句话说,这三个头不是站主写的,是平台在响应链路上统一加的。站主既没有主动配它们,也没有办法单独去掉它们——那不在他的控制范围里。
这一点改变了整件事的性质。如果这些头是自己写的,清理就是打开配置文件删几行;而如果是平台加的,清理这个动作本身就不存在,你只能知道它们在那儿、知道它们没有作用,然后接受。
自建的站为什么头更干净
这个差异不是因为自建站的团队水平更高,原因更结构化。
自建站的加固要自己配齐,目录禁解析加文件权限是最基础的一项。
换架构时这一层要整体重搭,这套基建换了架构就得自己重做是提前的提醒。
平台建站的响应头是累积的:平台在某个年代加了一批,之后为了不影响存量店铺,一般不会去掉。任何一个新开的店,拿到的都是这份累积到今天的默认配置。
自建站的响应头是每次重新决定的:换一次服务器、迁一次架构、升级一次框架,配置文件往往会被重写或者重新审一遍。这个过程天然带着清理效果——重写的时候没人会主动去加一个陌生的老字段。
所以两边的差异是维护方式带来的,不是判断力带来的。这一点对判断自己的处境有用:如果你的响应头配置已经好几年没被重写过,它的内容大概停留在最后一次重写的那个年代。不管是自建还是平台,这个规律都成立。
再看零头那50个站,构成完全不同
作为对照,把三个头都没有的50个站的Server值列出来:一家云服务商15个、没有发Server头的11个、其余是各种自建或者其它平台,包括几家静态托管服务、几种自建服务器、以及个别自定义值。
边缘那一层会改写很多东西,六层缓存与边缘路由的实际影响值得一并了解。
这批站的共同点是响应头由自己或者自己选的技术栈决定,没有一个统一的平台在中间加东西。它们的头普遍更短,也更干净。
所以这批数据里的死头分布,本质上反映的是平台默认配置的年代,而不是站主的技术水平。用同一个平台的站,头都长得一样;自己搭的站,头各不相同但都更新一些。
这对自查意味着什么
实操上有个直接的推论:查响应头之前先弄清每个字段是谁加的。
中间还有代理层时要分清谁加的,反向代理的完整配置与对比讲清了链路。
配置的副作用往往不出声,每次reload都通过,Googlebot每8次抓取却有1次撞在301上是同一类沉默。
判断办法是分层测。直接请求源站地址取一次头,再请求对外的域名取一次,两边做差集,差出来的部分就是边缘层或者平台加的。这一步做完,你才知道哪些字段是自己能改的、哪些不是。
省掉这一步的后果是很实际的:对着一个平台下发的字段查半天配置文件,怎么也找不到它从哪来。
给一个已经停运的插件发跨域策略,还有意义吗?
三个头里最值得单独说的是X-Permitted-Cross-Domain-Policies,67个站(47.2%)在发。它的接收端是这批里最特殊的。
这类残留攒起来就是技术债,技术债怎么排查和分批还给了一套顺序。
这个头本来是给谁看的
它管的是一件很具体的事:某些客户端软件在跨域读取数据之前,会先去站点根目录找一份策略文件,看自己有没有被允许。这个响应头的作用是覆盖那份文件的效力,比如声明整站都不许用策略文件。
真通过文档分发内容的站另有功课,压缩瘦身与页面管理的实战更贴那类场景。
PDF这类资源有它自己的一套讲究,怎么让它被收录与六个优化点单独讲过。
按X-Permitted-Cross-Domain-Policies的适用范围,会去读这套机制的主要就两类客户端:一个是当年遍地都是的网页动画播放插件,另一个是某家公司的PDF阅读软件。
顺带说一个反面情况:确实还需要它的站
把话说完整:这个头并不是对所有站都无意义。
真通过文档分发内容的站要另想,加密、权限与涂密的实战做法更贴那类场景。
如果一个站确实通过某类客户端软件分发内容,或者提供供第三方跨域读取的数据文件,那么这套跨域策略机制还在它的场景里生效。这类站在企业软件、金融数据、地图服务里还有,只是在消费品电商里基本不存在。
所以判断的时候要加一步:先确认自己有没有那类使用场景。有的话这个头是现行配置,动它需要评估;没有的话它就是残留。
这一步之所以要强调,是因为它是这类清理里最容易出事的地方——按一份通用清单删掉一个自己其实在用的字段。本文给的所有建议动作都带着这个前提:先确认自己的场景,再照做。
主要接收端在2020年底停运
那个播放插件在2020年12月31日正式停止支持,2021年1月起被厂商主动阻止运行,浏览器也早已移除了对它的内置支持。也就是说这个头最主要的读者,已经消失五年多了。
接收端下线之后的迁移可以参考,某个跳转方案下线之后的完整迁移路径是一次典型清理。
这里要说得准确一些:另一类接收端——那家公司的PDF阅读软件——在某些场景下仍会读取跨域策略。所以这个头不能说完全没有接收端,它的准确状态是:主要读者已经消失,剩下的适用场景极窄,而且跟一个电商站点几乎没有关系。
67个站里绝大多数不提供需要跨域读取的数据文件,也不通过PDF阅读器分发内容。对它们来说这个头就是纯粹的历史残留。
取值几乎都一样
这批站的取值高度统一,基本都是声明不允许使用策略文件。这也符合它作为平台默认配置的性质——默认值总是选最保守的那个。
默认值统一是自动生成的标志,从模板化转向语义化的八步讲了怎么把默认做扎实。
顺带说一句,这个取值本身在当年是合理的选择:多数站确实不需要那套跨域机制,明确关掉比留着不管好。问题只在于时间——当接收端消失之后,最保守的那个声明和什么都不声明,效果一样。
X-Download-Options这个头,是写给谁看的?
三个头里的第三个,68个站(47.9%)在发。它的历史更短,接收端也更明确。
文件分发这一层还有别的手段,两种服务器上的防盗链配法至今有用。
只有一个浏览器读过它
这个头是当年某个浏览器的私有扩展,作用是让用户从站点下载文件时,不能直接在浏览器里打开,必须先存到本地。目的是防止一个恶意的HTML文件在站点的上下文里执行脚本。
只在某一种环境下成立的配置都要留意,伪静态导致后台打不开的修复是同类问题。
只服务单一客户端的方案都短命,响应式与自适应的九个维度对比说明了通用性的价值。
它从来没有进入任何标准,也从来没有第二个浏览器实现过它。所以它的接收端从一开始就只有一个。
拿它跟连接安全那个头比一下
为了说清有效和无效的区别,把这批数据里覆盖率最高的那个安全头拿来对照:126个站(88.0%)发了强制安全连接的头。
那个头的配法与回滚都要提前想好,三种服务器上的配置与两条回滚路都在这里。
这个头是现行有效的,所有浏览器都实现,作用是告诉浏览器以后一律用加密连接访问这个域名。它的覆盖率比X-Download-Options高整整四成。
为什么它的覆盖率这么高?因为它同时满足三个条件:接收端明确、效果可验证、而且被绝大多数平台和证书服务商做成了默认或者一键开关。
对比之下就能看出这批死头的处境:它们当年也进了默认,但当接收端消失时,没有任何机制把它们从默认里拿掉。进默认容易,出默认没有通道。这句话基本可以概括本文所有数据。
那个浏览器已经在2022年退役
那款浏览器的桌面版本在2022年6月15日结束支持,之后的继任者内置了一个兼容模式,但那个模式的触发方式是企业策略配置的站点清单,跟这个响应头没有关系。
协议层的迁移也是同一类工作,从HTTP迁到HTTPS怎么才能不掉排名是完整路径。
所以这个头今天没有任何客户端会读。跟X-XSS-Protection一样,它属于第一种形态:功能被移除,字段本身还能合法地发出去。
这批头当年为什么会被一起推荐
要理解这三个头为什么会成为默认配置,得回到它们被推荐的那个年代。
当年那批加固建议里也有真正有效的,权限分离与应急响应的完整做法至今适用。
照抄清单在工具选型里同样常见,用第一性原理做选型比跟着别人的清单走稳。
2010年前后,网站安全加固这件事刚开始被系统整理,出现了一批影响很大的清单式建议:把这几个响应头一起加上,就能挡住当时最常见的几类攻击。清单里的每一项在当时都有明确的对象——有一个浏览器会读它,读了会改变行为。
这批建议后来被大量转载,进了各种框架的中间件、主机面板的一键配置、以及电商平台的默认响应链路。到这一步,它就脱离了原始判断,变成了一个只需要照抄的清单项。
问题出在之后十几年里:清单里的对象一个一个消失了,而清单本身还在流传。这跟爬虫名单老化是完全相同的机制——一份清单被抄下来的那一刻,它就和产生它的那些判断断开了联系。之后对象怎么变,抄清单的人不会知道。
所以这三个头的处境不是有人做错了什么,而是一条链条上没有任何一环负责回头看。写清单的人当年是对的,抄清单的人当年也是对的,只是没有人被安排去检查对象还在不在。
同样是被新头取代,为什么这一个反而该留
这里有个对照值得专门讲,因为它是判断这类问题时最容易搞错的地方。
新旧手段能不能并用是个常见问题,两个手段同时用的九种场景判断给了明确结论。
样本里有82个站(57.7%)发X-Frame-Options,同时有90个站(63.4%)的内容安全策略里写了帧祖先限制。后者是前者的现代替代品,功能更强也更灵活。按前面的逻辑,旧头似乎该删。
但它不该删,原因很简单:所有现代浏览器至今仍完整实现X-Frame-Options。它没有被移除,也没有被标记为已废弃,只是有了一个更好的替代方案。规范里的建议是两个并行——新的用于精细控制,旧的作为不支持新写法的客户端的兜底。
对比一下就清楚了。Pragma在响应方向上被规范明确定义为没有行为,所以该删;X-Frame-Options被所有浏览器实现,所以该留。两者都是被新头取代,区别在于接收端还在不在。
判据可以固化成一句话:被取代不是删除的理由,没有接收端才是。问这个字段还有没有人读,而不是问它有没有更新的写法。
它和另一个同源头的区别
有个容易混淆的地方值得澄清。同一批安全头里还有一个X-Content-Type-Options,值通常是nosniff,这批样本里110个站(77.5%)在发。
响应头能替非网页资源说话,接口和feed这类地址拿什么说自己不想被收录量过一次。
这一个是有效的。所有现代浏览器都实现了它,作用是禁止浏览器根据内容猜测响应类型。它已经进入了正式规范,是今天仍然应该配的头之一。
两个头名字前缀相同、出现的场合相同、通常由同一份默认配置一起下发,但一个有效一个无效。这也说明为什么这类判断不能靠名字或者印象,得逐个查它的接收端。
报告链断在中间:64个站的错误报告,发得出去吗?
接下来这一组比前面几个复杂,因为它涉及两个头的配合,而链条断在了中间。
监控这件事要成闭环,怎么搭一套能在掉量前报警的体系有可复用的规则。
64个站配了网络错误上报
样本里有64个站(45.1%)发了NEL这个头。它的作用是让浏览器把访问过程中遇到的网络层错误——连接失败、证书问题、DNS问题——收集起来上报给站点指定的地址。
这类失败在服务器日志里看不见,从DNS、线路到CDN的网络层排障是配套的排查路径。
这个机制本身很有价值。网络层的失败在服务器日志里是看不见的:请求根本没到服务器,日志里当然没有记录。浏览器主动上报是唯一能看到这类失败的途径。
但上报地址靠的是一个已经进入弃用状态的头
NEL本身不指定上报到哪里,它引用另一个头来定义上报端点。这里就是问题所在:
自己那一侧的日志也要配够,访问日志怎么配才查得清问题包含真实来源地址。
| 指定上报端点的头 | 站数 | 状态 |
|---|---|---|
| Report-To(旧版) | 65 | 已进入弃用状态,规范建议改用新版 |
| Reporting-Endpoints(新版) | 2 | 现行标准写法 |
差距是32.5倍。更极端的是把这两个头和NEL交叉看:64个配了NEL的站,全部只有旧版的Report-To,一个都没有配新版的Reporting-Endpoints。
新版写法长什么样
既然结论是先补,那就把补法写清楚。Reporting-Endpoints的现行写法是在一个头里声明一组命名端点,然后其它机制引用这些名字:
内容协商这类新机制也在长出来,用协商给机器直接送精简内容是另一个方向。
收上来的数据要有地方放,日志怎么管才不爆盘又查得到得先解决。
Reporting-Endpoints: default="https://example.com/report", network="https://example.com/nel"
NEL: {"report_to":"network","max_age":86400,"success_fraction":0}
要点有三个。第一是端点名可以自定义,NEL里引用的名字要和声明里的对上。第二是成功采样比例通常设成0,只收失败——设成正数会带来相当大的上报量,而成功的请求你在自己日志里已经有了。第三是新旧并行没有冲突,同时发旧版和新版是安全的过渡做法,不支持新版的客户端读旧的,支持的读新的。
补完之后要验证一次:打开浏览器开发者工具,在网络面板里确认这两个头都发出来了,然后故意制造一次失败——比如访问一个证书不匹配的子域名——看上报端点有没有收到数据。没有验证过的上报配置等于没配,这一点和本文批评的那些死头是同一个道理。
这不等于报告一定发不出去,但状态很脆弱
这里必须说清边界,不能夸大。旧版的头目前在部分浏览器里仍被识别,属于向后兼容期。所以准确的表述是:这64个站的错误上报能不能到达,取决于浏览器还愿意兼容多久,而不取决于站点自己的配置。
依赖别人兼容承诺的事都脆弱,抓取格局这两年的变化速度是另一个例子。
这跟前面几个头有本质区别。前面那些是已经确定无效,处理方式是删掉;这一个是暂时有效但依赖别人的兼容承诺,处理方式是补上新版的写法——加一行,两个头同时发,新旧客户端都覆盖。
这也是这类问题里最值得优先处理的一类:它现在还有作用,而且这个作用会在某个你不会被通知到的时间点消失。
这套机制能看到的东西,日志里确实看不到
顺着说清这套机制为什么值得配,而不只是值得修。
服务器侧的异常也要有对应记录,登录加固与爆破防护是同一批日志里能看到的事。
有些失败确实不出现在日志里,抓取速率被调低的那几天日志里一条错误都没有就是这种情形。
服务器日志有一个天然的盲区:它只能记下已经到达服务器的请求。而访问失败最常发生的位置恰恰在到达之前——域名解析失败、连接建立超时、证书校验不通过、中间网络丢包。这些情况下用户看到的是打不开,而服务器日志里什么都没有。
浏览器主动上报机制填的正是这个洞。它让浏览器把这类失败记录下来,攒够一批之后上报给站点指定的地址。对做海外业务的站尤其有用,因为跨境链路上的失败远比本地多,而这些失败在国内的监控里完全看不见。
本文那64个配了它的站,其实配的是一件相当有价值的事——只是上报端点的写法停在了旧版。这也是为什么前面把它列成先补而不是先删:这一组不是历史包袱,是一个配了一半的有用机制。
更容易被忽略的是没人看上报数据
还有一层实操上的问题。配了上报头不等于有人在看上报的数据——上报端点得有一个服务在接、有一个地方展示、有人定期打开。
收了数据没人看是常态,慢查询日志攒了783MB而真正超时的只有32条是同一类现象。
本文没法从外部判断这64个站有没有真的在用这些数据。但从新旧头的比例可以推测一部分:如果有人在实际使用这套机制,他多半会注意到规范换代这件事,因为接收数据的那一端通常会提示。全部64个站都停在旧版,更像是这套配置由平台一次性下发,然后没有人再碰过。
剩下那几个死头,各还剩多少个站在发?
把长尾的几个一起看,它们的数量小,但每一个都对应一段很具体的历史。
缓存这几个头的配合最容易搞混,怎么配才让回头客秒开又不出改了不更新的事故讲了取舍。
四个字段的实测数量
| 字段 | 站数 | 接收端的状态 |
|---|---|---|
| Pragma: no-cache | 19(13.4%) | 响应方向上早已废弃,缓存控制由另一个头接管 |
| P3P | 2(1.4%) | 相关标准工作已停止,唯一读它的浏览器已退役 |
| X-UA-Compatible | 1(0.7%) | 只有那个已退役的浏览器据此改变渲染行为 |
| Expect-CT | 1(0.7%) | 浏览器已于2022年移除支持 |
状态相关的配置最容易配错,自定义错误页与软404的正确配法单独讲过。
有时候这些头不是你写的,程序在响应头里替你写了一行1981年的日期就是个例子。
四个字段各自的时间线
把这四个的关键时间点排在一起,能直观看出它们的年龄。
新旧交替的速度在别处更快,这两年格局变化的规模是个参照。
| 字段 | 大致出现时间 | 失去接收端的时间 | 今天还剩多少站在发 |
|---|---|---|---|
| Pragma | 比现行HTTP版本还早 | 随缓存规范换代逐步废弃 | 19个 |
| P3P | 2000年前后 | 相关标准工作2018年停止 | 2个 |
| X-UA-Compatible | 2009年前后 | 对应浏览器2022年6月退役 | 1个 |
| Expect-CT | 2017年前后 | 浏览器2022年底移除支持 | 1个 |
有意思的是数量和年龄的关系:最老的那个反而剩得最多。这不是巧合——Pragma被写进了无数篇教程和模板,传播面远大于后面三个;而Expect-CT只活跃了五年左右,还没来得及被大量抄袭就退场了。
所以一个字段的残留数量,取决于它当年的传播广度,而不是它失效多久。这也解释了为什么本文最普遍的那三个死头都出自平台默认——传播广度的极致就是被写进默认配置。
Pragma这一行为什么还有19个站在发
Pragma是这几个里数量最多的,也最有代表性。它是一个比现行HTTP版本还老的字段,当年用来在请求里表达不要用缓存。
缓存声明要和缓存层对上,全页缓存怎么配以及怎么清里有对应处理。
现行HTTP缓存规范对它的处理写得很明确:这个字段已经废弃,只有出现在请求方向上的no-cache需要被处理,而出现在响应方向上的Pragma没有定义任何行为。也就是说,站点在响应里发Pragma: no-cache,规范上没有任何客户端需要理它。
它能留这么久,原因是很多年前流传的一套写法:为了兼容各种老客户端,把Cache-Control、Pragma、Expires三个头一起发。这套写法在当年是有道理的,因为那时确实还有不认识Cache-Control的客户端。今天这些客户端都不在了,但那套写法还在各种教程和模板里。
那唯一一个Expires单独有效的站
把这个孤例说完,因为它是本文唯一一个逆向的案例。
孤例往往出现在配置最简单的站上,双向跳转的两种服务器写法是那类站常用的。
孤例最能说明要看上下文,八维决策树与规则迁移也是逐个场景判断的。
28个发Expires的站里,27个同时发了Cache-Control,那27个的Expires都被忽略。剩下1个只发Expires不发Cache-Control——对这个站来说,Expires是它唯一的缓存声明,完全有效。
这个孤例的意义是提醒:同一个字段在不同上下文里的状态可能相反。如果按一份通用清单批量删除Expires,这个站的缓存策略就直接消失了。
所以前面那张判断表里,Expires那一行写的是看情况而不是可删。这类需要看上下文的字段在实践中不算少,处理它们的唯一办法是逐个看,没有捷径。这也是为什么本文反复强调先确认自己的场景——通用结论在孤例上就是错的。
顺带看一个相关的三头组合
正好接着说这三个头的配合,因为样本里还有个数字:28个站(19.7%)发了Expires,其中27个同时发了Cache-Control。
缓存分好几层,对象缓存的原理与运维是另一层的事。
按现行规范,两个头同时存在时,Cache-Control优先,Expires被完全忽略。所以这27个站的Expires也是空转的——它不会造成错误,只是没有作用。
剩下那1个只发Expires不发Cache-Control的站,情况反而不同:这时候Expires是真正生效的。这就是为什么这类清理不能批量做——同一个字段,在不同上下文里可能有效也可能无效。
这几个字段在别的场景里还有活的吗
为了不把话说过头,把这四个字段还可能有效的场景交代一下。
内网和公网的判断标准不同,端口放行与云安全组怎么配就分了这两种情况。
写代理服务时入方向的字段还得认,反向代理的完整配置场景是前置知识。
按Pragma的弃用说明,它在请求方向上仍然有定义——客户端发出的Pragma: no-cache需要被中间缓存处理。所以如果你在写一个代理或者缓存服务,这个字段在入方向上还得认。它失效的只是响应方向。
P3P和X-UA-Compatible在企业内网环境里可能还有对象:某些机构的终端仍在运行早期版本的浏览器。这类环境不在公网统计里,也不适用本文的结论。判断标准很实际——看自己的访问日志里有没有那些老浏览器的标识,有相当比例的话就得单独考虑。
Expect-CT是唯一没有余地的:它对应的机制已经被更基础的证书透明度要求取代,浏览器不再需要站点声明这件事。
说这些是为了强调一件事:所有关于失效的判断都带着适用范围,范围一变结论可能就变。本文的范围是面向公网消费者的站点,超出这个范围要重新判断。
P3P那两个站,值一模一样
剩下三个字段各只有一两个站,但细节挺有意思。
隐私相关的机制变化影响分析口径,默认追踪不到的流量怎么补是同一批变化带来的。
发P3P的两个站属于同一个集团旗下的两个品牌,发出来的值一模一样,是一串当年那套隐私声明的紧凑写法。这个头的历史很特殊:它当年真正的用途不是表达隐私政策,而是让一个特定浏览器同意接受第三方Cookie——不发这个头,那个浏览器会拦掉。所以它是一个为了绕过某一款浏览器的限制而存在的头,那款浏览器退役之后,它就彻底没有了对象。
发X-UA-Compatible的那一个站值是让浏览器用最新模式渲染。这个头当年解决的是一个真实痛点:某些浏览器会用兼容模式渲染现代页面,导致布局错乱。今天这个痛点不存在了。
发Expect-CT的那一个站值是max-age=0,意思是关闭这个机制。跟前面那两个发X-XSS-Protection: 0的站一样,是在关闭一个已经不存在的功能。
Feature-Policy一个都不剩,这说明什么?
本来预期会在样本里看到不少Feature-Policy,因为它也是一个被新字段取代的旧头。结果是零。
该先做什么得排序,三类站点各自的高回报修复项可以直接借。
零的两种可能,得分清
看到零的第一反应可能是清理得很彻底。但这个结论下得太早——零也可能意味着这批站从来就没用过它。
数字为零要看是没做还是做完了,响应模式怎么分析也强调了取样这一步。
数字为零的解释常有两种,某个命令能查什么、什么时候会误判也强调了这一点。
区分办法是看它的继任者用得怎么样。如果继任者覆盖率高,说明发生了迁移;如果继任者也几乎没人用,说明这一类能力从来没被广泛采用过。
继任者只有6个站在用
| 字段 | 站数 | 说明 |
|---|---|---|
| Feature-Policy(旧) | 0 | 已被取代 |
| Permissions-Policy(新) | 6(4.2%) | 现行标准写法 |
| Referrer-Policy | 18(12.7%) | 现行有效 |
| Cross-Origin-Opener-Policy | 4(2.8%) | 现行有效 |
| Cross-Origin-Embedder-Policy | 2(1.4%) | 现行有效 |
该配的东西要按收益排,让机器抓得到、读得懂、引得出是技术端的落地清单。
答案清楚了:不是迁移完成,是这一整类头本来就极少有人配。旧的零个、新的六个,两边都接近于零。
新头没人配,原因不是不知道
先解释一下为什么现行有效的新头覆盖率这么低,因为这决定了该怎么改。
收益不可见的事都排不上,500个站实测排出来的优先级能帮你把顺序讲清。
最容易想到的解释是大家不知道它存在。但这个解释站不住——这些头在各类技术清单和审计工具里都有,做技术的人多半听说过。
真实原因更实际,有三条。一是配它需要先做调研:能力策略这类头要求你列清页面上到底用了哪些浏览器能力,而这件事在一个挂了七八个第三方脚本的电商站上相当费时间。二是配错了会直接坏功能:一个写得过严的策略会把地图、支付按钮、视频播放器一起关掉,而这类故障往往要等用户反馈才发现。三是它没有可见收益:配好之后没有任何指标会变好,只是理论上减少了某类风险。
调研成本高、配错代价大、收益不可见——这三条合起来,任何团队都会把它排到最后。这跟前面那些死头留在文件里的机制正好互补:删掉没用的没有收益,配上有用的也没有收益,于是两件事都不会发生。
那什么情况下它才会被推动
按观察,这类配置真正落地通常靠三种外力。
做成默认或者一键是最有效的推动,免插件自动更新站点地图就是这个思路。
靠流程推动比靠说服有效,把变更做成日志的十三类信号讲的就是这套办法。
第一种是平台或框架把它做成默认。这是效率最高的路径,本文那三个死头就是这么普及的——只不过它们普及的时候还是有效的。
第二种是合规要求点名。当某份检查清单明确要求某个字段时,它会在一个季度内被大批量补上。这也是为什么本文强调要把清理结果同步给做审计的人——清单的力量比技术判断大。
第三种是出过一次事故。这是代价最高的推动方式,但效果最持久。
知道这三条之后,推动这类工作的策略就清楚了:别去说服每个人理解字段的原理,去改清单,或者去找平台的默认配置。
这个对照才是本文最该记住的数字
把两组数字并排放着,整件事的形状就出来了。
两个数字并排看才有意义,从指标体系到异常诊断的完整做法可以拿来搭口径。
| 类别 | 代表字段 | 覆盖率 | 是谁配的 |
|---|---|---|---|
| 已经没有接收端的老头 | X-XSS-Protection | 63.4% | 平台默认下发 |
| 已经没有接收端的老头 | X-Download-Options | 47.9% | 平台默认下发 |
| 现行有效的新头 | Permissions-Policy | 4.2% | 需要人主动写 |
| 现行有效的新头 | Cross-Origin-Opener-Policy | 2.8% | 需要人主动写 |
已经没用的头覆盖六成,真正有用的新头覆盖百分之几。这个反差不是因为大家不懂,而是因为两者的来源完全不同:老头是平台在某个年代一次性加进默认配置的,从此跟着每个新建的站;新头需要有人知道它存在、判断需不需要、然后手写进配置。
那这类新头到底该不该配
给一个实用的判断,不然读完只会觉得两头都不对。
引荐来源这类设置会影响归因,服务器端跟踪的部署方式对数据准确性影响更大。
先看能力策略这一类。它的价值在于限制页面能用哪些浏览器能力——摄像头、地理位置、全屏、支付接口这些。对一个内容站或者普通电商站,这个限制的实际收益不大,因为页面本来就不用那些能力,被滥用的概率也低。所以它属于有余力再配的档位,排在内容安全策略后面。
再看跨源隔离那两个头。它们的用途更窄,主要服务于需要用到高精度计时或者共享内存的场景。普通站点配了没有坏处但也没有收益,不配完全合理。本文列出它们的覆盖率,是为了说明覆盖率梯度,不是在建议每个站都补上。
唯一建议所有站都检查的是引荐来源策略——18个站(12.7%)在发。它决定用户从你的站点跳转出去时,对方能看到多少来源信息。这个头影响的是隐私和数据分析的准确性,配置成本极低,收益是确定的。
所以这一节的结论不是补齐所有新头,而是:按收益排序,别按清单顺序。清单是平铺的,收益不是。
覆盖率高低,基本等于有没有人做成默认
这个规律在这批数据里到处都能验证。自动加的字段覆盖率极高——压缩相关的头、连接安全相关的头都在八成以上;需要人写但有成熟模板可抄的落在五到七成;需要人写又没有默认模板的,掉到一成以下。
自建站的默认值由自己决定,从主机、插件到核心指标的完整做法可以对照排期。
所以要提高一类配置的覆盖率,最有效的动作不是写文章劝人配,是让它进入某个平台或者框架的默认值。反过来说,一旦一个错误的东西进了默认值,它的清理难度也一样大——这就是本文那248个死头的由来。
这些多余的字段,代价到底在哪里?
这一节要把账算清,因为如果代价真的可以忽略,那前面所有内容就只是一篇趣闻。
字节这件事在别处才是真问题,页面体积超出之后商品链接被截在外面是实测过的后果。
字节层面:中位数102字节,占响应头的1.74%
先量最直观的。142个站的响应头总字节数中位是3204,平均3298,最小的只有98字节,最大的17951。字段行数中位27行,最少4行,最多42行。
真要省字节该看压缩,出厂只压一种类型、其余字节全额计入是更大的一块。
死头占的部分:100个有死头的站里,中位数是102字节,最多120字节。全部142个站合计8145字节,占响应头总字节的1.74%。
算成实际流量:一个日均10万次请求的站,每天多发大约10MB,一年3.7GB左右。这个量对带宽成本几乎没有影响,对首字节时间的影响也小到测不出来。
所以字节这一层的结论要说得干脆:不值得为了省这102个字节去动配置。如果有人拿性能当理由推动清理,这个理由站不住。
那真正的代价是什么
代价有三层,都不在字节上。
看起来达标的指标常有水分,三平台和300个站的实测对比能看出差距。
看起来做过了其实没做的情况不少,大量没用的页面怎么诊断和处置有一张决策矩阵。
第一层是安全审计的假阳性。各类在线检测工具会给响应头打分,而不少工具的评分规则里还留着这些老头——发了就加分,没发就提示缺失。于是一个发着三个死头、却没配现代能力策略的站,可能拿到比反过来的站更高的分数。这个分数会进到报告里,报告会进到会议里,最后变成一句这一块已经做过了。
第二层是它挤掉了注意力。前面那张对照表说明了这个问题的规模:死头覆盖六成,有效的新头覆盖百分之几。如果一个团队看到自己的响应头里已经有一排安全相关字段,他去补新头的动力会明显下降——看起来已经配了不少了。
第三层是排查时的噪声。响应头是排查线上问题时最常翻的地方之一,中位27行里有三四行是完全无效的历史残留,每次都要重新判断一遍这行有没有用。这个成本很小但反复发生。
什么时候值得主动去查一遍
既然日常没有反馈,那查的时机就得靠事件触发。有四个时机的收益最高。
排查问题时顺手多看几眼最划算,负载飙高怎么定位到进程是常走的路径。
改版是最好的清理窗口,改版不掉SEO的完整防护清单里有对应检查项。
第一个是换平台或者换架构的时候。这时候响应头会整体重建,是唯一一个天然的清理窗口,成本几乎为零。
第二个是拿到安全审计报告的时候。报告里通常会列一堆响应头相关的项,正好借这个机会把每一项的接收端核一遍,顺手把审计规则本身也修正掉。
第三个是接入或者下掉第三方服务的时候。第三方服务经常会要求放开某些策略,改完之后那些放开的部分常常忘了收回去。
第四个是排查一个跟浏览器行为有关的问题时。既然已经在看响应头了,多花五分钟把全部字段过一遍,比专门安排一次检查更容易发生。
反过来说,不建议把它做成固定周期的独立任务。这类没有可见收益的任务在排期里活不过第二个季度,挂在别的事情上才活得久。
审计规则那一层,怎么改才有效
既然假阳性来自审计规则,那就得说清这一层怎么动。
把审查交给自动化之前有前提,数据、方法、人工复核这三条缺一条结论就不能用。
常见的检查清单里,安全响应头这一项通常列着一串字段名,逐个检查有没有发。改法有两步。
第一步是把清单里的字段分成三组重新组织:现行有效的必须发、已失效的不该发、新旧换代的必须发新版。这样一份清单从一个平铺的名字列表变成了有判断的结构,而且每一组的动作是明确的。
第二步是给每个字段加一列依据来源,写明这个判断出自哪份规范或者哪条浏览器状态记录。加了这一列之后,清单本身就可以被复核了——下次有人质疑某一项,可以直接去查那个来源,而不是争论谁的经验更新。
这两步做完,清单从一份需要定期人工重写的文档,变成一份可以核对的表。这跟前面处理robots.txt里那些字段是同一个思路:把结论换成判据,结论会过期,判据不会。
什么时候它会变成真问题
有两种情况会让这些字段从无害变成有害。
新旧两套声明同时存在会打架,跨页场景下的八种冲突有对应诊断办法。
一种是字段之间互相干扰。同一个语义有新旧两套写法时,旧的可能覆盖或者干扰新的。样本里那27个同时发Expires和Cache-Control的站属于这类的温和版本——旧的被忽略,没有坏处;但换成别的字段组合,比如新旧两套跨域策略同时存在,行为就可能不符合预期。
另一种是把它当成了防护。如果某个团队因为发着X-XSS-Protection而没有配内容安全策略,那这个头就不只是无害的残留,它直接替代掉了一个真正有效的手段。这批样本里内容安全策略的覆盖率是64.8%,跟X-XSS-Protection几乎相等——说明多数站两个都有,但那剩下的部分值得单独看一眼。
怎么判断一个响应头今天还有没有接收端?
这是本文最想给出的东西:一套能自己执行的判断办法,而不是一份会过期的清单。
越熟练越容易漏掉结构性问题,资深团队的技术判断为什么会失灵讲的正是这类盲区。
三个可查的信源,按可靠度排序
第一可靠的是标准文档本身。字段进了正式规范,规范里会写明它的状态——现行、已弃用、还是已废弃。已弃用意味着规范建议不要再用,通常同时指出替代方案。
信源不同结论就不同,同一个指标各家工具差那么大是最典型的一例。
不同工具能回答的问题不一样,五大类工具各自的边界可以拿来配组合。
第二可靠的是浏览器的功能状态记录。各家浏览器都有公开的特性状态数据库,能查到某个功能是什么时候引入的、什么时候被移除的、当前在哪些版本里可用。这是判断功能被移除这一类的唯一硬证据。
第三是权威的字段参考文档。它们通常会在页面顶部直接标注非标准或已弃用,并写清哪些浏览器实现过它。查得最快,适合初筛。
三者都查不到的字段,基本可以断定它是某家平台的私有扩展——这类不需要判断,因为它对浏览器本来就没有语义。
一个更快的反向判据
还有个不用查文档的办法,适合快速筛:看浏览器开发者工具的控制台会不会对它有反应。
开发者工具能看出的东西不少,四种渲染模式的引用率实测也是这么测出来的。
现代浏览器对自己认识但配置有问题的头会给出提示,比如内容安全策略写错了会明确报出违规详情,跨域策略冲突会说明原因。而对完全不认识的头,控制台一个字都不会说。
所以打开控制台把页面刷一遍,凡是响应头里有、控制台从来不提的字段,都值得拿去查一查——它可能是私有扩展,也可能是已经没人读的老头。这个办法不严谨但很快,适合先缩小范围。
本文所有字段的判断结果汇总
把这批数据里遇到的字段按结论整理成一张表,可以直接拿去对照自己的站:
逐项校验的思路在别处一样,一个尾逗号就能让整页标记失效是同类静默错误。
页面那一侧也有类似的体检表,骨架的六个维度体检能揪出结构短板。
| 字段 | 本批覆盖率 | 结论 | 建议动作 |
|---|---|---|---|
| X-XSS-Protection | 63.4% | 对应功能已被浏览器移除 | 可删,改用内容安全策略 |
| X-Download-Options | 47.9% | 唯一读它的浏览器已退役 | 可删 |
| X-Permitted-Cross-Domain-Policies | 47.2% | 主要接收端已停运 | 多数站可删 |
| Pragma: no-cache | 13.4% | 响应方向上已废弃 | 可删,保留Cache-Control |
| P3P | 1.4% | 唯一读它的浏览器已退役 | 可删 |
| X-UA-Compatible | 0.7% | 现代浏览器不据此改变行为 | 可删 |
| Expect-CT | 0.7% | 浏览器已移除支持 | 可删 |
| Report-To | 45.8% | 已进入弃用状态但仍被兼容 | 补发新版,两个并行 |
| Expires | 19.7% | 与Cache-Control同存时被忽略 | 看情况,单独存在时有效 |
| X-Content-Type-Options | 77.5% | 现行有效 | 保留 |
| Content-Security-Policy | 64.8% | 现行有效 | 保留并检查质量 |
| Permissions-Policy | 4.2% | 现行有效但极少人配 | 值得补 |
把判断做成一段能重复跑的检查
手工查一遍之后,可以把结论固化成一段脚本,以后每次上线顺手跑一次。思路是维护一份自己确认过的失效字段清单,然后检查响应头里有没有出现它们:
检查脚本适合挂成定时任务,用cron把这些活自动化有可直接改的脚本集。
DEAD="x-xss-protection x-download-options x-permitted-cross-domain-policies expect-ct p3p x-ua-compatible"
H=$(curl -s -D - -o /dev/null https://example.com/)
for f in $DEAD; do
echo "$H" | grep -qi "^$f:" && echo "还在发: $f"
done
echo "$H" | grep -qi "^report-to:" && ! echo "$H" | grep -qi "^reporting-endpoints:" && echo "上报端点只有旧版"
这段东西的价值不在于它有多精巧,而在于它把一次性的判断变成了可重复的检查。清单里的字段是你自己确认过的,所以不会像通用工具那样给出过期结论;而每次上线跑一遍,能挡住配置回退——比如某次改动把老配置又带回来了。
要记得给清单加上确认日期,并且在里面写清每一项的依据。半年后再看的时候,你需要知道当时为什么把它列进来。
遇到查不到的私有字段,怎么处理
实际操作时会遇到一批查不到任何资料的字段,本文样本里的233种字段名有相当一部分属于这类。它们通常是平台内部用的标识,比如请求追踪编号、缓存节点信息、分片标识。
这类字段的处理原则是不管,但要区分两件事。
第一件是它们对浏览器没有语义,所以不构成本文讨论的问题——它们不是失效的配置,而是本来就不写给浏览器看的东西。
第二件是其中有一部分会暴露技术栈信息。样本里有17个站(12.0%)发了标明服务端技术和版本的字段,还有125个站(88.0%)发了服务器软件名。这属于另一个话题——信息暴露面,跟本文的失效问题无关,但既然查到了顺手处理一下也不费事:这类字段通常在服务器配置里一行就能关掉。
判断某个私有字段是不是该关,标准也简单:它有没有暴露版本号。暴露了就关,没暴露就留着,那多半是运维或者客服排查问题时要用的。
这张表本身也会过期
得说明一句:这张表是2026年8月的状态,它跟本文批评的那些清单是同一种东西——一份会过期的现成结论。
会过期的结论都需要复核机制,上千篇旧内容的留改并删转怎么定是同一套思路。
所以更该记住的是产生这张表的动作:拿到一个字段名,去查它在规范里的状态、在浏览器里的实现状态,两个答案合起来就是结论。这个动作每个字段花两三分钟,而且永远不会过期。
清理的时候,该按什么顺序动手?
最后给一套可执行的顺序。这里的关键不是删得干净,是别把有效的一起删掉。
这几层的顺序不能乱,重写、缓存、规范化六层怎么排有完整示例。
第一步:先分层,弄清每个字段是谁加的
这一步必须放在最前面,否则后面全是白忙。做法是取两次响应头做对比:
上游和边缘各自会加东西,反向代理缓存怎么配能帮你理清链路。
边缘那一层能改的东西不少,在CDN边缘改配置的原理与落地形态给了几种形态。
curl -sI --resolve example.com:443:源站IP https://example.com/ > /tmp/origin.txt
curl -sI https://example.com/ > /tmp/edge.txt
diff <(grep -oiE '^[a-z0-9-]+:' /tmp/origin.txt | sort) \
<(grep -oiE '^[a-z0-9-]+:' /tmp/edge.txt | sort)第一条命令绕过边缘节点直连源站,第二条走正常路径,差集就是边缘层或者平台加的字段。要注意有些环境不允许直连源站,那就换成在服务器本机对127.0.0.1请求并带上Host头。
分层的结果决定后面的动作:自己加的可以直接删;平台加的删不掉,只能记录下来知道它无效;边缘层加的通常能在边缘的规则里改。
第二步:先补,再删
顺序很重要——先把需要补的补上,再动删除。原因是删除是不可逆的动作,而且删错了不会立刻表现出来。
补配置的收益有时相当直接,服务器把没改过的页面对爬虫重发了几百遍就是没补的后果。
改配置前先想好怎么退回来,备份了不等于安全,恢复演练才是底气是同一个道理。
需要补的通常只有两类。一类是新旧换代里的新版写法,比如前面说的上报端点,加一行让新旧并行,这一步没有任何风险。另一类是那些覆盖率极低但现行有效的头,判断需不需要之后再配。
补完观察一两周,确认没有异常,再进行删除。
第三步:删除按确定性分批
删的时候分三批,按确定性从高到低走。
动服务器配置要小步走,多域名统一跳转的完整配置可以照抄。
第一批是功能已被移除的:这类删掉零风险,因为它现在就没有任何效果。第二批是接收端产品已退役的:同样零风险,但删之前确认一下自己的用户里确实没有那类客户端——如果是面向企业客户的站,情况可能不同。第三批是被新字段取代的:必须先确认新字段已经生效,再删旧的。
每批之间留一个观察窗口。虽然理论上都是零风险,但配置改动本身可能带来别的问题——比如改动配置文件时不小心影响了同一个区块里的其它字段。
平台加的那些,能做什么
如果字段来自平台且删不掉,能做的有三件事。
服务器配置这一层的写法要熟,重写规则到底怎么写才不绕晕是常用参考。
一是记录。在自己的配置文档里写清哪些字段是平台加的、哪些已经无效,这样下次做审计时不会重复判断,也不会有人对着它找配置文件。
二是不要重复。既然平台已经在发,自己就别再发一遍——样本里没发现重复发同一个字段的情况,但这在自建加平台的混合架构里确实会出现。
三是在评审里说明。当安全审计报告把这些头列成加固措施时,指出它们已经无效,把注意力引到真正需要配的字段上。这一步的价值比删掉那102个字节大得多。
这件事多久做一次合适
按本文观察到的变化速度,一年一次足够,而且最好不要单独安排。
哪些该交给脚本有讲究,哪些能自动化、哪些不能划了一条比较清楚的界。
理由是响应头这一层的变化比爬虫名单那类东西慢得多。一个字段从被浏览器移除到彻底没人提,通常要好几年;而新字段的出现速度也不快,一年里值得关注的新增通常只有一两个。所以高频复核没有意义。
但有一件事值得每次上线都做,就是前面那段自动检查——它防的不是新字段出现,是旧配置回退。配置回退这件事比字段失效常见得多:一次回滚、一次合并冲突、一次从旧模板复制配置,都可能把清理掉的东西带回来。
所以节奏是:判断一年一次,检查每次上线。前者需要人的判断,后者交给脚本。
一次真实的清理是什么样的
保哥今年上半年帮一个做母婴用品的独立站过技术审查,响应头这一项就是照上面这套顺序走的。
指标不动不代表工作没意义,流量暴跌的七个元凶与三个诊断案例说明归因有多容易错。
先分层,结果发现团队自己在服务器上配的只有五个字段,其余十几个全是平台和边缘层加的。这个结果本身就让讨论清晰了很多——原来大部分讨论对象根本不在自己手里。
自己那五个里,有两个属于本文说的确定失效,一个是新旧换代需要补新版,剩下两个有效。动作因此变得非常具体:补一行,删两行,留两行。整件事二十分钟做完。
平台加的那批做了另一件事:写进了配置文档,标注哪些无效、依据是什么、确认日期是哪天。后来那份安全审计报告里把这几个头列成加固措施时,团队直接把这份文档贴过去,省掉了一轮解释。
值得说的是效果——所有指标都没有变化,因为删掉的东西本来就不起作用。这类工作的产出不是指标,是把一份说不清的配置变成一份说得清的配置。下次有人问这个头为什么在这儿,有答案。
一份可以直接用的检查清单
把整套动作压成清单:取一次完整响应头并存档;分层确认每个字段的来源;对着字段名逐个查规范状态与浏览器实现状态;把结论写进配置文档,注明确认日期;先补新版写法,观察;再按确定性分三批删除;把清理结果同步给做安全审计的人,避免下次又被要求加回来。
后台那边的告警也要分级,三平台后台告警怎么分级诊断给了处理顺序。
最后这一条经常被忽略,但它决定了这次清理是一次性的还是永久的。如果审计规则没改,删掉的头会在下一次审计之后被要求加回来——本文这248个字段里,有一部分就是这么来的。
回到最初那个问题
整篇量下来,最值得带走的其实不是那些百分比,是一个提问的习惯。
分层提问这个习惯用处很广,收录、排名、流量是三件事,先分清卡在哪是最常用的一版。
响应这一层还有更要紧的事,一上量就慢该怎么调影响的是所有请求。
响应头、robots.txt、域名记录、邮件策略——凡是你写下之后由别人的实现来解释的配置,都该定期问一句:读它的那一方还在吗。这个问题不会有人替你问,因为对方消失的时候不会通知你,你的系统也不会报错。
而问这一句的成本其实很低:一个字段两三分钟,一份配置一两个小时。真正难的地方在于想起来该问——毕竟一个安静了七年的字段,看起来和一个正在生效的字段一模一样。
常见问题解答
响应头里哪些字段可以直接删掉?
确定性最高的是三类:功能已被浏览器移除的(X-XSS-Protection、Expect-CT)、唯一读它的客户端已退役的(X-Download-Options、P3P、X-UA-Compatible)、响应方向上规范已定义为无行为的(Pragma)。这三类删掉零风险。另有两类不能一刀切:X-Permitted-Cross-Domain-Policies要先确认有没有通过PDF阅读器分发内容的场景;Expires要看有没有同时发Cache-Control——28个发它的站里有1个是单独发的。
怎么知道某个字段是平台加的还是自己加的?
取两次响应头做差集。第一次绕过边缘节点直连源站,第二次走正常的对外域名,两边的字段名清单一减,多出来的就是边缘层或者平台加的。不允许直连源站的环境,可以在服务器本机对127.0.0.1请求并带上Host头。这一步必须放在清理之前,否则会对着一个自己改不了的字段翻半天配置文件。本文那65个发全三个死头的站里,62个带同一个电商平台的特征字段、Server值有64个是同一家云服务商——这些字段全部不在站主的控制范围内。
删掉X-XSS-Protection会不会降低安全性?
不会,因为它现在没有提供任何防护。它对应的是浏览器内置的一套脚本注入检测机制,主流浏览器2019年下半年就默认关闭并随后移除了,另一家浏览器从来没实现过。今天真正防这类问题的是内容安全策略。本文样本里内容安全策略的覆盖率是64.8%,跟这个死头几乎相等,说明多数站两个都有。删之前确认一件事:自己的内容安全策略是不是写得足够严——如果它为了不影响业务写成了大范围放开,那实际防护就得另外补,这跟删不删老头是两件事。
有了内容安全策略的帧祖先限制,X-Frame-Options还需要发吗?
需要,两个并行才是推荐做法。区别在于接收端还在不在:X-Frame-Options至今被所有现代浏览器完整实现,它只是有了更好的替代品,不是失去了读者。规范的建议是新的用于精细控制,旧的作为兜底。这正好是判断这类问题的关键——被取代不是删除的理由,没有接收端才是。本文样本里82个站发X-Frame-Options、90个站的策略里写了帧祖先限制,两者大量重叠,这个状态是对的。
Report-To已经弃用,该怎么迁到新写法?
加一行,两个并行,不用急着删旧的。新写法是用Reporting-Endpoints声明一组命名端点,然后在NEL里引用端点名。要点有三个:端点名可以自定义但引用要对上;成功采样比例设成0只收失败,否则上报量会很大;新旧同时发是安全的过渡。本文那64个配了网络错误上报的站,全部只有旧版的Report-To,一个都没有配新版——它们的上报能不能到达取决于浏览器还兼容多久,而这是最该优先处理的一类,因为它现在还有用,而这个作用会在某个你不会被通知到的时间点消失。
这些多余的响应头会拖慢站点或者影响SEO吗?
基本不会,这一点得说得干脆。本文实测:有死头的100个站里,这些字段占的字节数中位是102,全部142个站合计8145字节,占响应头总字节的1.74%。一个日均10万请求的站每天多发大约10MB,对带宽和首字节时间的影响都测不出来。所以拿性能当清理理由是站不住的。真正的代价在三个地方:安全审计工具会把它们当加固措施打勾造成假阳性、它们让人误以为安全这块已经做过了从而不去补真正有效的头、以及每次排查问题时都要重新判断一遍这行有没有用。
安全审计报告要求加上这些头,该怎么处理?
改清单比改配置重要。做法是把清单里的响应头项重新分成三组——现行有效的必须发、已失效的不该发、新旧换代的必须发新版,然后给每一项加一列依据来源,写明这个判断出自哪份规范或哪条浏览器状态记录。加了来源之后清单本身可以被复核,下次有人质疑可以直接查依据而不是争论谁的经验更新。如果字段是平台加的删不掉,就把这个事实写进配置文档,审计时直接引用,省掉一轮解释。跳过这一步的话,删掉的头会在下一次审计后被要求加回来。
Permissions-Policy这类现行有效的新头,值不值得配?
按收益排序,别按清单顺序。本文样本里它的覆盖率只有4.2%,而已经失效的老头覆盖六成——这个反差不是因为大家不懂,是因为配它需要先梳理页面用了哪些浏览器能力、配错了会直接坏功能、而且配好之后没有任何指标会变好。对普通电商站和内容站,它的实际收益不大,排在内容安全策略后面。真正建议所有站都检查的是引荐来源策略,配置成本极低、收益确定。跨源隔离那两个头用途更窄,不配完全合理。
权威参考资料
本文标题:《HTTP响应头有248个死字段,多数不是你写的》
本文链接:https://zhangwenbao.com/http-response-header-dead-fields-audit.html
版权声明:本文原创,转载与引用请注明作者与原文链接。许可协议: CC BY 4.0