技术学院 / HTTP 协议与抓包实战

HTTP 协议与抓包实战

进阶

状态码、头部、缓存与重定向,用抓包工具看懂每一次请求。

6 章 · 20 节 · 约 10 分钟 · 更新于 2026-09-28 · 免费

章节
01 请求与响应的结构 1 小节
02 常见状态码逐个讲 4 小节
03 缓存头与 CDN 3 小节
04 重定向链追踪 1 小节
05 Cookie 与会话 1 小节
06 抓包工具上手 4 小节

请求与响应的结构

HTTP 报文是纯文本(HTTP/2、HTTP/3 改成了二进制帧,但语义完全一样),结构就四段:起始行、头部、空行、消息体。

http
POST /api/login HTTP/1.1
Host: www.ip438.com
User-Agent: curl/8.4.0
Content-Type: application/json
Content-Length: 34
Accept: application/json
 
{"user":"demo","password":"***"}
http
HTTP/1.1 200 OK
Date: Mon, 28 Sep 2026 03:12:44 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 27
Cache-Control: no-store
Set-Cookie: sid=abc123; Path=/; HttpOnly; Secure; SameSite=Lax
 
{"code":0,"msg":"success"}

请求行里的方法决定语义,几个必须分清的性质:

方法幂等安全可缓存说明
GET是是是只取数据,不该有副作用
HEAD是是是只要响应头,探活、查大小很好用
POST否否基本否提交,重复发会重复产生数据
PUT是否否整体替换,发两次结果一样
PATCH否否否局部更新
DELETE是否否删除
OPTIONS是是否跨域预检用的就是它

「幂等」不是「会不会改数据」,而是「发一次和发 N 次的最终状态是否相同」。这个性质决定了网关和客户端敢不敢自动重试——所以别把删除操作做成 POST 之外再做个 GET 入口,爬虫一扫就全没了。

几个必须认识的头

头方向作用
Host请求同一个 IP 上区分虚拟主机,HTTP/1.1 必填
Content-Type双向载荷类型,少了 charset 就会乱码
Content-Length / Transfer-Encoding: chunked双向body 多长,或者分块传
Accept-Encoding / Content-Encoding双向压缩协商,gzip / br
X-Forwarded-For / X-Real-IP请求代理层带上的真实客户端 IP,可伪造,别直接当身份用
Location响应3xx 要跳去哪
Server响应服务端软件,生产环境建议隐藏

完整对照表在 HTTP 请求头和响应头信息对照表;想直接构造一个请求发出去看结果,用 HTTP 请求测试;在浏览器里调通之后要落到代码里,用 curl 命令转代码把 curl 一键转成 PHP / Python / Go。

常见状态码逐个讲

状态码分五类:1xx 协议中间态、2xx 成功、3xx 重定向、4xx 你错了、5xx 我错了。日常真正需要分清的是这些:

2xx

码含义什么时候用
200 OK成功默认
201 Created已创建POST 创建资源后,配合 Location 指向新资源
204 No Content成功但没有内容删除成功、埋点上报
206 Partial Content部分内容断点续传、视频拖进度条,配合 Range

3xx

码是否保留方法是否永久场景
301否(POST 会变 GET)永久换域名、规范化 URL,搜索引擎会转移权重
302否临时临时跳转
303否(强制 GET)临时表单提交后跳到结果页,防重复提交
307是临时需要保持 POST 的临时跳转
308是永久需要保持 POST 的永久跳转
304 Not Modified——协商缓存命中,没有 body,见下一章

301 要慎用:浏览器会把它长期缓存下来,配错了访客本地清不掉缓存就一直跳错地方。上线新规则时先用 302 验证,确认无误再换 301。

4xx

码含义常见原因
400请求格式错JSON 写坏、参数类型不对
401未认证没带或带错凭证,响应应带 WWW-Authenticate
403已认证但无权限也常被 WAF、IP 黑名单用来挡人
404资源不存在
405方法不允许用 GET 去请求只收 POST 的接口
413载荷过大nginx client_max_body_size 没放开
429请求过多限流,响应应带 Retry-After

401 和 403 的区别是「你是谁没搞清」还是「知道你是谁但不让你进」。

5xx

码含义排查方向
500服务端内部错误看应用日志,通常是代码异常
502网关收到无效响应后端进程挂了 / 端口不对 / 后端崩溃
503服务不可用过载、维护中,应带 Retry-After
504网关超时后端太慢,或者网关超时设太短

