📘 第六章 HTTP首部
📄 一句话概括

Pragma 是 HTTP/1.0 的遗留字段,只有一个值有意义:no-cache。 它的存在只是为了照顾老到看不懂 Cache-Control 的中间件。

📝 本节和前面几节的关系

上一节讲时间,这一节讲一个几乎已经没用的字段。但它天天出现在老代码里,所以必须认得。

一、它的全部历史

HTTP/1.0 时代没有 Cache-Control,只有 Pragma: no-cache 这一个开关。 HTTP/1.1 引入 Cache-Control 之后,Pragma 就被保留了,但规范明确说它「只作为向后兼容而存在」。 所以这个字段只有一种用法是对的:和 Cache-Control: no-cache 一起发, 让那些只认 1.0 的老中间件也照办。 其他所有值都是未定义的。 写 Pragma: no-store 或者 Pragma: max-age=0 不会有任何效果——它们不在规范里。

二、两个都发的时候听谁的

Demo 试了四种组合,结论很清楚: | 发的东西 | 实际判定 | |---|---| | 只有 Pragma: no-cache | 没有 Cache-Control,退回按 Pragma 处理 | | 只有 Cache-Control: no-cache | 按 Cache-Control:每次都验证 | | 两个都是 no-cache | 按 Cache-Control:每次都验证 | | Pragma: no-cache 加 Cache-Control: max-age=600 | Pragma 被忽略,按 max-age 缓存 | 最后一行是关键:只要出现了 Cache-Control,Pragma 就完全失效, 哪怕 Cache-Control 里根本没有 no-cache。 这条规矩很多人不知道, 于是写出 Pragma: no-cache 加 Cache-Control: max-age=3600 这种自相矛盾的组合, 以为前者能兜住后者,实际完全无效。

三、为什么今天还在用它

因为老系统多。2010 年前后的大量框架、代理、甚至一些企业内网的网关,只认 Pragma。 所以你会看到「双保险」的写法:两条都发,一条给新设备,一条给老设备。 这在过渡期是合理的,但新项目不该再发 Pragma。 读代码时要有这个意识:看到 Pragma 就知道这段代码要么很老,要么在兼容很老的客户端。 它是个时间戳,标记着这段代码的年龄。

四、动手 Demo:四种组合,看谁说了算

分四次发:只有 Pragma、只有 Cache-Control、两个都发、以及 Pragma 配一个相反的 max-age, 看服务端的判定怎么变。

cc = hd.get(u'cache-control', u'')
pg = hd.get(u'pragma', u'')
if cc:
    if u'no-cache' in cc.lower():
        verdict = u'按 Cache-Control 处理:每次都验证'
    else:
        verdict = u'有 Cache-Control(%s),Pragma 被忽略' % cc
elif u'no-cache' in pg.lower():
    verdict = u'没有 Cache-Control,退回按 HTTP/1.0 的 Pragma 处理'
else:
    verdict = u'没有缓存指令'

实跑输出:

--------------------------------------------------------------------
Pragma: no-cache 是个历史包袱
--------------------------------------------------------------------
  【只发 Pragma: no-cache】
    服务端收到的首部         host | pragma
    实际判定                 没有 Cache-Control,退回按 HTTP/1.0 的 Pragma 处理

  【只发 Cache-Control: no-cache】
    服务端收到的首部         cache-control | host
    实际判定                 按 Cache-Control 处理:每次都验证

  【两个都发】
    服务端收到的首部         cache-control | host | pragma
    实际判定                 按 Cache-Control 处理:每次都验证

  【发 Pragma: no-cache 加 max-age】
    服务端收到的首部         cache-control | host | pragma
    实际判定                 有 Cache-Control(max-age=600),Pragma 被忽略


  Pragma 是 HTTP/1.0 的产物,只有一个值有意义:no-cache。
  它的存在只为了一个目的:让老到看不懂 Cache-Control 的中间件也照办。
  HTTP/1.1 里如果两个都出现,按标准 Cache-Control 赢,Pragma 被忽略。
  所以今天写新代码不该再发 Pragma,但读代码时要认得它——
  老系统里到处是「Pragma: no-cache 加 Cache-Control: no-cache」的双保险写法。

只有 Pragma: no-cache 时,服务端拿不到任何 Cache-Control, 只能退回按 1.0 的规矩办——这是它唯一还有用的场合。 只有 Cache-Control: no-cache 时,正常判定为「每次都验证」。 两个都发,结果和只有 Cache-Control 一样。Pragma 是冗余的,但不会造成错误。 最后一组最能说明问题:Pragma: no-cache 配 Cache-Control: max-age=600, 判定是「有 Cache-Control,Pragma 被忽略」。那 600 秒的缓存照旧生效——Pragma 一点用都没起。

30 秒实验

  1. 把最后一组反过来:Cache-Control: max-age=0 配 Pragma: no-cache,看判定变不变。
  2. 发一个 Pragma: no-store,看服务端认不认这个值。
  3. 想一想:为什么「双保险」这种写法在过渡期之后应该被清理掉?

常见误区

  1. 「Pragma 是 Cache-Control 的别名」——只有 no-cache 一个值重叠。
  2. 「两个都发就以 Pragma 为强」——正好相反,Cache-Control 赢。
  3. 「Pragma: no-store 能禁止存储」——那个值不存在,没有效果。
自测三问(点开对答案) 1. `Pragma` 只有一个有意义的值是什么? —— `no-cache`。其他值都是未定义的。 2. 同时出现 `Pragma: no-cache` 和 `Cache-Control: max-age=600`,听谁? —— 听 `Cache-Control`。只要它出现,`Pragma` 就完全失效。 3. 为什么新项目不该再发 `Pragma`? —— 它只为兼容 HTTP/1.0 的老中间件而存在,现代实现都认 `Cache-Control`。

遗留字段说完了。下一个字段解决一个真问题:正文发完了才知道的信息往哪儿放。

相关笔记