逐项检查响应头

安全响应头检测:如何发现并修复缺失的 HTTP 安全响应头

安全响应头是服务器随响应一并发送的指令:页面可以加载哪些资源、浏览器是否必须坚持使用 HTTPS、谁可以把页面放进框架。本文介绍如何自己读取这些响应头、每项的作用、安全的起始值,以及一个让你先测试再强制执行的上线顺序。

概念插图:一道薄荷绿光束穿过五块带图案的玻璃板,射向一座象牙白拱门,一只铜色镜片正在检查其中一块玻璃板。
概念插图

网站的每个响应都带有访客看不到的响应头。其中一部分是给浏览器的安全指令:只从这些位置加载脚本、对这个主机使用 HTTPS、不允许其他网站嵌套此页面、不让 JavaScript 读取这个 Cookie。缺少这些响应头时,浏览器会退回到更宽松的默认行为;而配置错误时,登录或结账流程可能会失效。

本文介绍如何读取网站实际发送的响应头、每项的作用,以及哪些值适合作为起点。文中包含 nginx 以及 Cloudflare Pages、Netlify 等静态托管平台的基础配置,也列出了导致部分响应没有响应头的常见错误。所有示例均使用 example.com。

本指南的适用范围

免费的上线准备快照只读取一次响应的响应头:即你输入的源站首页,最多跟随五次重定向。它主要检查各响应头是否存在,而不判断取值是否适合你的网站,也不是渗透测试。

1. 自己检查响应头:DevTools 与 curl

在 Chrome 或 Edge 中打开 DevTools,选择网络(Network)面板并刷新页面。点击第一个请求(HTML 文档),打开标头(Headers)标签页,滚动到响应标头(Response Headers)。勾选保留日志(Preserve log)可在页面跳转和重定向后保留请求,勾选停用缓存(Disable cache)可获得新的响应而不是缓存内容(Chrome DevTools 网络参考)。Firefox 的网络监视器用法相同。

在终端中,curl 会原样打印服务器发送的内容:

curl -sS -D - -o /dev/null https://example.com/

加上 -L 可以跟随重定向,并查看每一跳的响应头。在 Windows 上请调用 curl.exe,并使用 -o NUL。curl -I 更短,但它发送的是 HEAD 请求而非 GET,有些应用对 HEAD 走的是不同的代码路径,因此请用 GET 确认你的发现。

请对以下目标重复检查:http:// 地址(应以 301 或 308 重定向到 HTTPS)、另一个主机名(www 或裸域名)、一个不存在的页面、登录页、API 响应和静态文件。应用、Web 服务器和 CDN 往往在这些位置设置不同的响应头,而只有最终到达浏览器的才算数。

2. 每个响应头的作用与安全的起始值

响应头作用安全的起始值
Content-Security-Policy限制脚本、样式、框架和其他资源的加载来源一份草案策略,先以 Content-Security-Policy-Report-Only 发送
CSP frame-ancestors决定哪些网站可以把页面嵌入框架(防点击劫持)frame-ancestors 'self' 或 'none'
X-Frame-Options旧式的嵌套控制,作为旧浏览器的后备SAMEORIGIN 或 DENY,与 frame-ancestors 保持一致
Strict-Transport-Security告诉浏览器在 max-age 秒内只对该主机使用 HTTPSmax-age=300,分阶段提高
X-Content-Type-Options禁止 MIME 类型猜测;拦截以错误类型提供的脚本和样式表nosniff
Referrer-Policy控制通过 Referer 头向其他网站发送多少页面 URLstrict-origin-when-cross-origin
Permissions-Policy为页面及其框架关闭摄像头等浏览器功能camera=(), microphone=(), geolocation=()
Cross-Origin-Opener-Policy将你的窗口与跨源弹窗以及打开它的页面隔离same-origin;如有 OAuth 或支付弹窗,用 same-origin-allow-popups
Set-Cookie 属性限制会话 Cookie 的传输方式和可读取者Secure; HttpOnly; SameSite=Lax
X-XSS-Protection曾用于控制旧浏览器中的过滤器;现已弃用删除,或发送 0

