⌘K 切换工具
NETWORK · HTTP

HTTP头信息检查工具

检查任何URL的HTTP响应头信息和安全状况。

server
http-headers

🌐 sitekits fetches the URL server-side (private/internal addresses are blocked). Response body is not retrieved.

§01 关于此工具

概述

响应头才是一个站点的行为真正被配置的地方——缓存、压缩、安全策略、CORS、重定向都在这里——而这也恰恰是你不打开 DevTools、不在里面找到正确的那个请求就看不见的部分。

这个工具在服务端请求一个 URL,把它的响应头返回给你,同时附上一路走到那里的完整重定向链。因为浏览器自身的安全模型把跨源响应头对 JavaScript 隐藏了起来,这是本站少数几个无法完全在你自己页面里运行的工具之一。

使用方法

  1. 输入一个 URL。协议部分可以省略——example.com 会被当作 https://example.com 来处理。
  2. 先读重定向链。其中每一跳都会显示它自己的 URL 和状态码。
  3. 然后再读最终那一份响应头。

重定向链通常就是答案本身

当某个东西慢下来、或者某个规范 URL 不对时,这条链就是你唯一能看见它的地方。每一跳都是一次完整的网络往返,而下面这几种常见的情形其实全都是可以避免的:

  • http://example.comhttps://example.comhttps://www.example.com 是两次重定向,而一次本来就够了。直接一跳跳到最终的主机和协议。
  • 站点迁移之后,一个 301 落到另一个 301 上,意味着两代规则都还在生效。
  • 重定向到一个又跳回来的 URL 就构成了环;工具在五跳之后停下,而不会跟着它一直绕。
  • 你本意是 301 却写成了 302,那是在告诉爬虫这次搬迁只是临时的,于是旧 URL 保住自己的排名,新 URL 什么也积累不到。

301/302307/308 之间的区别在于请求方法。历史上,客户端在跟随 301302 的时候会把 POST 变成 GET;而 307308 会保留原来的方法。如果一次表单提交在经过重定向之后行为变得很古怪,这就是第一件应该去查的事。

值得仔细读的那些响应头

响应头该看什么
cache-control静态资源上的 no-store 是在浪费带宽;HTML 上一个过长的 max-age 会让发布变得完全看不见
etag / last-modified它们缺失意味着每一次重新验证都要把整个正文再传一遍
content-encoding文本响应上缺 gzipbr,是你能拿到的最便宜的那份性能收益
vary一个 Vary: User-Agent 会把每个 CDN 缓存碎片化成上百份副本
strict-transport-securitymax-age 太短,或者缺了 includeSubDomains,会让第一次请求仍然可以被降级
content-security-policy各条指令具体是什么含义,见 CSP 生成器
x-content-type-options没有 nosniff,一个写错的 content-type 就可能被重新解释成脚本
access-control-allow-origin你的 fetch 失败的原因几乎总在这里,或者就是它根本不存在
set-cookie凡是携带会话的,都检查一下 SecureHttpOnlySameSite

serverx-powered-by 值得注意的理由正好相反:它们只是泄露了软件版本,除此之外什么作用都没有。把它们删掉算不上一项真正的安全控制措施,但也确实找不出任何理由继续留着它们。

这个工具刻意不做的事

处理器在建立连接之前,会先把 URL 对照一份包含私有、回环、链路本地和保留地址段的黑名单做校验,并在每一跳重定向上把这个检查重做一遍。没有这个逐跳检查,一个公网主机名就可以重定向到 127.0.0.1 或者某个云元数据地址,而请求会一路跟过去——一个检查响应头的服务,正是这样变成从外部探测别人内网的手段的。

它同样从不转发响应正文。连接只被读取到响应头为止,然后就直接丢掉。一个会返回任意正文的服务就是一个开放代理,不管它当初是不是抱着这个意图做的。

URL 里内嵌的凭据(https://user:pass@host/)在请求发出之前就已经被剥掉了,所以它们既不会被发给目标站点,也不会在重定向链里被回显出来。

示例

  • 一个你怎么都复现不了的 CORS 错误。 拿真实端点上的 access-control-allow-origin 和你代码所期待的那个值对比一下。响应头缺失和响应头写错,在浏览器里会产生完全相同的一条消息。
  • 一次死活出不来的发布。 看 HTML 上的 cache-controlage。CDN 正在提供一个缓存过的文档,这个解释比构建出错要常见得多。
  • 审计一次安全响应头的上线。 在线上主机上、而不是在你的配置文件里检查 strict-transport-securitycontent-security-policyx-content-type-options,因为 CDN 或 WAF 可能添加、剥离或者覆盖其中的任何一个。
  • 验证一次规范重定向。 确认这条链正好只用一跳,就落在你想要的那个主机和协议上。

说明

请求使用一个明确标明本工具身份的固定 User-Agent,并且在五秒之后超时。一个会屏蔽未知代理的站点会回一个 403,那是关于该站点配置的一个真实答案,而不是这里出了什么故障。

响应头名称按服务器发送时的原样显示。HTTP/2 要求在线路上使用小写名称,所以一个走 HTTP/2 的响应会显示 content-type,而一个 HTTP/1.1 的响应则可能显示 Content-Type。无论哪一种情况,这些名称都是大小写不敏感的。

如果你需要发送自定义请求头、指定某个方法,或者带上请求体,请改用 REST API 测试器——它从你自己的浏览器里发起请求,所以能做一些共享服务绝对不能做的事。

FAQ
这个工具会把我的 URL 发送出去吗?
会——URL 会发到 api.sitekits.dev,由它在服务端发起请求,只把响应头返回给你。浏览器读不到跨源的响应头,所以这个服务端环节绕不开。URL 在内存里处理,不会写入我们保留的任何日志或数据库。
为什么它只返回响应头而不返回正文?
因为返回正文会把它变成一个开放代理:任何人都能用它通过我们的服务器抓取任意页面,同时把自己的地址藏起来。处理器读完响应头就把连接丢掉,一个字节的内容都不转发。
我可以检查内网或 localhost 的 URL 吗?
不行。私有地址、回环地址、链路本地地址和保留地址都会被拒绝,而且这个检查在每一跳重定向上都会重做一次,这样一个公网主机名就没法把请求弹到内网地址上去。那是刻意设置的限制,不是缺陷——一个没有这道限制的抓取服务,会变成扫描别人网络的工具。
为什么我看到的结果和 curl 不一样?
最常见的原因是缓存或者内容协商。请求是从一个 Cloudflare 数据中心、用一个固定的 User-Agent 发出的,所以 CDN 可能提供一个不同的缓存对象,而一个按 Accept-Language 或 User-Agent 做区分的站点,回答也可能和你终端里看到的不一样。
它会跟随多少次重定向?
最多五次,并且会显示每一跳的 URL 和状态码。超过五次会返回一个错误,而不是无限地跟下去。