502 和 504 是站长最常撞上的一对:502 多半是 php-fpm / 应用进程没起来或被打挂了,504 是它还活着但跑得太慢。先看后端进程在不在、端口通不通,再看慢查询。

全量码表在 HTTP 协议状态码;想确认线上某个 URL 现在返回什么,用 HTTP 状态检测;一批 URL 一次过,用批量网站巡检。

缓存头与 CDN

HTTP 缓存分两层:强缓存(本地直接用,不发请求)和协商缓存(发请求问一句「变了吗」,没变就回 304)。

强缓存

http
Cache-Control: public, max-age=31536000, immutable
指令含义
max-age=NN 秒内直接用本地副本
s-maxage=N只对 CDN / 共享缓存生效,优先级高于 max-age
public / private能否被 CDN 等中间节点缓存,带个人数据的一律 private
no-cache可以存,但每次都要去校验(不是不缓存)
no-store真的不许存,登录态、支付页用
immutable内容永不变,刷新时也别去校验

Expires 是 HTTP/1.0 的老写法,和 Cache-Control 同时存在时后者优先,留着只为兼容。

协商缓存

http
# 响应带上
ETag: "a1b2c3"
Last-Modified: Mon, 21 Sep 2026 10:00:00 GMT
 
# 下次请求带回去
If-None-Match: "a1b2c3"
If-Modified-Since: Mon, 21 Sep 2026 10:00:00 GMT

命中就回 304 Not Modified,没有 body,省的是带宽不是往返。ETag 比 Last-Modified 精确(后者只到秒,且多机部署时文件 mtime 可能不一致)。

站长该怎么配

资源建议
带指纹的静态文件 app.9f2c1.jsmax-age=31536000, immutable
不带指纹的 logo.pngmax-age=86400
HTML 页面no-cache 或很短的 max-age,保证能及时更新
登录后页面、接口no-store + private

给 HTML 配长缓存是最常见的事故:改了内容用户看不到,还没法让他们清缓存。静态资源要长缓存,必须靠文件名加指纹,而不是靠 ?v=2(部分中间节点会忽略查询串)。

CDN 还要多看两个头:Age(在缓存里待了多久)和 X-Cache: HIT/MISS(各家命名不同)。调试时用 curl -I 看一眼最快:

bash
curl -I https://www.ip438.com/static/app.css
curl -I -H 'Accept-Encoding: gzip, br' https://www.ip438.com/

压缩有没有真正生效,用网站 GZIP 压缩检测确认;整体加载表现用网站测速。

重定向链追踪

一次访问背后常常串着好几跳:http://example.com → https://example.com → https://www.example.com → https://www.example.com/index.html。每一跳都是一次完整的 DNS + TCP + TLS,对首屏和 SEO 都是纯损耗。

bash
# 跟随跳转并打印每一跳
curl -sIL https://ip438.com | grep -Ei '^(HTTP/|location:)'
 
# 只要最终地址和跳数
curl -so /dev/null -w '%{num_connects} 次连接  %{num_redirects} 次跳转  %{url_effective}\n' -L https://ip438.com

优化原则

  • 一次到位:把 http + 裸域 直接跳到 https + www(或反过来),不要先升 HTTPS 再补 www,那是两跳。
  • 规范化跳转用 301,让搜索引擎把权重转移过去;同时页面上配好 canonical。
  • 跳转目标要是绝对地址且协议正确,Location: //example.com 这种省协议写法在老客户端上会出问题。
  • 注意别绕成环:A→B→A 会让浏览器直接报 ERR_TOO_MANY_REDIRECTS,curl -L 默认最多跟 50 跳。

nginx 里的标准写法:

nginx
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://www.example.com$request_uri;
}
 
server {
    listen 443 ssl;
    server_name example.com;          # 裸域也要有证书,否则跳转前就报警告
    return 301 https://www.example.com$request_uri;
}

短链接是重定向的另一种用法,点之前先用短网址还原看清楚最终落点,能躲掉不少钓鱼。跳转链和最终状态也可以用 HTTP 状态检测一次看全;怀疑运营商在中间插东西,用网站劫持检测。

Cookie 与会话

HTTP 本身无状态,登录态靠 Cookie 带。服务端 Set-Cookie 下发,浏览器之后自动带上 Cookie 头。