启用 nosniff 后,浏览器会信任声明的 Content-Type,并拦截类型错误的脚本和样式表,因此开启前请先检查这些类型。即使没有 Referrer-Policy 响应头,当前的浏览器也已默认使用 strict-origin-when-cross-origin;OWASP 仍建议为旧浏览器显式发送它;如果你依赖的功能都不需要来源信息,no-referrer 更严格。在 Permissions-Policy 中,() 会在页面及其内部所有框架中禁用某项功能;只应为确实需要该功能的组件源开放。COOP 的 same-origin 有助于防御跨源泄露攻击(XS-Leaks),但可能破坏由其他源提供的登录或支付弹窗。

3. Content-Security-Policy 与 frame-ancestors

CSP 告诉浏览器页面可以从哪里加载脚本、样式、图片、框架和连接。它限制被注入脚本能做的事,但并不能取代对输入的转义和校验。MDN CSP 指南推荐使用严格 CSP,即基于每个响应全新生成的 nonce 或哈希,而不是一长串允许的主机列表。'unsafe-inline' 会让大部分防护失效,而且在同时包含 nonce 或哈希的指令中,浏览器会忽略它。让严格策略难以实施的,通常是会继续加载其他脚本的第三方代码,例如标签管理器和聊天组件,所以请记下你添加的每一项例外的理由。

default-src 是 script-src 等获取类指令的后备,但不是 frame-ancestors 的后备:策略为 default-src 'none' 时,任何网站仍然可以嵌套该页面。请显式设置 frame-ancestors 'self',或设为 'none',后者与 X-Frame-Options: DENY 类似(MDN)。OWASP HTTP 响应头速查表指出,在支持 frame-ancestors 的浏览器中它会使 X-Frame-Options 过时,而 X-Frame-Options 仍可覆盖旧浏览器;如果两者都发送,请让它们允许相同的嵌套方式。有两个陷阱:frame-ancestors、仅报告策略和 X-Frame-Options 放在 <meta> 标签中都不会生效;而 X-Frame-Options: ALLOW-FROM 会让现代浏览器忽略整个响应头。

对于没有第三方脚本的网站,第一版草案可采用 OWASP Secure Headers 项目的推荐策略:

default-src 'self'; form-action 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests

它会拦截内联脚本、内联样式以及从其他主机加载的一切内容,因此一开始会出现大量报告;这正是它要先以 Report-Only 方式运行的原因(第 8 节)。只取 CSP 这一行:该项目的完整配置还包含 Cache-Control: no-store 和 Clear-Site-Data 等值,它们会禁用缓存,并在每个响应上清除访客的 Cookie 和存储数据。对于浏览器不会渲染的 JSON API 响应,CSP 作用不大。

4. HSTS:逐步提高 max-age,慎用 preload

Strict-Transport-Security 告诉浏览器对该主机只使用 HTTPS,并自动升级之后的 http:// 请求。如果响应头通过普通 HTTP 到达,浏览器会忽略它,所以应在 HTTPS 响应上发送,而不是在 HTTP 到 HTTPS 的重定向上发送。仅靠它本身,无法保护访客的第一次连接。

要先考虑代价:在启用 HSTS 的主机上,访客无法点击跳过证书错误,因此证书过期会把回访的用户挡在门外。自 2026 年 3 月 15 日起签发的公共 TLS 证书有效期最长为 200 天(CA/Browser Forum),而且 Let’s Encrypt 已不再发送到期提醒邮件,所以请先建立自动续期和自己的到期监控。证书方面的内容见 SSL/TLS 与安全响应头指南。

按 hstspreload.org 的建议分阶段提高 max-age(单位为秒),并在每一步检查是否出问题:300(5 分钟)、604800(1 周)、2592000(1 个月),然后是 31536000(1 年)或 63072000(2 年,OWASP 使用的值)。max-age=0 可以移除该策略,但只有通过 HTTPS 发送才有效,并且只对回访并收到它的访客生效。待每个子域名都能通过 HTTPS 正常访问后,再添加 includeSubDomains,并让每个子域名也发送自己的响应头。

预加载(preload)会把你的域名内置到浏览器中,使首次访问也使用 HTTPS。提交网站 hstspreload.org 现在推荐使用 HSTS,但不推荐预加载:Chrome 和 Safari 已经会把 HTTP 导航升级为 HTTPS,预加载的额外收益很小,而从列表中移除需要数月才能传达到用户。OWASP HTTP 响应头速查表的示例响应头仍包含 preload,而 OWASP Secure Headers 项目的推荐值则没有。不要默认添加它,也不要从模板中直接照搬。

5. Cookie 属性:Secure、HttpOnly 与 SameSite

Set-Cookie: __Host-session=…; Path=/; Secure; HttpOnly; SameSite=Lax
  • Secure 让浏览器只通过 HTTPS 发送该 Cookie(localhost 除外)。它不会对 JavaScript 隐藏该 Cookie。
  • HttpOnly 阻止页面上的脚本读取该 Cookie(例如通过 document.cookie),但浏览器仍会在匹配的请求中发送它,因此它只能限制通过 XSS 窃取 Cookie,而无法阻止 XSS 本身。
  • SameSite 决定 Cookie 是否随跨站请求发送。Strict 永不发送;Lax 还会随来自其他站点的顶级 GET 导航发送,例如访客点击链接来到你的网站;None 则始终发送,且 None 要求同时设置 Secure。各浏览器的默认值不同,所以请显式设置。
  • __Host- 名称前缀让浏览器拒绝接受该 Cookie,除非它来自 HTTPS 源并带有 Secure、Path=/,且没有 Domain 属性。

会话 Cookie 请使用 Lax 或 Strict,除非某个流程确实需要 None,例如从其他站点回传的支付或登录返回。OWASP 会话管理速查表将 SameSite 视为防御跨站请求伪造(CSRF)的纵深防御措施,而不是 CSRF 令牌的替代品。

6. 应该去掉的响应头:X-XSS-Protection 及其他遗留项

  • X-XSS-Protection 在旧浏览器中启用的过滤器本身可能产生 XSS 漏洞(MDN)。OWASP 建议不要设置它,或用 0 将其关闭;替代方案是 CSP。
  • Expect-CT:OWASP 建议不要使用,并引用 Mozilla 的说法,建议从现有配置中移除。
  • Public-Key-Pins(HPKP)已于 2018 年从 Chromium 中移除,任何现代浏览器都不再支持。
  • Feature-Policy 已被 Permissions-Policy 取代。

Server 和 X-Powered-By 会暴露你使用的软件,有时还带版本号。OWASP 建议移除它们或让其不带有用信息,但其自身的测试指南称这属于“通过隐藏求安全”,而 MDN 则指出及时给软件打补丁更重要。请去掉版本号(在 nginx 中,server_tokens off; 去掉的是版本,而不是响应头本身),并把精力花在更新上。

7. 可直接复制的 nginx 与静态托管基础配置

下面的 nginx 基础配置设置了低风险的响应头,让 HSTS 和 CSP 处于测试阶段,并在同一主机上把 HTTP 重定向到 HTTPS。always 参数会让错误响应也带上这些响应头。

server {
    listen 80;
    server_name example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com;
    # 在此填写 ssl_certificate 和 ssl_certificate_key
    server_tokens off;

    add_header Strict-Transport-Security "max-age=300" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header Cross-Origin-Opener-Policy "same-origin" always;
    add_header Content-Security-Policy-Report-Only "default-src 'self'; form-action 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'self'" always;
}

nginx headers 模块有两个文档中记载的行为,能解释很多响应头缺失的情况。没有 always 时,add_header 只对状态码 200、201、204、206、301、302、303、304、307 和 308 生效,因此 404 和 500 页面发出时没有这些响应头。另外,只有当前层级没有定义任何 add_header 时,才会继承上层的 add_header 指令:在 location 块中只写一条 add_header,就会丢掉该处所有 server 级别的安全响应头。解决办法是重复写出这些响应头、用 include 引入共享文件,或者从 nginx 1.29.3 起使用 add_header_inherit merge;。