http
Set-Cookie: sid=abc123; Path=/; Domain=example.com; Max-Age=86400; HttpOnly; Secure; SameSite=Lax
属性作用建议
Domain哪些域名带上它不写=只当前主机;写了会带给所有子域
Path哪些路径带上它一般 /
Expires / Max-Age存活时间都不写就是会话 Cookie,关浏览器即失效
HttpOnlyJS 读不到会话 Cookie 必须加,挡 XSS 偷 Cookie
Secure只走 HTTPS必须加
SameSite跨站请求带不带Lax 为现代浏览器默认值,能挡掉大部分 CSRF

SameSite 三个值的区别:Strict 完全不跨站带(从外站点链接进来会显示未登录);Lax 顶级导航的 GET 会带,POST 和子资源不带;None 都带,但必须同时加 Secure,否则浏览器直接丢弃——嵌到别家站点里的服务(在线客服、第三方支付)要特别注意这条。

会话怎么存

方案优点代价
Session ID + 服务端存储(Redis)能随时踢下线,Cookie 里没有敏感信息需要共享存储
JWT 等自包含令牌无状态,天然适合多服务签发后无法撤销,只能靠短有效期 + 刷新令牌兜底

JWT 的载荷只是 Base64URL 编码、不是加密,任何人都能解开看内容,所以里面不能放密码、手机号这类敏感数据。解一个令牌看看它到底装了什么,用 JWT 在线解析。

会话安全的三条底线:登录成功后换一个新的 session id(防会话固定)、退出时服务端真正销毁(不只是删 Cookie)、敏感操作二次校验。

抓包工具上手

排查 HTTP 问题,按「从外到内」的顺序换工具,别一上来就开抓包软件。

第一层:curl

bash
# 看完整的请求头与响应头(> 是发出的,< 是收到的)
curl -v https://www.ip438.com/ -o /dev/null
 
# 分段耗时,一眼看出慢在 DNS、TCP、TLS 还是服务端
curl -o /dev/null -s -w \
'dns:%{time_namelookup}  tcp:%{time_connect}  tls:%{time_appconnect}  ttfb:%{time_starttransfer}  total:%{time_total}\n' \
https://www.ip438.com/
 
# 跳过 DNS 直接指定后端 IP,用来确认是不是 CDN 调度的问题
curl -v --resolve www.ip438.com:443:1.2.3.4 https://www.ip438.com/

--resolve 这一招很实用:域名照常写(Host 和 SNI 都对),只是把解析结果按你说的来,这样可以单独测某一台源站或某个 CDN 节点。

第二层:浏览器 DevTools

Network 面板里最该看的几列:

  • Status:注意灰色的 (from disk cache) / (from memory cache),那是没发请求;
  • Waterfall 的分段:Queueing / Stalled 长说明连接数被占满或有排队,TTFB 长是服务端慢,Content Download 长是响应体太大或带宽不够;
  • 勾上 Disable cache 再复现,排除缓存干扰;
  • 勾上 Preserve log,否则跳转一次日志就清了——查登录、支付这类会跳转的流程必开。

第三层:代理抓包

Charles / Fiddler / mitmproxy 这类工具是在中间装一个代理,要解 HTTPS 必须把它的根证书装到系统/浏览器信任区。这也正说明了 HTTPS 的信任模型:只要有人能往你的信任链里塞证书,就能看到明文,所以别随便装来路不明的根证书。

移动端抓包还有一道坎:Android 7 之后 App 默认不信任用户安装的证书,需要目标 App 配合(debug 版 network_security_config);做了证书绑定(pinning)的 App 则完全抓不了。

第四层:tcpdump

到了怀疑网络本身的时候才用它:

bash
# 抓指定端口,不解析域名和服务名,存成文件拿回本地用 Wireshark 看
tcpdump -i any -nn port 443 -w /tmp/https.pcap
 
# 直接看 HTTP 明文(仅限未加密的 80 端口)
tcpdump -i any -nn -A -s 0 port 80

要区分「协议本身的问题」还是「实现的问题」,先确认协议协商对不对:HTTP/2 有没有真正启用、TLS 版本和加密套件是什么,用 HTTP/2 与 TLS 协议检测扫一眼,比在本地猜快得多。

关于本文

本文由 ip438查询网 原创,正文中的命令与配置片段可直接取用。 内容会随实践持续修订,发现错漏欢迎反馈。