Cloudflare Pages 和 Netlify 等静态托管平台会读取随网站部署的 _headers 文件。下面的示例遵循 Cloudflare 文档中的格式,Netlify 也使用同样的格式:

/*
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()
  X-Frame-Options: SAMEORIGIN
  Content-Security-Policy-Report-Only: default-src 'self'; form-action 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'self'

要了解其限制。Cloudflare Pages 不会把该文件应用到由 Pages Functions 生成的响应上,最多允许 100 条响应头规则、每行最多 2,000 个字符,并且当同一响应头被应用两次时,会用逗号把值合并。Netlify 只对它自己提供的文件应用自定义响应头,不适用于代理内容、函数、边缘函数或服务器端渲染的页面;这些响应必须自行设置响应头。示例中没有包含 HSTS:添加之前,请先用 curl 确认平台已经发送了什么。

8. 分阶段上线,避开常见错误

  1. 添加 nosniff、Referrer-Policy、Permissions-Policy 和防嵌套保护,然后测试登录、结账和嵌入式组件。
  2. 把 CSP 草案以 Content-Security-Policy-Report-Only 发送。此时不会拦截任何内容;违规会显示在浏览器控制台中,如果策略指定了报告端点,也会发送到该端点。走一遍关键流程,包括登录后的页面,并据此调整策略。
  3. 用较短的 max-age 启用 HSTS,并分阶段提高。
  4. 强制执行 CSP。可以同时运行一个更严格的 Report-Only 策略,用来测试下一步收紧。
  5. 每次更改服务器、CDN 或框架后,重新运行 curl 检查。

常见错误:

  • 只有部分响应带有响应头。首页有,而 404 页面、API、静态文件或单独提供的登录页没有。
  • 在一次重定向中同时更改主机和协议。如果 http://example.com 直接重定向到 https://www.example.com,浏览器永远收不到 example.com 的 HSTS。应先在同一主机上重定向到 HTTPS,在该 HTTPS 响应上发送 HSTS,然后再重定向到 www。
  • CDN 覆盖了源站。CDN 或代理可能添加、替换、删除或重复响应头。请确定由哪一层负责每个响应头,并用 curl 确认结果。
  • 放行一切的 CSP。为了消除报错而塞满 'unsafe-inline'、'unsafe-eval' 或 * 的策略,能通过存在性检查,却几乎拦不住什么。
  • 过早使用 includeSubDomains。被遗忘、只能通过 HTTP 访问的子域名,会让已存储该策略的浏览器无法访问。

响应头只是其中一层。网站上线检查清单把它们与 DNS、TLS 和重定向放在一起,网站安全审计指南则涵盖登录、权限和可重复的审查流程。

9. 免费的上线准备快照如何检查响应头

免费的上线准备快照无需注册,对一个公开源站运行八项被动检查,其中一项读取 HTTP 安全响应头。它不是渗透测试,也不是六个领域的完整审计。

  • 它请求你所输入源站的首页(路径和查询字符串会被丢弃),最多跟随五次重定向,并对最终响应进行判断。不会请求其他页面。
  • 它主要检查是否存在。缺少 CSP、HTTPS 目标缺少 HSTS,以及没有防嵌套保护,都评为中等。Access-Control-Allow-Origin: *、X-Content-Type-Options 不是 nosniff、缺少 Referrer-Policy、任何 X-Powered-By 响应头,以及包含版本号的 Server 响应头,都评为低。不带版本号的 Server: nginx 只作为信息显示,不计为风险信号。该响应上设置的 Cookie 缺少 HttpOnly,或在 HTTPS 上缺少 Secure,评为中等;SameSite 不检查。
  • 另有一个加固评级(A 到 F),会结合 TLS 结果为已有的响应头打分,当 CSP 允许 'unsafe-inline'(或脚本允许 'unsafe-eval')时,给分会更少。Permissions-Policy 和 COOP 只计入此评级。
  • 快照中单独的 HTTPS 重定向检查取决于你输入的内容。输入裸域名或 https:// 地址时,它会把低于 180 天的 HSTS max-age 标为低(逐步提高期间这很正常),并查找页面 HTML 中的 http:// 引用(混合内容);它不测试 HTTP 到 HTTPS 的重定向。只有输入 http:// 地址时才会测试该重定向,并且这次运行会跳过 TLS 检查,也不会报告缺少 HSTS。

结果会显示一个分数、加固评级、风险信号和通过检查的数量、最多三条信号(风险优先),以及八项检查及其评级。对于它检查的那八个响应头,会显示评级,并在可用时显示观察到的值,而不是响应发送的所有响应头;其余响应头和其他页面请使用 DevTools 或 curl 检查。

如果想先打开这项检查,请使用免费的安全响应头检测工具:它运行同一个快照,并把响应头检查展开显示在其他七项检查之上。

常见问题

如何检查我网站的安全响应头?

在浏览器 DevTools 中打开网络面板,刷新页面,选择 HTML 文档并查看响应标头。在终端中,curl -sS -D - -o /dev/null https://example.com/ 会把它们打印出来。也请检查 http:// 地址、404 页面、API 响应和静态文件,因为它们的响应头往往不同。

应该先设置哪些 HTTP 安全响应头?

先从 X-Content-Type-Options: nosniff、Referrer-Policy、Permissions-Policy 和防嵌套保护开始,它们最不容易造成问题;之后仍要测试脚本、登录和嵌入式组件。然后以 Report-Only 模式测试 Content-Security-Policy,并分阶段提高 HSTS 的 max-age。

还应该发送 X-XSS-Protection 吗?

不应该。请删除它或发送 X-XSS-Protection: 0,并改用 Content-Security-Policy。它所控制的旧浏览器过滤器本身可能产生 XSS 漏洞。

已经使用 frame-ancestors,还需要 X-Frame-Options 吗?

在支持 CSP frame-ancestors 指令的浏览器中,它会取代 X-Frame-Options。X-Frame-Options: DENY 或 SAMEORIGIN 仍是旧浏览器合理的后备,前提是两个响应头允许相同的嵌套方式。不要使用 ALLOW-FROM:现代浏览器遇到它时会忽略整个响应头。

应该把我的域名加入 HSTS 预加载列表吗?

通常不需要。预加载提交网站 hstspreload.org 现在推荐使用 HSTS,但不推荐预加载,因为 Chrome 和 Safari 已经会把 HTTP 导航升级为 HTTPS。而且从列表中移除需要数月才能传达到用户。

设置了安全响应头,网站就安全了吗?

仅靠它们并不够。它们能减轻注入脚本、点击劫持、协议降级和 Cookie 窃取带来的损害,但无法修复有漏洞的代码、过时的软件或薄弱的访问控制。

来源与延伸阅读

  1. MDN:内容安全策略(CSP)指南developer.mozilla.org
  2. MDN:CSP frame-ancestors 指令developer.mozilla.org
  3. MDN:Strict-Transport-Security 响应头developer.mozilla.org
  4. MDN:Set-Cookie 响应头与 Cookie 属性developer.mozilla.org
  5. OWASP HTTP 响应头速查表cheatsheetseries.owasp.org
  6. OWASP Secure Headers 项目:推荐的响应头取值raw.githubusercontent.com
  7. HSTS 预加载列表提交hstspreload.org
  8. nginx:ngx_http_headers_module(add_header)nginx.org
  9. Cloudflare Pages:响应头developers.cloudflare.com
  10. Netlify:自定义响应头docs.netlify.com
Sitelemetry 团队

由 Sitelemetry 编辑团队编写。文末的一手资料可帮助你深入了解相关主题。

SITELEMETRY

把学到的知识用起来。

通过 Sitelemetry 探索网站的结构、设置与技术信号。