超文本传输协议 -- HTTP/1.1(二)

超文本传输协议 -- HTTP/1.1(二)

13 HTTP中的缓存

HTTP1通常用于分布式信息系统,这些系统能够使用响应缓存来改善性能。HTTP/1.1包含一些打算尽可能地使缓存工作的元素。因为这些元素与HTTP协议的其它方面是紧密联系的,并且它们之间相互影响,所以分别地从方法、头和响应码的详细描述来说明HTTP的基本缓存设计是很有用的。

缓存如果不能显著地改善性能就没有什么用了。HTTP/1.1缓存的目标就是消除多数情况下发送请求的需要,并且消除其它情况下发送整体响应的需要。前者为许多操作减少了网络来回传输的次数,为了达到这个目的,我们使用了“到期”机制(参考13.2节)。后者减少了网络带宽的需要,我们使用了“验证”机制来实现这个目的(参考13.3节)。

对性能、可用性和失连操作的需求需要我们能够缓和语义透明的目的。HTTP/1.1协议允许源服务器、缓存和客户端明确地减少必要的透明度。然而,由于非透明操作会使非专家用户感到迷惑,并且可能与某些服务器应用不兼容(比如商品订购系统),HTTP/1.1协议要求用以下方式缓和这些透明度:

  • 当被客户端或源服务器缓和时,只有明确的协议层请求能够被缓和透明度;
  • 当被缓存或客户端缓和时,只有用一个给最终用户的明确的警告来缓和透明度;

因此,HTTP/1.1协议提供了以下这些重要的元素:

  1. 协议特性:在各方都需要时提供完全的语义透明性;
  2. 协议特性:允许源服务器或用户代理明确地请求和控制非透明操作;
  3. 协议特性:允许缓存将警告附加到不能保持所请求的语义透明性近似值的响应上;

一个基本的原理就是客户端必须有可能检测到任何潜在的语义透明性的缓和。

注意:服务器、缓存或客户端实现者可能会面对一些没有在本协议内明确讨论过的设计决定。如果有决定可能会影响语义透明性,则实现者应该是在保持语义透明时有失误,除非有仔细和完整的分析表明打破透明性能够得到重大地受益。

13.1 警告模式

13.1.1 缓存正确性

一个正确的缓存系统必须用缓存中最新的响应结果去响应请求,这个响应结果需要满足以下条件之一:

  1. 已经用源服务器可能返回的响应进行了等值检查(通过使源服务器的响应重新生效,参考13.3节);
  2. ”足够新鲜“(参考13.2节)。在默认情况下,这意味着它满足客户端、源服务器和缓存的最小新鲜度限制要求(参考14.9节);如果源服务器这样指定,则它只是源服务器的新鲜度要求;
    如果存储的响应不是”足够新鲜“,不满足客户端和源服务器两者最严格的新鲜度要求,那么在仔细地考虑情况之后,缓存仍然可能返回这个响应,并且附加上合适的警告头(参考13.1.5节和14.46节),除非这样的响应是被禁止的(例如,”禁止存储“的缓存指令,”禁止缓存“的缓存请求指令;参考14.9节)。
  3. 它是一个合适的304(未修改),305(代理转发)或者错误响应消息(4xx 或 5xx)。

如果缓存系统不能和源服务器通信,那么一个正确的缓存系统应该按照以上所述进行响应,如果这个响应能够正确地服务的话;如果不能,则必须返回一个能够表明通信失败的错误或者警告。

如果缓存系统接收到了一个响应(要么是整个响应,要么是304(未修改)响应),并且会将这个响应正常地转发给请求客户端,同时接收到的响应不再是最新的,那么缓存应该不添加新警告(但是不会移除任何已经存在的警告头)地转发给请求客户端。缓存不应该简单地试图让响应生效,因为这个响应在传输的过程中变得不那么新鲜了;这样做还可能导致一个无限循环。接收到一个没有警告的旧响应的用户代理可能会向用户显示一个警告。

13.1.2 警告

无论什么时候,如果缓存返回了一个既不是第一手也不是“足够新鲜”的响应(参考13.1.1节中条件二的描述),那么它必须使用警告通用头(Warning general-header)对此响应产生的影响附加一个警告。警告头和当前警告信息都在14.46节中有具体描述。

警告允许客户端采取合适的行动。

警告也可能用作其它的目的,包括缓存相关的和其它的用途。使用警告(而不是错误状态代码)将这些响应与真正的失败区分开来。

警告被分配了三个数字警告码。第一个数字表明,当一个响应从缓存中被成功激活时,警告是否必须或者禁止从缓存中删除:

  • 1xx警告。描述响应的新鲜度或激活状态的警告,所以在成功激活后,必须删除。只有当启动一个缓存条目时,缓存才可能会生成1xx警告码。警告码禁止由客户端生成。
  • 2xx警告。描述实体体或实体头的某些方面的警告,这些体或头没有被激活修正(例如,实体体的有损压缩),所以在响应激活后,禁止删除这些警告。

参考14.46节获取这些警告码定义的详细信息。

HTTP/1.0的缓存系统会缓存响应中的所有警告,不会删除第一类警告(1xx)。传输给HTTP/1.0的警告中携带了一个额外的警告日期(warning-date)字段,这个字段防止了以后的HTTP/1.1接收者错误地相信这些过期的缓存警告。

警告可能会携带一个警告文本。这个文本可能使用任何合适的自然语言(可能基于客户端的Accept头字段),并且包含一个可选的字段,这个字段表明了这段文本使用的字符集。

响应中可能附加多重警告(要么是源服务器,要么是缓存系统),包括使用相同警告码的多重警告。例如,服务器可能提供同时使用英语和巴斯克语(Basque)文本的相同警告。

当响应中附加了多重警告时,向用户展示所有的警告可能是不切实际或不合理的。本HTTP版本没有为使用何种顺序和显示哪个警告指定严格的优先级规则,只是给出了一些启发。

13.1.3 缓存控制机制

HTTP/1.1中的基本缓存机制(服务器指定的过期时间和验证器)对于缓存来说都是隐式指令。在一些情况下,服务器或客户端可能需要向HTTP缓存提供显示指令。我们使用缓存控制头(Cache-Control header)来达到这个目的。

缓存控制头允许客户端或服务器在请求或响应中传输各种各样的指令。这些指令通常重写了默认的缓存算法。作为一种通用规则,如果在头字段值之间存在任何明显的冲突,那么就会启用最严格的解释(就是说,最符合语义透明性的那个)。然而,在一些情况下,明确指定的缓存控制指令会同时削弱语义透明性(例如,“max-stale"或者"public”)。

缓存控制指令在14.9节有详细描述。

13.1.4 显式用户代理警告

许多用户代理为用户重写基础缓存机制提供可能。例如,用户代理可能允许用户指定已缓存的实体永久失效(甚至显式地使其过期)。有些用户代理会习惯性地添加“Cache-Control:max-stale=3600”头字段到每一个请求之上。用户代理不应该默认使用非透明行为,或者导致异常无效缓存的行为,但是允许让用户通过明确地行为显式地进行配置。

如果用户已经重写了基本缓存机制,当显示的信息可能不满足服务器的透明性要求(通常,如果被显示的实体已知为已经老旧)时,用户代理应该显式地向用户指出。通常协议本身正常地允许用户代理自己决定响应是否老旧,只有响应真正老旧的时候才需要向用户指出。向用户指出时不必是一个对话框,它也可以是一个图标(例如,一张死鱼图)或者其它一些指示器。

如果用户已经重写了缓存机制,但是使用了一种会以非正常的方式减少缓存效力的行为,则用户代理应该持续地向用户表明这种状态(例如,通过显示一张燃烧着的钱的图片),以至于用户不会疏忽地消耗过多的资源或者遭受太多的延迟。

13.1.5 规则和警告的例外

在某些情况下,缓存的操作者可能会选择配置缓存返回陈旧的响应,甚至当没有被客户端请求时。不应该轻易地做出这样的选择,但是基于可用性和性能的原因又是必须的,特别是当缓存和源服务器的连接不稳定时。缓存无论何时返回了陈旧的响应,都必须对其标记,通过(使用警告头)使客户端软件警示用户可能存在一个潜在的问题。

允许用户代理采取一些行动来获取第一手或最新的响应。由于这个原因,如果客户端显示地请求了第一手或最新的响应,缓存不应该返回一个陈旧的响应,除非技术上达不到要求或政策上的原因。

13.1.6 客户端控制的行为

在源服务器(在较小的程度上,中间缓存,是由它们对响应年龄的贡献决定的)是过期信息的主要来源时,在某些情况下,客户端可能需要控制缓存的选择,是否在没有激活缓存的响应时就返回它。客户端通过使用缓存控制头的几个指令来达到这个目的。

客户端请求可能会指定它希望接收到的未激活响应的最大年龄;当指定为0时,强制缓存激活所有的响应。客户端也可以指定响应过期前的最小保留时间。这些选项约束了缓存的行为,因此不能进一步地缓和语义透明性。

客户端也会指定它可以接收陈旧的响应,直到最大次数。这样做放松了缓存的约束,因此可能会侵犯了源服务器指定的在语义透明上的约束,但是对于支持断线操作或连通性差情况下的高可用来说确是必须的。

13.2 过期模式

13.2.1 指定服务端过期

当缓存能够完全避免向源服务器发送请求时,HTTP缓存是工作得最好的。对源服务器来说,避免请求的主要方式就是提供一个未来的过期时间,这个过期时间表明一个响应可能被直接用于后来的请求。用另一句话来说就是,缓存在非首次连接服务器时能够返回一个新鲜的响应。

我们期望服务器会给响应分配未来的过期时间,我们相信实体不太可能发生变化,用一种语义上有意义的话来说就是,在过期时间到达之前。只要服务器上的过期时间是谨慎选择的,通常情况下还能保护语义透明性。

过期机制只适用于来自缓存的响应,而不是立即转发到请求客户端的第一手响应。

如果源服务器希望强制一个语义上透明的缓存去验证每一个请求,它就会分配一个已经过去的明确的过期时间。这表明响应总是过期的,因此缓存应该在将它用于后来请求的时候进行验证。想要强制激活的更多严格的方式,请参考14.9.4节。

如果源服务器希望强制任意的HTTP/1.1缓存(不管缓存如何配置)去验证每一个请求,那么就应该使用“must-revalidate”缓存控制指令(参考14.9节)。

服务器指定显示的过期时间有两种方式:一是使用Expires头字段;二是使用缓存控制头中的max-age指令。

过期时间不能用来强制用户代理刷新显示或重载资源;它在语义上只适用于缓存机制,并且这样的机制在访问资源的请求初始化以后,只需要检查资源的过期状态。参考13.13节以了解缓存和历史机制的差异。

13.2.2 启发式过期

由于源服务器并不总是提供显式的过期时间,HTTP缓存通常会分配一个启发式的过期时间,使用的算法就是使用其它的头字段值(例如Last-Modified时间)来估算一个看似可信的过期时间。HTTP/1.1规范没有提供指定的算法,但是在它们的结果上面强加了一个最坏打算的约束。因为启发式过期时间可能违背了语义透明性,它们应该谨慎地使用,我们鼓励源服务器尽可能地提供显式的过期时间。

13.2.3 年龄计算

为了知道缓存条目(cache entry)是否有效,缓存需要知道它的年龄是否超过了它的新鲜度寿命。我们会在13.2.4节中讨论怎么计算后者;本节描述了如何计算响应或缓存条目的年龄。

在本次讨论中,我们使用了术语“now”来表示“在主机执行计算时时钟的当前值”。使用HTTP的主机,特别是运行源服务器和缓存的主机,应该使用NTP(Network Time Protocol,网络时间协议)或一些类似的协议来同步它们的时钟,以达到全球精确时间标准。

HTTP/1.1要求源服务器发送Date头,如果可能,每一个响应中都要携带响应生成的时间(参考14.18节)。我们使用术语“date-value”来表示Date头字段的值,用一种适用于算术操作的形式。

本质上,Age头字段值是由响应在从源服务器开始的路径上的每一个缓存中驻留的时间的总和,在加上网络路径中传输的时间汇总而成。

我们使用术语“aga-value”来表示Age头字段的值,用一种适用于算术操作的形式。

响应的年龄能够用两种完全独立的方式计算出来:

  • age_value = now - (date_value) 。需要本地时钟能够很好很合理地同步为源服务器的时钟。如果计算结果为负,则替换为0。
  • age-value。需要响应路径上的所有缓存都是HTTP/1.1的实现。

考虑到我们已经有了两种独立的方式计算接收到的响应的年龄,我们可以将这两种结果结合起来:corrected_received_age = max(now - date_value, age_value)

只要我们同步了时钟或全部采用了HTTP/1.1协议,就能到一个可靠的结果。

由于网络强加的延迟,可能会在服务器生成响应的时间与在下一个出站缓存或客户机接收响应的时间之间有一定的间隔。如果不正确,此延迟会导致错误的低年龄。

由于导致返回年龄值的请求必须在生成该年龄值之前就已经初始化,所以我们可以通过记录请求初始化的时间来纠正网络造成的延迟。然后,当接收到年龄值时,必须将其解释为与发起请求的时间有关,而不是与接收响应的时间有关。此算法比较保守,它不管中间经历了多少延迟。所以,计算公式:
corrected_initial_age = corrected_received_age + (now - request_time)

其中,request_time(根据本地时钟)就是引出此响应的请求的时间。

当缓存接收到响应时,年龄计算算法汇总:
/*
* age_value: is the value of Age: header received by the cache with this response.
* date_value: is the value of the origin server’s Date: header
* request_time: is the (local) time when the cache made the request that resulted in this cached response
* response_time: is the (local) time when the cache received the response
* now: is the current (local) time
*/
apparent_age = max(0, response_time - date_value);
corrected_received_age = max(apparent_age, age_value);
response_delay = response_time - request_time;
corrected_initial_age = corrected_received_age + response_delay;
resident_time = now - response_time;
current_age = corrected_initial_age + resident_time;

缓存条目的当前年龄是通过加上自缓存条目被源服务器最后验证为corrected_initial_age的时间量。当响应是由缓存条目生成的,缓存必须包含一个单个的Age头在响应中,该头字段的值等于缓存条目的current_age

响应中Age头字段的存在暗示着响应不是第一手的。然而,反过来就不对了,缺少Age头字段的响应并不意味着响应就是第一手的,除非请求路径上的所有缓存都遵守HTTP/1.1规范(例如,旧HTTP缓存就没有Age头字段)。

13.2.4 过期计算

为了判定响应是否过期,我们需要比较它的新鲜度寿命和年龄。年龄的计算在13.2.3节中有描述;本节描述了怎样计算新鲜度寿命和响应是否已经过期。在接下来的讨论中,值都是以任何方便算术操作的形式表示。

我们使用术语”expires_value“表示Expires头字段的值。使用术语”max_age_value“表示Cache-Control头中的”max-age“指令携带的秒数(参考14.9.3节)。

max-age指令的优先级比Expires高,所以如果响应中提供了max-age,计算就很简单:
freshness_lifetime = max_age_value

否则,如果响应中提供了Expires,计算公式为:
freshness_lifetime = expires_value - date_value

注意,这两种计算方式都不容易发生时钟偏移,因为它们所有的信息都来自于源服务器。

如果响应中没有Expires、Cache-Control:max-age、Cache-Control:s-maxage(参考14.9.3节),并且不包含其它的缓存约束,那么缓存就会用启发式的方式计算出一个新鲜度寿命时间。缓存必须附加113警告给任何未被附加此警告且年龄超过24小时的响应。

而且,如果响应含有Last-Modified时间,则启发式过期时间应该不超过从那时起到现在间隔时间的一部分,一个通常的”一部分“设定是10%。

计算响应是否过期的公式十分简单:
response_is_fresh = (freshness_lifetime > current_age)

13.2.5 消除过期值歧义

由于过期值是乐观分配的,对两个缓存来说,有可能包含的对同一资源的新鲜值是不一样的。

如果执行检索的客户机接收到一个请求的非第一手响应,而该请求在其自己的缓存中已经是新鲜的,并且其现有缓存条目中的日期头比新响应中的日期更新,那么客户机可能会忽略该响应。如果是这样,客户端可能使用”Cache-Control: max-age=0“指令(参考14.9节)重试请求,来强制检验源服务器。

如果缓存对具有不同验证器的相同表示有两个新的响应,则它必须使用Date头字段值是最近的响应。这种情况可能会上升因为缓存正在汇聚来自其它缓存的响应,或者因为客户端已经请求明显的新鲜的缓存条目的重载或激活。

13.2.6 消除多种响应歧义

因为客户端可能正在通过多种渠道接收响应,以至于一些响应流过一个缓存集,而另一些响应流过不同的缓存集,客户端可能接收到的响应的顺序与源服务器发出的不同。我们想要客户端使用最近生成的响应,尽管旧响应仍然明显地有效。

不管是实体标签还是过期值都不能强制响应排序,因为有可能后来的响应的携带了一个更早的过期时间。Date字段的值在一秒之内是有序的。

当客户端尽力激活缓存条目,并且接收到的响应包含的Date字段的值比已经存在的条目中的值还要老时,那么客户端应该无条件地重试请求,并且包含Cache-Control: max-age=0来强制任何中间缓存用源服务器来验证它们的副本,或者Cache-Control: no-cache来强制任何中间缓存从源服务器获取一份新的副本。

如果Date字段值是相等的,那么客户端可能使用其中的任何一个(或者可能,如果足够谨慎的话,请求一个新的响应)。如果过期时间重叠,服务器禁止依赖于客户机才能够在同一秒内生成的响应之间做出确定性的选择。

13.3 验证模型

当缓存有一个老旧的条目,并且缓存想用它去响应客户端请求,那么缓存首先不得不用源服务器进行检查(或可能是一个有新鲜响应的中间缓存)以便查看这个缓存条目是否还可用。我们将这称之为”验证“缓存条目。如果缓存条目可用的话,我们不想支付重复传输整个响应的开支;如果缓存的条目无效,我们不希望支付额外的往返开销,HTTP/1.1协议支持有条件的方法的使用。

支持条件性方法的关键协议特性就是与”cache validators“有关的。当源服务器生成了整个响应之后,它就会附加某种验证器在其中,这个验证器与缓存条目保存在一起。当客户端(用户代理或代理缓存)制造了一个对有缓存条目的资源的条件性请求时,就已经包含了一个关联的验证器在请求之中。

随后,服务器就会将请求中的验证器和缓存条目中的验证器进行比对。如果匹配上了(参考13.3.3节),就会响应一个特殊的状态码(通常是304(Not Modified)),并且没有实体体;否则,就会返回整个响应(包括实体体)。因此,当验证器匹配上时,我们避免传输整个响应;当不匹配时,我们避免额外的往返开销。

在HTTP/1.1中,有条件性的请求与请求同一资源的普通请求看起来恰好是一样的,除了它携带了一个特殊的头(包含验证器),这个头含蓄地将方法(通常,GET)转换为一个条件性的方法。

协议包含了缓存验证条件的积极和消极的两个方面。也就是说,当且仅当验证器匹配或当且仅当没有验证器匹配时,可以请求执行方法。

注意:缺少验证器的响应仍然可能会被缓存,并为缓存服务直到过期,除非被缓存控制指令明确禁止。然而,如果缓存中的实体没有验证器,则不能进行条件性的恢复。没有验证器意味着在响应过期之后,无法进行刷新。

13.3.1 Last-Modified日期

Last-Modified实体头字段值经常被用作缓存验证器。用简单的术语来说,如果自从Last-Modified以来,实体并未发生改变,则缓存条目被认为是有效的。

13.3.2 实体标签缓存验证器

ETag响应头字段值,一个实体标签,专为“不透明的”缓存验证器准备的。这个标签在某些情况下可能有更可靠的验证,例如不方便存储修改日期时,HTTP日期值一秒分辨(the one-second resolution)不充足时,源服务器希望避免某种由于修改日期的使用而导致的悖论时。

实体标签在3.11节中有详细描述。与实体标签一起使用的头字段描述于14.1914.2414.2614.44节中。

13.3.3 弱和强验证器

由于源服务器和缓存都会比较2个验证器以判断它们是否代表相同或不同的实体,人们通常希望如果实体(实体体或任何一个实体头字段)以任何方式发生改变时,其关联的验证器也跟着改变。如果真是这样,那么我们就称这个验证器是一个“强验证器”。
‘’
然而,可能有这样的情况:服务器更喜欢只有在语义上的重大改变而不是实体中非重要方面发生改变时去改变验证器。一个不总是随着资源的改变而改变的验证器称之为“弱验证器”。

实体标签一般都是“强验证器”,但是协议提供了一种机制,会将实体标签标记为“弱”。我们可以认为强验证器是随着实体的位数的变化而变化的验证器,而弱验证器则是在实体的意义发生变化时发生变化的验证器。或者,
我们也可以认为强验证器是作为某个指定实体的标识符的一部分,而弱验证器则是作为一系列语义上相等的实体的标识符的一部分。

注意:一个强验证器的例子就是一个每当实体改变而跟着增长的稳定存储的整数。
一个实体的修改时间,如果是由one-second resolution表示的,就是一个弱验证器,因为资源可能在一秒之内改变两次。
对弱验证器的支持是可选的。然而,弱验证器在缓存相等的对象时效率很高。例如,每隔几天或几周更新一次的命中计数器可能就足够好了,并且在此周期内的任何值都可能“足够好地”相等。

验证器的使用要么是当客户端生成请求之后,将验证器放在验证头字段中,要么是在服务器比较两个验证器时。

强验证器在任何上下文中都可用。弱验证器只有在不依赖于精确相等的实体的上下文中才有用。例如,其中任何一个对于全实体条件性GET请求都是有用的。然而,对于子区间检索来说,只有强验证器才是有用的,因为否则客户端就会以内部非一致性实体而告终。

客户端可能以弱验证器或强验证器发布简单的(非子区间)GET请求。客户端禁止在其它格式的请求中使用弱验证器。

HTTP/1.1协议在验证器上定义的唯一的功能就是用于比较。目前存在两个验证器比较功能,它们依赖于用于比较的上下文是否允许弱验证器的使用与否。

强比较功能:是为了比较是否相等。两者在任何方面都必须是完全一致的,并且不能是弱验证器。
弱比较功能:是为了比较是否相等。两者在任何方面都必须是完全一致的,但是它们两者或其中任何一个都可能在不影响结果的情况下被标记为“弱”验证器。

实体标签就是强验证器,除非它被显式地标记为“弱”。3.11节给出了实体标签的语法。

Last-Modified头字段,当在请求中被用作验证器时,隐式地被当作弱验证器,除非存在推断出它是强验证器的可能,使用以下规则:

  • 源服务器正将此验证器与实体的实际当前验证器比较,并且,
  • 源服务器可靠地知道关联的实体不会在提供的验证器覆盖期间改变两次。

或者

  • 验证器即将被客户端用在If-Modified-Since或If-Unmodified-Since头中,因为客户端在所关联的实体上有一个缓存条目,并且,
  • 缓存条目包含一个Date值,此值作为源服务器发送原始响应的时间,而且,
  • 提供的Last-Modified时间至少比Date值早60秒。

或者

  • 中间缓存正将此验证器与存储在它的缓存条目中实体的验证器作比较,而且,
  • 缓存条目包含一个Date值,此值作为源服务器发送原始响应的时间,而且,
  • 提供的Last-Modified时间至少比Date值早60秒。

这个方法依赖于这样的事实:如果源服务器在同一秒内发出了2个不同的响应,但是这两个响应都有相同的Last-Modified时间,那么至少这些响应中的某一个的Date值是与其Last-Modified时间是相等的。强制60秒限制是为了防止一种可能性,这种可能性导致Date和Last-Modified值在不同时间的生成或者在响应的准备过程中的不同时间生成。一种实现是可能使用比60秒更长的时间,如果认为60秒时间太短的话。

如果客户端想要在一个只有Last-Modified时间且没有不透明验证器的值上执行子区间检索,它只可能在此Last-Modified时间处于本节描述的属于强验证器时才会这样做。

接收条件性请求的缓存或源服务器(而不是整个主体GET请求)必须使用强比较功能来评估条件。

这些规则允许HTTP/1.1的缓存和客户端安全地在来自于HTTP/1.0的值上执行子区间检索。

13.3.4 何时使用实体标签和Last-Modified日期的规则

关于各种各样的验证器类型应当在何时使用、为何种目的使用,我们为源服务器、客户端和缓存采取了一系列的规则和建议。

HTTP/1.1 源服务器:

  • 应该发送一个实体标签验证器,除非无法生成。
  • 可能使用弱实体标签替代强实体标签,如果性能考虑支持弱实体标签的使用,或者无法生成强实体标签。
  • 如果可以,应该发送一个Last-Modified值,除非在If-Modified-Since头使用使用这个值会导致语义透明性崩溃的风险,这种风险可能导致严重的问题。

换句话说,对HTTP/1.1源服务器最好的行为就是发送强实体标签和Last-Modified时间。

为了合法,强实体标签必须随着所关联的实体以任何方式的变化而变化。弱实体标签应该随着在所关联的实体以语义上的重大方式变化时也跟着变化。

注意:为了提供语义透明的缓存,源服务器必须避免在两个不同的实体上使用相同的强实体标签,或者将一个指定的弱实体标签重复使用于两个语义上不同的实体。缓存条目必须坚持任意的长周期,无视过期时间,因此不应该期待缓存永远不会尝试使用在过期某个点获取的验证器去验证实体。

HTTP/1.1 客户端:

  • 如果源服务器已经提供了一个实体标签,则必须在任何条件性缓存(cache-conditional)的请求(使用If-Match或If-None-Match)中使用此实体标签。
  • 如果源服务器只提供了一个Last-Modified时间,则应该在非子区间条件性请求(使用If-Modified-Since)中使用它的值。
  • 如果HTTP/1.0源服务器只提供了一个Last-Modified时间,则可能在子区间条件性请求(使用If-Unmodified-Since)中使用它的值。用户代理应该提供一种方式来关闭这种功能,万一出现困难的话。
  • 如果源服务器同时提供了实体标签和Last-Modified时间,则应该在条件性缓存请求中使用这两个验证器。这允许了HTTP/1.0和HTTP/1.1的缓存进行合适地响应。

一个HTTP/1.1源服务器,接收包含Last-Modified时间和一个或多个实体标签作为缓存验证器的条件性请求,禁止返回一个本地缓存的响应给客户端,除非缓存的响应与请求中所有条件性头字段保持一致。

注意:这些规则背后的一般原则是,HTTP/1.1服务器和客户端应该传输尽可能多的响应和请求中可用的非冗余信息。接收到这些信息的HTTP/1.1系统将会为它们接收到的验证器制定最保守的假设。
HTTP/1.0客户端和缓存将会忽略实体标签。一般地,被这些系统接收和使用的last-modified时间会支持透明性和高效缓存,因此HTTP/1.1源服务器应该提供Last-Modified时间。在HTTP/1.0系统将Last-Modified用作验证器时导致了严重问题这样罕见的情况下,HTTP/1.1源服务器应该不提供它。

13.3.5 非验证条件

实体标签背后的原理就是只有服务创建者对资源的语法知道得足够好,才能选择一个合适的缓存验证机制,并且,任何比字节等值(byte-equality)更复杂的验证器比较函数的规范都会造成大麻烦。因此,任何其它头字段(除了Last-Modified,为了兼容HTTP/1.0)的比较都不会用来验证缓存条目。

13.4 响应的可缓存性

除非缓存控制指令明确地约束,否则缓存系统总是会存储成功的响应(参考13.8节)作为缓存条目,会在当它是新鲜的时候不经过验证就直接返回,也可能在通过成功验证后返回。如果既没有缓存验证器也没有一个明确的与响应有关的过期时间,我们就不希望缓存它,但是某些缓存会违反这个期望(例如,只有很少或者没有网络连通性可用时)。客户端能够经常检测到来自于将Date头与当前时间比较的缓存发来的响应。

注意:已知一些HTTP/1.0缓存在违反此期望时并未提供任何警告。

然而,在某些情况下,并不适合缓存保存实体,或作为随后请求的响应。这可能是因为服务创建者认为绝对的语义透明是必要的,或是由于安全或隐私方面的考虑。某些缓存控制指令因此被提供出来以方便服务器能够指示某些资源实体或其中的部分不能够被缓存,无视其它考虑因数。

注意14.8节一般会阻止共享缓存给之前的请求保存和返回响应,如果此请求包含一个Authorization头。

响应码是200、203、206、300、301或401的响应会被缓存起来并作为随后请求的响应,受制于过期机制,除非缓存控制指令禁止缓存。然而,不支持Range和Content-Range头的缓存系统禁止缓存206(Partial Content)响应。

其它响应码(例如302和307)的响应禁止作为随后请求的返回结果,除非存在缓存控制指令或其它头字段允许这样做。例如,包含头:Expires(14.21节);包含控制指令:max-age,s-maxage,must-revalidate,proxy-revalidate,public、private(14.9节)。

13.5 从缓存中构造响应

HTTP缓存的目的就是存储对应请求的响应信息并用于回应未来的请求。在许多情况下,缓存简单地返回响应中合适的部分给请求者。然而,如果缓存拥有基于之前响应的缓存条目,它就不得不将部分新响应和缓存条目中的结合起来。

13.5.1 端对端和逐段跳

为了定义缓存和非缓存代理的行为的目的,我们将HTTP头划分为两个种类:

  • 端对端头(end-to-end),被传输给请求或响应的最终接收者。响应中的端对端头必须作为缓存条目一部分被保存起来并且必须在由缓存条目够成的响应中传输。
  • 逐段跳(hop-by-hop)头,只在单个传输级连接中才有意义。不能被缓存,也不能被代理转发。

以下是HTTP/1.1中的逐段跳头字段:

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • TE
  • Trailers
  • Transfer-Encoding
  • Upgrade

其它HTTP/1.1定义的所有头字段都是端对端头。

其它逐段跳头必须在Connection头中列出来(参考14.10节),并引用给HTTP/1.1(或以后版本)。

13.5.2 不可变头

有一些HTTP/1.1协议的特性,如摘要认证,是依赖于某个端对端头的。透明的代理不应该修改端对端头的值,除非头字段的定义要求或明确地允许这样做。

透明的代理禁止修改以下请求或响应中的字段,也不允许添加:

  • Content-Location
  • Content-MD5
  • ETag
  • Last-Modified

透明的代理禁止修改响应中的以下字段:

  • Expires

但是,如果响应中没有提供这些字段,代理却可以额外添加它们。如果代理添加了Expries头字段,就必须给Date头赋一样的值。

代理禁止修改或添加任何请求或包含非转换缓存控制指令消息中的以下字段:

  • Content-Encoding
  • Content-Range
  • Content-Type

非透明的代理可能会修改或添加以上字段给不包含非转换控制指令的消息,但是如果代理这样做了,就必须同时添加一个214(应用转换)的警告,前提是这个警告尚未出现在此消息中(参考14.46节)。

警告:端对端头的非必要的修改可能导致认证失败,这是由于在HTTP以后的版本中可能采用更强的认证机制。这样的认证机制可能依赖于没有在此列出的头字段的值。

13.5.3 头字段的结合

当缓存向服务器发送验证请求,且服务器返回了304(未修改)响应或206(部分内容)响应,那么缓存就会构造一个响应发送给请求客户端。

如果响应码是304,缓存就使用存储在缓存条目中的实体体作为即将发送的响应的实体。如果响应码是206且ETag或Last-Modified头精确地匹配,则缓存就会将缓存条目中存储的内容和接收到的响应中的内容结合起来,并将结果作为即将发送的响应的实体(参考13.5.4节)。

缓存条目中存储的端对端头被用于构造出来的响应,除了:

  • 存储的任意警告码为1xx(参考14.46节)的Warning头都必须从缓存条目和转发的响应中删除。
  • 存储的任意警告码为2xx的Warning头都必须在缓存条目和转发的响应中保存。
  • 在304和206响应中提供的任意端对端头都必须替换为缓存条目中对应的头。

除非缓存决定移除缓存条目,否则除了以上紧接着描述的Warning头以外,还必须将与缓存条目一起存储的端对端头替换为来自于响应中的对应的头。如果进来的响应中的头字段名称匹配到了缓存条目中的多个头,则所有这样的旧头字段都必须被替换。

换句话说,进来的响应中的所有端对端头都必须替换为对应的与缓存条目一起存储的端对端头(除了存储的警告码为1xx的Warning头,即使没有被重写,它们也会被删除)。

注意:本规则允许源服务器使用304或206响应来更新跟之前有相同实体或其子区间的响应的任何头,尽管这样做并不总是有意义的或正确的。本规则不允许源服务器使用304或206响应来整体删除之前响应提供的头字段。

13.5.4 字节子集的结合

响应可能只传输实体字节的子集,要么是因为请求包含一个或多个Range规范,要么是因为过早地断开了连接。在几个这样的传输之后,缓存可能已经接收到了同一个实体的部分子集。

如果缓存存储了一些同一实体的非空子集,且进来的响应传输了另一部分子集,则缓存可能会将新子集与已经存在的子集相结合,需要满足一下条件:

  • 接收的响应和缓存条目都有一个缓存验证器。
  • 两个验证器使用强验证功能相匹配(参考13.3.3节)。

如果任一要求都未能满足,则缓存必须只能使用最新的那一部分响应(基于每个响应中传输过来的Date字段值,如果这些值都是相等的或缺失,则使用接收到的响应),且必须抛弃其它部分。

13.6 缓存协商的响应

服务器驱动的内容协商的使用,如同响应中Vary头字段的出现表明的那样,通过改变环境和程序,缓存能够将此响应用于随后的请求。参考14.44节以了解服务器Vary头字段的用法。

由于服务器驱动协商的影响,服务器应该使用Vary头字段来通告缓存应该使用何种请求头字段在多个可缓存响应的表示中进行选择。以Vary字段值命名的头字段集合就被称之为“选择”请求头。

当缓存接收到了一个后续请求,该请求的Request-URI指定了一个或多个包含了Vary头字段的缓存条目,则缓存禁止使用这样的缓存条目来为新请求构造响应,除非新请求中出现的所有选择请求头与在原始请求中对应的已存储的请求头相匹配。

当且仅当通过在对应的BNF允许的地方添加或移除线性空格,和(或)按照第4.2节中关于消息头的规则将多个消息头字段与相同的字段名组合在一起,第一个请求的选择请求头能够被转换为第二个请求的选择请求头时,来自于这两个请求的选择请求头才被定义为匹配。

字段值为“*”的Vary头总是会匹配失败,且在此资源上的后续请求只能被源服务器正确地解析。

如果缓存条目中的选择请求头不匹配新请求中的选择请求头,那么缓存禁止使用此缓存条目去满足该请求,除非缓存首先用条件性请求的方式转发此新请求到源服务器中,并且服务器返回了304响应,此响应还包含了一个能够表明被使用的实体的实体标签或Content-Location头。

如果一个实体标签被分配给了一个已缓存的表示,则转发的请求应该是条件性的,并且应该在来自于该请求资源下所有缓存条目的If-None-Match头字段中包含该实体标签。这等于转告了服务器,缓存拥有当前的实体集,因此如果此实体集中任意一个匹配到了请求实体,服务器便可在其304响应中使用ETag头字段来告知缓存哪一个实体是合适的。如果新响应的实体标签匹配到了一个已存在缓存条目的实体标签,则新响应应该被用来更新已存在缓存条目的头字段,且更新结果必须返回给客户端。

如果已存在缓存条目的任意一个都只包含其关联实体的部分内容,则其实体标签不应该包含在If-None-Match头字段中,除非此缓存条目完全满足请求的范围。

如果缓存接收到了一个成功的响应,该响应的Content-Location字段匹配此响应相同Request-URI已存在缓存条目的Content-Location字段,该响应的实体标签与已存在缓存条目的实体标签不同,且该响应的Date字段比已存在缓存条目的Date值还要新,则该已存在的缓存条目不应该存在于返回给未来的请求的响应中,并且应该从缓存中删除。

13.7 共享和非共享缓存

由于安全和隐私的原因,区别“共享”和“非共享”是必要的。一个非共享缓存就是只对单个用户来说是可访问的。此缓存的可访问性应该被合适的安全机制来执行。所有其它的缓存都被认为是“共享”的。本规范的其它章节对共享缓存的操作施加了一些约束来防止隐私的缺失和访问控制的失败。

13.8 错误或不完全响应缓存行为

接收到不完全响应(例如,传输了比在Content-Length头中指定数据字节的小部分)的缓存可能会存储此响应。然而,缓存必须将此响应作为部分响应来对待。部分响应可能像在13.5.4节中描述的那样被结合起来;结合的结果可能是一个完整的响应或者仍然是部分的。缓存禁止在没有明确地使用206(Partial Content)状态码的情况下,返回给客户端一个部分响应。缓存禁止以200(OK)状态码返回一个部分响应。

如果缓存接收到了一个响应码为5xx的响应,并且尝试激活一个缓存条目,则它可能要么转发此响应给请求客户端,要么假装服务器响应失败。在后面一种情况,它可能返回一个更早的已接收到的响应,除非该缓存条目包含“must-revalidate”缓存控制指令(参考14.9节)。

13.9 GET和HEAD请求的副作用

除非源服务器明确禁止缓存它们的响应,否则如果响应来自于缓存,则对任何资源的GET和HEAD请求的应用都不应该有导致错误行为的副作用。它们可能仍然有副作用,但是缓存不需要考虑在缓存决定中的影响。缓存总是被期望去观察源服务器在缓存上明确的限制。

我们在此规则上注意到一个例外:因为某些应用一直传统地使用查询URL(这些URL包含了一个“?”在真实的路径中)的GET和HEAD请求来执行有重大副作用的操作,所以缓存禁止将这样的URI的响应作为有效的缓存,除非源服务器提供了明确的有效期。这特别地意味着对这些URI的来自于HTTP/1.0的响应不应该从缓存中获取。参考9.1.1节了解相关信息。

13.10 更新或删除后的无效性

某些执行在源服务器上资源的请求方法影响可能导致一个或多个已存在的缓存条目变成非透明的无效。换言之,尽管它们可能仍然是“新鲜的”,但是却不会明确地反映出哪个源服务器会为在此资源上的请求返回响应。

对HTTP协议来说,无法保证所有这样的缓存条目都被标记为无效。例如,在源服务器上导致这种变化的请求可能不会经过存储缓存条目的代理。然而,存在几个规则能够帮助减少错误行为的可能性。

在本章中,短语“invalidate an entity”意味着缓存要么移除仓库中该实体的所有实例,要么将它们标记为“无效”并且在它们能够作为后续请求的响应之前需要一个强制激活。

一些HTTP请求方法必须让缓存使实体失效。这些实体要么是被Request-URI引用的,要么是被Location或Content-Location(如果提供的话)头引用的。这些请求方法是:

  • PUT
  • DELETE
  • POST

为了防止拒绝服务攻击,基于Location或Content-Location头的URI无效操作必须只能当URI中的主机部分与Request-URI中一样才能执行。

当请求通过缓存,而其请求方法不被缓存理解时,缓存应该使任何被Request-URI引用的实体无效。

13.11 强制直写

所有可能被期望导致源服务器上资源被修改的请求方法必须直写到源服务器。目前除了GET和HEAD方法外,包含其它所有方法。缓存禁止在已经将此请求传输给入站服务器并接收到对应的响应之前,对此请求进行回应。这不会阻止代理缓存在入站服务器发送其最后回应之前发送一个100(Continue)响应。

另外一种方式(被称为“回写”或“复回”缓存)在HTTP/1.1中是不允许的,这是由于提供一致性更新的困难性和在回写之前来自于服务器、缓存或网络故障的问题。

13.12 缓存替换

如果接受到某一资源的新的可缓存的(参考14.9.213.2.513.2.613.8等章节)响应,同时在此资源上的任何已存在的响应都被缓存了起来,缓存应该用新的响应去回应当前请求。此新响应可能被存入缓存库,如果它还满足所有其它要求,可能会用它来响应任何未来的请求,这些请求曾经导致了旧响应的返回。如果它将新的响应插入缓存存储,则应用第13.5.3节中的规则。

注意:一个Date头字段值比已存在的缓存中的响应的该字段值还要旧的新响应是不可缓存的。

13.13 历史记录

用户代理通常都有历史机制,例如“回退”按钮和历史记录,这些历史记录能用来重现早期会话中接收到的实体。

历史机制和缓存是不同的。尤其是历史机制不应该尽量展示一个资源当前状态的语义透明性视图。当然,历史机制本意就是明确展示当接收到资源时用户当时所看到的东西。

默认情况下,历史机制没有过期时间。如果实体仍然还在,即使它过期了历史机制也应该展示它,除非用户已经特别地配置代理刷新过期的历史文档。

这并不意味着阻止历史机制告诉用户视图可能过时。

注意:如果历史列表机制不必要地阻止用户查看陈旧的资源,这将迫使服务作者避免在需要时使用HTTP过期控件和缓存控件。服务作者可能认为重要的是,当用户使用导航控件(如BACK)查看以前获取的资源时,不要向用户显示错误消息或警告消息。尽管有时这样的资源不应该缓存,或应该尽快过期,对用户接口的考虑可能迫使服务作者依靠其它阻止缓存的手段来为了不遭受非适当运作历史机制的影响。

14 头字段定义

本章节定义了所有标准HTTP/1.1头字段的语法和语义。对于实体头字段来说,发送者和接收者指的是要么是客户端要么是服务器,依赖于谁发送谁接收实体。

14.1 Accept

Accept请求头字段能被用来指定某些响应可接收的媒体类型。Accept头字段能被用来表明请求被特别限制为一小戳期望的媒体类型,如在请求内嵌图片的情况下。

Accept = "Accept" ":" #( media-range [ accept-params ] ) media-range = ( "*/*" | ( type "/" "*" ) | ( type "/" subtype ) ) *( ";" parameter ) accept-params = ";" "q" "=" qvalue *( accept-extension ) accept-extension = ";" token [ "=" ( token | quoted-string ) ]
星号“*”被用来给媒体类型分组为区间,“*/*”表示所有的媒体类型,“type/*”表示"type"类型的所有子类型。“media-range”可能表示适用于该区间的媒体类型参数。

每个“media-range”都可能跟随有一个或多个“accept-params”,以“q”开始的参数表示一个相关的质量因数。第一个“q”参数(如果有的话)将“media-range”参数从“accept-params”中区分出来。质量因数允许用户或用户代理表明对该媒体类型偏好的相对等级,取值范围从0到1(参考3.9节)。默认值为 q = 1 。

注意:使用“q”这个参数名称从Accept扩展参数中区分出媒体类型参数是由于历史实践的原因。尽管这样阻止了任何以“q”命名的媒体类型参数与“media-range”一起使用,但是由于IANA媒体类型注册表中没有任何“q”参数且Accept中任何媒体类型参数都很少使用,所以人们认为这样的事件是不太可能发生的。未来的媒体类型不鼓励注册任何以“q”命名的参数。

例如

Accept: audio/*; q=0.2, audio/basic

应该被解释为“我偏好audio/basic类型,但是如果在质量下降80%后仍然是最可用的也可以发给我。”

如果没有提供Accept头字段,那么就假定客户端接收所有的媒体类型。如果提供了Accept头字段,并且服务器在根据结合Accept头字段值后不能发送一个可接收的响应,那么服务器应该发送406(不可接收)响应。

一个更详细的案例就是

Accept: text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c

口头上,它应该被解释为“ text/html 和 text/x-c 都是偏好的媒体类型,但是如果它们不存在,那么就可以发送 text/x-dvi 实体,如果 text/x-dvi也不存在,发送 text/plain 也可以。”

媒体类型范围可以被更特殊的或指定的媒体类型重写。如果有超过一个媒体范围适用于给定的类型,则最具体的参考优先。例如,

Accept: text/*, text/html, text/html;level=1, /

有以下优先顺序:

1) text/html;level=1
2) text/html
3) text/*
4) /

与给定类型关联的媒体类型质量因子是通过查找与该类型匹配的具有最高优先级的媒体范围来确定的。例如:
Accept: text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, /;q=0.5

会导致以下值被关联:

text/html;level=1 = 1
text/html = 0.7
text/plain = 0.3
image/jpeg = 0.5
text/html;level=2 = 0.4
text/html;level=3 = 0.7

注意:用户代理可能给某些媒体范围提供了一系列默认的质量因数。然而,除非该用户代理是一个封闭系统,无法与其它呈现代理相互作用,否则这个默认质量因数集应该由用户配置。

14.2 Accept-Charset

Accept-Charset 请求头字段能被用来表明响应可接受的字符集。这个字段允许有能力理解更广泛或特殊目的字符集的客户端向能提供这些字符集文档的服务器展示这种能力。

Accept-Charset = “Accept-Charset” “:” 1#( ( charset | “*” )[ “;” “q” “=” qvalue ] )

有关字符集的描述参见3.4节。每一个字符集都可能给定一个相关的质量值“q”,这个质量值表示用户对此字符集的偏好程度。默认值为 q = 1。例如:

Accept-Charset: iso-8859-5, unicode-1-1;q=0.8

Accept-Charset字段中如果出现了特殊值“*”,则表示匹配所有的字符集(包括 ISO-8859-1),即使某些字符集并未在此字段中提到。如果此字段值没有提供“*”,则所有未在此字段中列除的字符集默认质量值为 0,但 ISO-8859-1 除外,如果未列出此字符集,则默认质量值为 1。

如果请求头中未提供Accept-Charset字段,则默认所有字符集都是可接受的。如果提供了此字段但服务器不能提供一个符合请求字符集的响应,则即使允许发送一个不可接收的响应,服务器也应该返回一个携带406状态码的错误响应。

14.3 Accept-Encoding

Accept-Encoding请求头字段与Accept相似,但是它限制了可接收响应的内容编码(参考3.5节)。

Accept-Encoding = "Accept-Encoding" ":" 1#( codings [ ";" "q" "=" qvalue ] ) codings = ( content-coding | "*" )
它的使用案例如下:
	   Accept-Encoding: compress, gzip
       Accept-Encoding:
       Accept-Encoding: *
       Accept-Encoding: compress;q=0.5, gzip;q=1.0
       Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0

服务器根据Accept-Encoding字段来测试一个内容编码是否是可接受的,使用以下规则:

  1. 如果该内容编码是Accept-Encoding列出的内容编码中的一个,则它是可接收的,除非其质量值为0。(如同3.9节中定义的一样,质量值为0则表示“不可接受”。)
  2. 特殊字符“*”表示匹配任何可用的内容编码,即使该内容编码未在Accept-Encoding字段中提及。
  3. 如果有多个内容编码都可用,那么质量值最高的内容编码胜出。
  4. “identity”内容编码总是可接受的,除非特别指定其质量值为0(例如:identity;q=0),或字段值包含“*;q=0”且未明确包含“identity”内容编码。如果Accept-Encoding字段值为空,则只有“identity”内容编码是可接受的。

如果请求中指定了Accept-Encoding字段,但服务器不能提供一个符合其指定内容编码的响应,则服务器应该返回一个406状态码的错误响应。

如果请求中未提供Accept-Encoding字段,则服务器可能假设客户端会接受任意的内容编码。在这种情况下,如果“identity”内容编码是可用内容编码中的一个,则服务器应该使用“identity”,除非有额外信息表明客户端更偏好其它的内容编码。

注意:如果请求不包含Accept-Encoding字段,且“identity”不可用,则优先选择HTTP/1.0客户端通常能理解的内容编码(如,“gzip”或“compress”);一些更旧的客户端会不合适地显示用其它内容编码发送的消息。服务器根据特殊的用户代理或客户端信息可能也会做出这种决定。
注意:大多数HTTP/1.0应用不认识或不遵守指定的与内容编码相关的质量值。这意味着质量值将不会工作并且不允许与x-gzip或x-compress一起使用。

14.4 Accept-Language

Accept-Language请求头字段与Accept相似,但是限制了对此请求的响应偏好的自然语言集。语言标签定义于3.10节。

       Accept-Language = "Accept-Language" ":"
                         1#( language-range [ ";" "q" "=" qvalue ] )
       language-range  = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )

每个“language-range”可能都会指定一个相关的质量值,这个质量值代表了语言区间(language-range)指定的语言被用户偏好的估值。此质量值默认为 “q = 1”。例如:

       Accept-Language: da, en-gb;q=0.8, en;q=0.7

以上案例表示:我偏好丹麦语(Danish),但是也接受英式英语(British English)和其它类型的英语。如果语言区间恰好等于语言标签值,或恰好等于标签的前缀(语言标签中以“-”分割的前面部份),则表示该语言区间匹配该语言标签。特殊字符“*”如果出现在字段中,则它匹配所有未被已出现在字段中的语言区间匹配过的语言标签。

注意:语言标签前缀匹配规则的使用并不是暗示语言标签就是以这样的方式分配给语言的:如果用户理解某种标签代表的语言,那么这个用户也一定理解以该标签作为前缀的其它语言区间所代表的语言。如果是这样的话,前缀规则只允许使用前缀标记。

Accept-Language字段分配给语言标签的语言质量因数就是该字段中匹配这个语言标签的最长语言区间的质量值。如果没有语言区间与该语言标签匹配,则语言质量因数赋值为0。如果请求未提供Accept-Language字段,则服务器应该假定所有的语言都同样是可接受的。如果请求提供了此字段,则所有质量值大于0的语言都是可接受的。

在每个请求中都发送一个含有用户全部语言偏好的Accept-Language头可能违背了用户对隐私的期望。有关这个问题的讨论,请参考15.1.4节。

由于可理解性高度依赖于个人用户,我们建议客户端应用在对用户可用的语言偏好中做出选择。如果无法做出选择,那么请求中就不应该给出Accept-Language字段。

注意:当在给用户可用的语言偏好中做出选择时,我们提醒该操作实际的实现者:用户并不熟悉以上描述的有关语言匹配的详情,你们应该提供合适的指导。举例,用户在选择“en-gb”语言时可能会假设,如果英式英语不可用,其它任何英语文档也是可以的。用户代理在这种情况下可能会建议添加一个“en”语言以便得到最好的匹配行为。

14.5 Accept-Ranges

Accept-Ranges响应头字段允许服务器表明对资源的区间请求的赞同性:

          Accept-Ranges     = "Accept-Ranges" ":" acceptable-ranges
          acceptable-ranges = 1#range-unit | "none"

接受字节区间请求(byte-range request)的源服务器可能会发送:

          Accept-Ranges: bytes

但并未要求这么做。客户端可能在没有接收到涉及相关资源的Accept-Ranges头时就生成了字节区间请求。区间单位定义于3.12节。

不接受资源的任何类型的区间请求的服务器可能会发送:

          Accept-Ranges: none

来建议客户端不要尝试区间请求。

14.6 Age

Age响应头字段传达了自响应(或其重新激活)在源服务器上生成后发送者对其时间的估值。如果一个已缓存的响应的Age没有超过它的新鲜度生命周期,则它就是“新鲜”的。年龄值的计算方式参考13.2.3节。

           Age = "Age" ":" age-value
           age-value = delta-seconds

年龄值是非负十进制整数,单位是秒。

如果缓存接收到的值比它能够接受的最大的正整数还要大,或如果其年龄计算的任何一步溢出,则它必须传输值为2147483648(2^31)的Age头。含有缓存的HTTP/1.1服务器必须在其每一个响应中包含一个由其缓存生成的Age头字段。缓存应该使用至少有31位的算法类型。

14.7 Allow

Allow实体头字段列出了被Request-URI标识的资源所支持的访问方法集。这个字段的目的就是严格地通知与资源有关的有效方法的接收者。Allow头字段必须出现在405(Method Not Allowed)响应中。

          Allow   = "Allow" ":" #Method

使用案例:

          Allow: GET, HEAD, PUT

此字段不能阻止客户端尝试其它方法。然而,Allow头字段给定的建议应该被遵守。在每次请求时,源服务器都定义了真实的允许访问的方法集。

PUT请求可能会提供Allow头字段以建议在创建或修改资源时支持的方法。服务器不要求支持这些方法,并且应该包含一个给出了真实支持的方法的Allow头在响应中。

代理即使不理解Allow头字段中指定的方法,也禁止修改这个字段,因为用户代理可能有与源服务器通信的其他方法。

14.8 Authorization

用户代理希望获得常用服务器的授权,但是在接收到401响应之后却没必要这么做–通过在请求中包含Authorization请求头字段的方式完成。Authorization字段值由用户代理请求资源范围的授权信息组成。

          Authorization  = "Authorization" ":" credentials

HTTP访问授权描述于“HTTP Authentication: Basic and Digest Access Authentication”。如果请求获得授权并指定了访问范围,则对于在此访问范围上的所有请求来说,相同的证书应该是有效的(假设此授权模式不需要其它条件,例如根据授权值或使用同步时钟而变化的证书)。

当共享缓存(参考13.7节)接收到一个含有Authorization字段的请求时,禁止它将对应的响应作为任何其它请求的回应,除非下列特定的例外情况之一成立:

  1. 如果响应包含“s-maxage”缓存控制指令,则缓存可能使用对应的响应作为后续请求的回应。但是(如果超过了最大的指定年龄)代理缓存必须首先用源服务器激活它,使用来自于新请求的请求头来让源服务器去授权新请求。(这是s-maxage定义的行为。)如果响应包含“s-maxage=0”,则代理必须总是在重新使用它之前进行激活。
  2. 如果响应包含“must-revalidate”缓存控制指令,则缓存可能在回应后续请求时使用此响应。但是如果响应已经过期,所有缓存必须首选用源服务器进行激活,使用来自于新请求的请求头来让源服务器去授权新请求。
  3. 如果响应包含“public”缓存控制指令,则它可能被用于响应任何后续的请求。

14.9 Cache-Control

缓存控制指令是用来沿着请求/响应链指定必须被所有缓存机制遵守的指令。指令指定行为打算防止缓存不利地妨碍请求或响应。这些指令通常重写了默认的缓存算法。缓存指令是单向的,在请求中存在一个指令并不意味着在响应中会给出相同的指令。

注意:HTTP/1.0缓存可能没有实现缓存控制,可能只实现了Pragma: no-cache(参考14.32节)。

缓存指令必须经过代理或网关应用,而不管对该应用的重要性,因为这些指令可能对请求/响应链上的所有接收者都是可用的。不可能为特殊的缓存单独指定一个缓存指令。

    Cache-Control   = "Cache-Control" ":" 1#cache-directive
    cache-directive = cache-request-directive
         | cache-response-directive
    cache-request-directive =
           "no-cache"                          ; Section 14.9.1
         | "no-store"                          ; Section 14.9.2
         | "max-age" "=" delta-seconds         ; Section 14.9.3, 14.9.4
         | "max-stale" [ "=" delta-seconds ]   ; Section 14.9.3
         | "min-fresh" "=" delta-seconds       ; Section 14.9.3
         | "no-transform"                      ; Section 14.9.5
         | "only-if-cached"                    ; Section 14.9.4
         | cache-extension                     ; Section 14.9.6
     cache-response-directive =
           "public"                               ; Section 14.9.1
         | "private" [ "=" <"> 1#field-name <"> ] ; Section 14.9.1
         | "no-cache" [ "=" <"> 1#field-name <"> ]; Section 14.9.1
         | "no-store"                             ; Section 14.9.2
         | "no-transform"                         ; Section 14.9.5
         | "must-revalidate"                      ; Section 14.9.4
         | "proxy-revalidate"                     ; Section 14.9.4
         | "max-age" "=" delta-seconds            ; Section 14.9.3
         | "s-maxage" "=" delta-seconds           ; Section 14.9.3
         | cache-extension                        ; Section 14.9.6
    cache-extension = token [ "=" ( token | quoted-string ) ]

当一个指令以没有任何“1#field-name”参数的形式出现,则这个指令适用于整个请求或响应。当这样的指令以一个“1#field-name”参数形式出现,则它只适用于参数指定的字段。此机制支持扩展;HTTP协议未来版本的实现可能会应用这些指令到未在HTTP/1.1中定义的头字段中。

缓存控制指令能被分解为以下一般分类:

  • 约束什么是可缓存的;这些指令可能只会是源服务器强加的。
  • 约束什么是能被缓存存储的;这些指令可能要么是源服务器要么是用户代理强加的。
  • 基本过期机制的修改;这些指令可能要么是源服务器要么是用户代理强加的。
  • 掌控缓存激活和重载;这些指令可能只会是用户代理强加的。
  • 掌控实体传输。
  • 缓存系统的扩展。

14.9.1 什么是可缓存的

默认地,一个响应是否是可缓存的需要满足请求方法、请求头和响应状态的条件。13.4节总结了缓存性的这些默认条件。以下Cache-Control响应指令允许源服务器重写响应默认的缓存性:

  • public:表明响应可能被任意的缓存系统缓存,尽管它可能通常是非缓存性的或只被非共享缓存系统缓存的。(也可以参考Authorization字段,14.8节,了解更多详情。)
  • private:表明响应消息的全部或部分内容仅服务于单个用户,且禁止被共享缓存存储。它允许源服务器声明指定的部分响应仅服务于一个用户且此响应对于其它用户请求是无效的。一个私有(非共享)的缓存系统可能缓存这个响应。
    注意:“private”指令的使用只能控制响应可能被缓存在什么地方,并不能确保消息内容的隐私性。
  • no-cache:如果no-cache指令没有指定字段名,则缓存系统禁止使用此响应去满足没有成功经过源服务器重新验证的后续请求。该指令允许源服务器阻止响应被缓存,即使是已经配置了可以返回过期响应的缓存系统也不行。
    如果no-cache指令指定了一个或多个字段名,则缓存系统在满足缓存的任意其它限制条件下,可能使用此响应去满足后续的请求。然而,指令中的字段在经过源服务器的重新验证之前禁止发送给后续的请求。该指令允许源服务器防止响应中的某些头字段被重复使用,但是却允许响应的剩余部分被缓存。
    注意:大多数HTTP/1.0缓存系统可能不认识或不遵守此指令。

14.9.2 什么能被缓存存储

no-store指令:
        no-store指令的目的就是防止敏感信息(例如,备份磁带)疏忽大意的泄露或保留。no-store指令适用于整个消息,并且可能在响应或请求中被发送。如果在请求中发送,则缓存系统禁止存储此请求或此请求的任何响应中的任何部分。如果发送在响应中,则缓存系统禁止存储此响应或引出此响应的请求的任何部分。该指令同时适用于共享和非共享缓存。此处的“禁止存储”意思为缓存禁止有意地将信息存储在持久性仓库中,并且在转发此信息后,要尽可能迅速地尽最大努力地从临时仓库中移除此信息。
        即使此指令跟响应有关(禁止存储此响应),用户也可能显式地在缓存系统之外存储此响应(例如,通过“Save As”对话框)。历史缓冲区可能会存储这样的响应作为用户的常规操作的一部分。
        此指令的目的就是为了满足某些用户和服务作者声明的需求,他们关心通过对缓存数据结构无意间的访问导致的偶然的信息泄露。尽管此指令的使用可能会改善某些场景下的隐私性,然而我们警告这绝不是一个确保隐私的可靠的或充分的机制。特别地,恶意的或妥协的缓存系统可能不认识或不遵守此指令,网络通信可能很轻易被窃听。

14.9.3 基本过期机制的修改

源服务器可能使用Expires头字段(参考14.21节)来指定实体的过期时间。或者,也可能用响应中的max-age指令指定。当缓存的响应中提供了max-age缓存控制指令,则如果响应的当前年龄大于新请求请求此响应时给定的年龄值(单位:秒)时,此响应就是过期的。响应中的max-age指令意味着响应是可缓存的(例如,“public”指令),除非提供了一些其它的更严格的缓存指令。

如果响应同时包含Expires头和max-age指令,则max-age指令会重写Expires头,尽管Expires头更加严格。这个规则允许源服务器对于给定的响应,能够给HTTP/1.1(或更高版本)比HTTP/1.0的缓存提供一个更长的过期时间。在HTTP/1.0缓存系统由于非同步时钟的原因导致的错误地计算年龄或过期时间时可能是有用的。

许多HTTP/1.0缓存实现会将小于或等于响应Date头字段值的Expires头字段值与Cache-Control响应指令“no-cache”等同对待。如果HTTP/1.1缓存接收到了一个这样的响应,并且此响应不包含Cache-Control头字段,则缓存应该认为此响应是不可缓存的以便与HTTP/1.0服务器保持兼容。

注意:在一个包含不理解新HTTP缓存控制特性(如“private”指令)的旧缓存系统的网络中,如果源服务器想要使用这些新特性,它需要将新特性与小于或等于响应Date头字段值的Expires头字段相结合。这样做可以防止旧缓存系统错误地缓存响应。

s-maxage:
        如果响应包含s-maxage指令,则对于一个共享缓存来说(但并不是私有缓存),此指令指定的最大年龄会覆盖max-age指令或Expires头字段指定的最大年龄。s-maxage指令还含有代理验证(proxy-revalidate)指令的语义,例如,共享缓存禁止在没有首先通过源服务器验证的情况下使用过期的实体作为后续请求的响应。s-maxage指令总是会被私有缓存忽略。

注意,多数更旧的缓存并不遵守此规范,没有实现任何缓存控制指令。一个希望使用缓存控制指令通过遵守HTTP/1.1协议的缓存来约束而非阻止缓存的源服务器可能会开发max-age指令重写Expires头字段的需求,而事实上HTTP/1.1之前的缓存并未遵守max-age指令。

其它指令允许用户代理修改基本过期机制。这些指令可能出现在请求中:

  • max-age:表明客户端希望接收的响应其年龄不超过给定的时间秒数。除非包含了max-stale指令,客户端不希望接收一个过期的响应。
  • min-fresh:表明客户端希望接收的响应其有效生命周期不小于响应的当前年龄加上给定的秒数。也就是说,客户端想要一个响应,该响应在其给定的时间之内仍然是有效的。
  • max-stale:表明客户端希望接收一个已经过了有效期的响应。如果max-stale分配了一个值,那么客户端希望接收一个虽然过了有效期但是未超过此给定值的响应。如果没有分配值,则客户端可以接收任意年龄的过期响应。

如果缓存返回了一个过期响应,要么是因为请求中指定了max-stale指令,要么是因为缓存被配置为可以重写响应的过期时间,缓存必须在过期响应中附加一个Warning(警告码:110)头。

如果新请求和缓存的实体都包含max-age指令,则两个值间的最小值决定了缓存实体是否有效。

14.9.4 缓存验证和重载控制

有时候,用户代理可能想要或需要坚持缓存系统用源服务器对缓存实体重新验证(不只是沿着到源服务器路径上的下一个缓存),或者从源服务器上重载其缓存实体。如果缓存或源服务器高估了已缓存响应的过期时间,则端对端(end-to-end)的重新验证是必须的。如果缓存条目因为某些原因已经损坏,则端对端的重载可能也是必须的。

当客户端没有其自己本地的缓存复制时可能会请求一个端对端重新验证,这种情况下我们称之为“未指定的端对端重新验证”,或者当客户端有自己的本地缓存复制时也可能请求端对端重新验证,我们将这种情况称之为“指定的端对端重新验证”。

客户端通过使用Cache-Control请求指令来指定以下三种操作:

  • End-to-end reload:
    此请求包含一个“no-cache”缓存控制指令,或者为了兼容HTTP/1.0客户端会包含“Pragma:no-cache”。请求中禁止“no-cache”指令包含字段名称。服务器禁止用已缓存的复制品响应此种请求。
  • Specific end-to-end revalidation:
    此请求包含一个“max-age=0”缓存控制指令,此指令强迫请求到源服务器路径上的所有缓存都要使用下一个缓存或服务器(如果有的话)对缓存实体进行重新验证。初始请求都包含一个使用客户端当前验证器的缓存验证条件。
  • Unspecified end-to-end revalidation:
    此请求包含一个“max-age=0”缓存控制指令,此指令强迫请求到源服务器路径上的所有缓存都要使用下一个缓存或服务器(如果有的话)对缓存实体进行重新验证。初始请求不包含缓存验证条件;请求路径上获得请求资源缓存条目的第一个缓存用其自己的缓存验证器包含有缓存验证条件。
  • max-age:
    当一个中间缓存被“max-age=0”指令强迫对其自己的缓存条目进行重新验证,且客户端在请求中已经提供了自己的验证器,则提供的验证器可能不同于缓存条目中当前存储的验证器。此种条件下,缓存可能使用任意的验证器在不影响语义透明性的前提下发出自己的请求。
    然而,验证器的选择可能会影响性能。对中间缓存来说最好的方案就是在发送请求时使用其自己的验证器。如果服务器响应了304,则缓存现在可以用其已验证的复制品给客户端返回一个200(OK)响应。如果服务器响应了一个新的实体和缓存验证器,此时,中间缓存可以用强验证函数将请求中提供的验证器与返回的进行比较。如果客户端的验证器和服务器端的一致,则中间缓存可以简单地返回304响应。否则,缓存就要用200(OK)响应返回那个新的实体。
    如果请求包含“no-cache”指令,它就不应该再包含有“min-fresh”、“max-stale”或者“max-age”。
  • only-if-cached:
    在某些情况下,比如网络连通性极差的时候,客户端可能想要缓存只返回其实时存储的响应,且不需要重载或用源服务器重新验证。为了这么做,客户端就会在请求中包含“only-if-cached”指令。如果缓存接收到此指令,则它就应该要么用与请求中其它约束保持一致的缓存条目作为响应,要么直接用504(网关超时)响应。然而,如果一组缓存都被作为一个拥有稳定网络连通性的统一系统处理的话,这样的请求就可以在该缓存组中转发。
  • must-revalidate:
    因为缓存可能被配置为忽略服务器指定的过期时间,也因为客户端请求可能包含有“max-stale”指令(它们有相似的影响),所以HTTP/1.1协议为源服务器包含了一种机制用来在任意后续使用中要求缓存条目的重新验证。当缓存接收到的响应中包含有“must-revalidate”指令,则缓存禁止在缓存条目已经过期并且没有首先经过源服务器成功验证的情况下作为后续请求的响应。(也就是说,如果仅仅基于源服务器的Expires头或max-age指令值就判断为已缓存的响应是过期了的话,则缓存必须每次都要进行端对端重新验证。)
    “must-revalidate”指令必须支持某些协议特性的可靠性操作。不管在什么环境,HTTP/1.1缓存都必须遵守must-revalidate指令;特别地,如果缓存因为某些原因不能到达源服务器,则缓存必须生成一个504(网关超时)响应。
    当且仅当在实体上对请求的重新验证失败会导致非正确操作时,服务器才应该发送must-revalidate指令,比如一个静默的未执行的金融交易。接收者禁止采取任何自动化的违反此指令的行为,禁止在重新验证失败的情况下自动地提供实体的未验证副本。
    受制于严峻的网络连通性约束的用户代理可能会违反此指令,我们不推荐这么做,但如果真的发生了,则用户代理必须显式地警告用户提供了一个未验证的响应。每一个未验证的访问都必须给出这样的警告,而且应该要求明确的用户确认。
  • proxy-revalidate:
    proxy-revalidate指令与must-revalidate指令拥有相同的含义,但此指令并不适用于非共享用户代理缓存。它可以用于对已认证请求的响应之中,以便允许用户的缓存来存储响应,并且可以在不需要重新验证(因为已经被用户认证过一次了)的情况下返回此响应,然而仍然需要代理在每一次都要重新验证(确保每个用户都认证成功),尽管它们已经服务了许多用户。注意,即使是这样已认证的响应也需要公共缓存控制指令来让允许它们被完全缓存。

14.9.5 非转换指令(No-Transform Directive)

中间缓存(代理)的实现者已经发现转换某些实体的媒体类型是有用的。例如,一个非透明的代理可能在图片格式之间进行转换以便节省缓存空间或减少低速链路的通信量。
然而,当打算为了某几种应用而对实体应用这些转换可能会导致一些严重的操作性问题。例如,用于医学成像、科学数据分析和端对端认证的应用都依赖于接收到的实体与原始实体是位对位一致的。
因此,如果一个消息包含了非转换指令,则中间缓存或代理禁止改变这些头字段(在13.5.2节中列出的且从属于非转换指令)。这意味着缓存或代理禁止改变被这些头指定的实体的任何方面,包括实体自己的值。

14.9.6 缓存控制扩展

Cache-Control头字段可以通过使用一个或多个cache-extension令牌进行扩展,每个令牌都带有一个可选的指定值。信息化扩展(它们不需要改变缓存的行为)可能在不会改变其它指令的语义的情况下被添加进来,而行为化扩展被设计作为一个已存在的缓存指令基础的修改者来工作。同时提供了新指令和标准化指令,这样的话,那些不理解新指令的应用将会默认使用标准化指令指定的行为,而那些理解新指令的应用将会识别它,就像修改了与标准化指令相关的需求一样。使用这种方式,就可以在不改变基本协议的基础上对cache-control指令进行扩展。

此扩展机制依赖于HTTP缓存遵守所有与其本地HTTP版本相关的缓存控制指令、遵守某些扩展并且忽略所有不理解的指令。

例如,考虑一个假想的称之为”community”的新响应指令,它扮演了一个私有指令修改者的角色。我们定义这个新指令就是要除了任意非共享缓存之外,任意的只由其值内命名的社区(community)成员共享的缓存也能缓存这个响应。一个希望允许UCI社区在其共享缓存中使用另外的私有响应的源服务器可以这样做:
Cache-Control: private, community=“UCI”
即使是不理解“community”缓存控制指令的缓存也能正确地扮演其角色,因为它也会看到并理解“private”指令并因此采取默认安全的行为。

不识别的缓存指令必须被忽略。我们假设任意的可能不被HTTP/1.1缓存识别的缓存指令会和标准指令(或响应的默认缓存性)联合在一起,如此这般缓存行为也能保持最低限度的正确性,即使缓存并不理解那些缓存扩展。

14.10 Connection

Connection通用头字段允许发送者为期望的特殊连接指定某些选项,并禁止代理在进一步的连接之上进行通信。

Connection头字段的语法:

Connection = “Connection” “:” 1#(connection-token)
connection-token = token

HTTP/1.1代理必须在消息被转发之前解析Connection头字段,对于此字段中的每一个connection-token来说,移除消息中任意的与此connection-token相同名称的头字段。Connection选项是由Connection头字段中connection-token标识的,而不是任何对应的额外的头字段,因为这些头字段在没有与连接选项相关的参数时可能不会发送。

在Connection头中列出的消息头字段禁止包含端对端头字段,例如Cache-Control。

HTTP/1.1定义了“close”连接选项,让发送者发布通知,在响应完成之后,连接将会被关闭。例如:

Connection: close

要么出现在请求中,要么出现在响应中,“close”表明连接在当前请求/响应完成之后不应该认为是“persistent”。

HTTP/1.1中不支持持久化连接的应用必须在每一个消息中包含“close”连接选项。

接收到HTTP/1.0(或更低版本)的含有Connection头字段消息的系统必须移除或忽略任意的与其所有connection-token名称相同的头字段。这样可以防止HTTP/1.1之前版本的代理错误地转发这些头字段。参考19.6.2节。

14.11 Content-Encoding

Content-Encoding实体头字段是用来修改媒体类型的。当提供了此字段,它的值就表明何种额外的内容编码会被用于实体,并因此表明必须应用何种解码机制来获取Content-Type头字段引用的媒体类型。Content-Encoding主要用来让文档在压缩的时候不会丢失其潜在媒体类型的一致性。

Content-Encoding = “Content-Encoding” “:” 1#content-coding

内容编码定义在3.5节。使用案例如下:

Content-Encoding: gzip

内容编码是被Request-URI标识的实体的特征。通常,实体体(entity-body)是用其编码存储的,也只能在渲染或类似用法之前进行解码。然而,一个非透明的代理可能会修改此内容编码,如果修改后的编码对接收者来说是已知可接收的,除非消息中提供了“no-transform”缓存控制指令。

如果实体的内容编码是非“一致性”的,则响应必须包含一个列出了所使用的非一致性内容编码的Content-Encoding实体头。

如果请求消息中实体的内容编码对源服务器来说是不可接收的,则源服务器应该响应415状态码(不支持的媒体类型)。

如果已经有多个编码应用于实体,则内容编码必须以其被应用的顺序列出。有关编码参数的额外信息可能由其它不被此规范定义的实体头字段提供。

14.12 Content-Language

Content-Language实体头字段为封装的实体描述了其目标观众的自然语言。要注意的是它并不等于实体体内使用的所有的语言。

Content-Language = “Content-Language” “:” 1#language-tag

3.10节定义了语言标签。Content-Language字段的主要目的就是让用户根据他们偏好的语言去标识和区分实体。因此,如果实体内容只打算适用于丹麦人,则合适的字段值就是:

Content-Language: da

如果没有指定Content-Language字段,则默认为内容适用于所有的语言。这意味着发送者没有考虑为任意一种自然语言指定值,或者发送者不知道使用何种语言。

有可能为多种目标受众指定多种自然语言。例如,《怀唐伊条约》的译文同时使用原始的毛利语和英语版本,则字段值为:

Content-Language: mi, en

然而,只是因为实体中提供了多种自然语言并不意味着此内容就是打算适用于多种语言的观众。一个例子就是语言初学者的初级读本,比如“拉丁语的第一节课”,它非常清楚地表明是给英语观众使用的,在这种情况下,Content-Language应该使用“en”比较合适。

Content-Language可能适用于任意一种媒体类型 – 它并不限制于文本文档。

14.13 Content-Length

Content-Length实体头字段表明实体的大小,用八位字节的十进制数表示,发送给接收者;在HEAD方法中,如果请求是GET,就会发送实体体的大小。

Content-Length = “Content-Length” “:” 1*DIGIT

一个案例就是:

Content-Length: 3495

应用应该使用此字段来表明消息体的传输长度,除非被4.4节的规则禁止了。

任何大于或等于0的Content-Length都是有效的值。4.4节描述了在没有提供Content-Length的情况下如何确定一个消息体的长度。

注意,此字段的含义显著地不同于在MIME中定义的、一个用在“message/external-body”内容类型的可选字段。在HTTP中,无论何时,只要在消息被传输之前能够决定消息的长度,此字段就应该发送,除非被4.4节的规则禁止了。

14.14 Content-Location

当封装在消息中的实体从请求资源URI分离的位置是可访问的,Content-Location实体头字段可能会被用来提供该实体的资源定位。服务器应该为对应的响应实体的变量提供Content-Location字段;特别是在资源拥有与该变量相关的多个实体的时候,这些实体实际上拥有独立的定位,它们可能被单独访问,服务器应该为返回的该变量提供一个Content-Location字段。

Content-Location = “Content-Location” “:”
( absoluteURI | relativeURI )

Content-Location字段的值也定义了实体的基本URI。

Content-Location字段值不是原始请求URI的替代品;它只是在请求的当时,对此特殊实体所代表的资源定位的声明。如果未来期望标识此特殊实体的资源,未来请求可能会指定Content-Location URI作为Request-URI。

缓存不能假定一个Content-Location的URI不同于用来检索的URI的实体能够被用来响应在Content-Location URI上的后续请求。然而,Content-Location字段可以用来区分从单个请求资源上进行检索的多个实体,如同在13.6节中描述的那样。

如果Content-Location是一个相对URI,则此URI会相对于Request-URI进行解释。

在PUT或POST请求中的Content-Location头的含义未定义;服务器可以自由地忽略这些场景下的它们。

14.15 Content-MD5

Content-MD5实体头字段,如同在RFC1864中定义的那样,是一个实体体的MD5摘要,用于提供一个对实体体的端对端消息完整性(MIC:message integrity check)检查。(注意,一个MIC可以有效的帮助检测实体在传输过程中的意外修改,却不是对抗恶意攻击的证据。)

Content-MD5 = “Content-MD5” “:” md5-digest
md5-digest = <base64 of 128 bit MD5 digest as per RFC 1864>

Content-MD5头字段可能由源服务器或客户端生成作为一个实体的完整性检查运行。只有源服务器或客户端才可能生成Content-MD5头字段;代理和网关禁止生成,因为这样做会阻止它的值作为一个MIC。任何实体体的接收者,包括网关和代理,都可能检查该字段中提供的摘要是否匹配接收到的实体的摘要。

MD5摘要是基于实体体的内容计算的,包括任何被应用的内容编码,却排除了任何应用于消息体的传输编码。如果消息是一种传输编码被接收的,则在检查MD5之前必须移除该编码。

其结果就是,在实体的八位字节上计算出的摘要的顺序与没有应用传输编码时发送它们的顺序一致。

HTTP扩展了RFC 1864 来允许为MIME复合媒体类型(例如,multipart/* 和 message/rfc822)计算摘要,但此扩展并不会改变摘要计算的方式。

这样做会导致几个结果。复合媒体类型的实体可能包含多个实体部分,每个实体部分都有自己的MIME和HTTP头(包括Content-MD5,Content-Transfer-Encoding和Content-Encoding 头)。如果一个部分实体拥有一个Content-Transfer-Encoding或Content-Encoding头,我们就假定此部分实体的内容已经被编码了,并且此部分实体被包含在了Content-MD5摘要中当 – 例如,在应用编码之后。每个部分实体不允许出现Transfer-Encoding头。

CRLF的所有换行符的转换在计算或检查摘要之前都禁止完成:换行符在实际传输的文本中使用的约定在计算摘要的时候必须保持不变。

注意:当HTTP中Content-MD5的定义与RFC 1864中MIME实体体的定义完全相同时,有几种方式可以区分HTTP实体体的Content-MD5与MIME实体体的Content-MD5。一种就是HTTP,不像MIME,不使用Content-Transfer-Encoding,而使用Transfer-Encoding和Content-Encoding。另一种就是HTTP比MIME更加频繁地使用二进制内容类型,所以在这种情况下,值得注意的是,计算摘要的字节顺序就是为类型定义的传输字节的顺序。最后一种,HTTP允许几种换行符约定的任意一种的文本类型的传输,并不只是使用CRLF公认格式。

14.16 Content-Range

Content-Range实体头是随着一个部分实体(partial entity-body)发送的,它指定了此部分实体应该在整个实体中应用的位置。3.12节定义了区间的单位。

Content-Range = “Content-Range” “:” content-range-spec
content-range-spec = byte-content-range-spec
byte-content-range-spec = bytes-unit SP
byte-range-resp-spec “/”
( instance-length | “*” )
byte-range-resp-spec = (first-byte-pos “-” last-byte-pos)
| “*”
instance-length = 1*DIGIT

该头字段应该表明整个实体的总长度,除非长度是未知的或很难确定。字符“*”意味着在生成响应的时候还不知道instance-length。

与byte-ranges-specifier值(参考14.35.1节)不同,一个byte-range-resp-spec必须只能指定一个区间,并且必须包含区间中第一个和最后一个字节的绝对位置。

一个byte-content-range-spec,如果伴随的byte-range-resp-spec的last-byte-pos的值小于其first-byte-pos的值,或instance-length的值小于或等于其last-byte-pos的值,则此 byte-content-range-spec是无效的。接收到无效byte-content-range-spec的接收者必须忽略它和随着它传输而来的任何内容。

服务器应该在发送状态码是416(请求区间不满足)的响应时包含一个byte-content-range-spec值为“*”的Content-Range字段,其中,instance-length指定了被选择的资源的当前长度。而在发送状态码是206(部分实体)的响应时禁止包含byte-content-range-spec值为“*”的Content-Range字段。

byte-content-range-spec的使用案例如下,假设实体的总长度为1234个字节:

. The first 500 bytes:
bytes 0-499/1234
. The second 500 bytes:
bytes 500-999/1234
. All except for the first 500 bytes:
bytes 500-1233/1234
. The last 500 bytes:
bytes 734-1233/1234

当一个HTTP消息包含单个区间(例如,请求单个区间的响应,或请求无缝重叠区间集的响应)的内容时,此内容就随着一个Content-Range头和一个展示了实际传输字节长度的Content-Length头传输过来。例如:

HTTP/1.1 206 Partial content
Date: Wed, 15 Nov 1995 06:25:24 GMT
Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
Content-Range: bytes 21010-47021/47022
Content-Length: 26012
Content-Type: image/gif

当HTTP消息包含多个区间(例如,请求多个不重叠区间的响应)的内容时,它们就作为一个多部分消息(multipart message)被传输。用于此目的的多部分媒体类型是“multipart/byteranges”,定义于附录中19.2节。参考附录19.6.3节了解其兼容性问题。

对请求单个区间的响应禁止使用multipart/byteranges媒体类型发送。对请求多重区间结果却是单个区间的响应,可能会用multipart/byteranges媒体类型进行发送,但却只包含一个部分。不能解码multipart/byteranges消息的客户端禁止在单个请求中请求多部分字节区间。

当客户端在一个请求中请求多部分字节区间时,服务器应该用它们在请求中出现的顺序返回响应。

如果服务器因为无效的语句结构而忽略了byte-range-spec,则服务器应该认为不存在无效的Range头。(一般地,这意味着会返回一个包含整个实体的响应码为200的响应。)

如果服务器接收到一个不满意的Range头(也就是说,其所有的byte-range-spec值的first-byte-pos的值都大于所请求资源的当前长度)的请求(而非一个包含If-Range头的请求),则服务器应该返回一个状态码为416(请求区间不满足。参考10.4.17节)的响应。

注意:客户端不能依赖于为不满足的Range请求头发送416而不是200响应的服务器,因为不是所有服务器都实现了这个请求头。

14.17 Content-Type

Content-Type实体头字段表示发送给接收者的实体的媒体类型,或对于HEAD方法,如果是GET请求,那么将发送媒体类型。

Content-Type = “Content-Type” “:” media-type

媒体类型的定义在3.7节。此字段的一个案例如下:

Content-Type: text/html; charset=ISO-8859-4

7.2.1节提供了关于标识实体媒体类型的方法的更深的讨论。

14.18 Date

Date通用头字段表示消息生成的日期和时间,与RFC 822中的orig-date拥有一样的语义。其字段值是一个HTTP-date,如同3.3.1节所述。它必须用RFC 1123 中定义的日期格式发送。

Date = “Date” “:” HTTP-date

一个案例如下:

Date: Tue, 15 Nov 1994 08:12:31 GMT

源服务器必须在所有的响应中包含一个Date头,除了以下情况:

  1. 如果响应的状态码是100(继续)或101(切换协议),则响应可能会包含Date头,要看服务器的选择。
  2. 如果响应的响应码传达了一个服务器错误,例如500(服务器内部错误),或503(服务不可用),则不方便或不可能生成一个有效的Date。
  3. 如果服务器没有一个能够提供当前时间合理的近似值的时钟的话,则其响应中禁止包含Date头。在这种情况下,就必须要遵守14.18.1节中定义的规则了。

如果接收到的消息没有Date头,而消息的接收者或网关想要通过一个需要Date头的协议对消息进行缓存,则消息接收者或网关必须赋值一个日期。没有时钟的HTTP实现禁止在没有每次使用前对响应进行重新验证的情况下缓存它们。一个HTTP缓存,特别是共享缓存,应该使用一种机制,比如NTP,用一种可靠的外部标准来同步时钟。

客户端应该只在包含有实体的消息中发送Date头,如PUT或POST请求,尽管它是可选的。没有时钟的客户端禁止在请求中发送Date头。

Date头中的HTTP-date不应该表示消息生成之后的日期和时间。它应该代表消息生成时的最接近的日期和时间,除非该实现没有办法生成一个合理的精确的日期和时间。理论上,日期应该表示实体生成之前的那一刻。实际上,这个日期可以在从消息起源开始的任意时间生成,只要不影响其语义含义即可。

14.18.1 源服务器的无时钟操作

一些源服务器的实现并没有一个可用的时钟。它们禁止在响应中包含Expires或Last-Modified字段,除非这些值通过拥有可靠时钟的系统或用户与资源进行关联。在服务器配置时或其之前,就可以给Expires赋值一个已知的时间,在过去,这允许响应提前过期,而不需要单独为每个资源配置Expires。

14.19 ETag

ETag响应头字段为请求变量提供了实体标签的当前值。与实体标签一起使用的头字段在14.24节、14.26节、14.44节都有描述。实体标签可能用来与来自于同一资源的其它实体进行比较(参考13.3.3节)。

ETag = “ETag” “:” entity-tag

案例:

ETag: “xyzzy”
ETag: W/“xyzzy”
ETag: “”

14.20 Expect

Expect请求头字段是用来表示客户端期望的特殊的服务器行为。

Expect = “Expect” “:” 1#expectation
expectation = “100-continue” | expectation-extension
expectation-extension = token [ “=” ( token | quoted-string )
*expect-params ]
expect-params = “;” token [ “=” ( token | quoted-string ) ]

不理解或不能遵守在请求中Expect字段指定的任何期望值的服务器必须响应合适的错误码。如果任何一个客户端期望值都无法满足,则服务器必须响应417(期望失败)状态码;如果请求中出现其它问题,可以使用其它4xx状态值。

此头字段是用可扩展的语法定义的,以便将来进行扩展。如果服务器接收到一个含有Expect头的请求,而该Expect字段中包含有一个服务器不支持的expectation-extension,则服务器必须响应417状态码。

对非引号引用的token的期望值的比较是不区分大小写的,而对引号引用的expectation-extension是大小写敏感的。

Expect机制是逐跳(hop-by-hop)的:也就是说,一个HTTP/1.1代理如果接收到一个它不能满足的期望时必须返回一个417(期望失败)状态。然而,Expect请求头自己是端对端(end-to-end)的;如果请求被转发,它也必须被转发。

许多旧的HTTP/1.0和HTTP/1.1应用都不理解Expect头。

参考8.2.3节了解100(继续)状态码的使用。

14.21 Expires

Expires实体头字段给出了一个日期/时间,在这之后,响应就被认为是过期的。缓存(要么是代理缓存,要么是用户代理)可能不会正常地返回已过期的缓存入口(cache entry),除非它首先经过了源服务器(或有实体新鲜复制的中间缓存)的验证。参考13.2节了解有关过期模型的进一步讨论。

Expires字段的出现并不暗示着原始资源在这时间之时、之前或之后就会改变或断绝存在。

该字段的格式是一个如同在3.3.1节中HTTP-date定义的那样的绝对的日期和时间;它必须使用RFC 1123中的日期格式:

      Expires = "Expires" ":" HTTP-date

其使用案例如下:

      Expires: Thu, 01 Dec 1994 16:00:00 GMT

注意:如果响应中包含有一个Cache-Control字段,该字段含有max-age指令(参考14.9.3节),则该指令会重写Expires字段。

HTTP/1.1客户端和缓存必须将其它无效的日期格式,特别是包含值为0的日期,都视为已过期。

为了标识一个响应是“已经过期的”,源服务器就会发送一个值与Date头字段值相等的Expires头字段(参考13.2.4节中过期计算的规则。)。

为了标识一个响应是“永不过期的”,源服务器就会发送一个值近似为从响应被发送开始一年的时间的Expires字段。

HTTP/1.1服务器不应该发送一个值超过未来一年时间的Expires字段。

响应中带有未来某个时间值(否则默认视为不可缓存)的Expires字段表明该响应是可缓存的,除非已经被Cache-Control头字段
指示过了(参考14.9节)。

14.22 From

From请求头字段,如果已经给定,应该包含一个网络邮箱地址给控制请求用户代理的人类用户。这个地址应该是机器可用的,如同在RFC 822中的“邮箱”定义亦和在RFC 1123中更新的那样:

       From   = "From" ":" mailbox

案例如下:

       From: webmaster@w3.org

这个头字段可能被用于记录的目的,并作为一种标识无效或不必要请求的资源的手段。它不应该被用作为一种访问保护的不安全形式。这个字段的解释就是为了给定的人已经执行了该请求,这个人承担被执行方法的责任。特别地,机器人代理应该包含这个头,这样一来当在接收最终出现问题的时候,可以联系到运行这个机器人的责任人。

此字段中的网络邮件地址可能独立于发布该请求的网络主机地址。例如,当一个请求经过了一个代理,则应该使用原始发布者的地址。

客户端在没有得到用户批准的情况下禁止发送From头字段,因为这样做可能与用户的隐私兴趣或其网站安全策略冲突。我们强烈建议用户有能力在发送请求之前的任何时间都可以禁用、启用或修改此字段的值。

14.23 Host

Host请求头字段指定了被请求资源的网络主机和端口号,与从用户或引用资源(通常是一个HTTP URL,如3.2.2节所述)给定的原始URI中获取的一样。Host字段必须代表原始URL中给定的源服务器或网关的命名权限。它允许源服务器或代理能够区分内部模糊的URL,例如在单个IP地址上的多个主机名称的服务器的根“/”URL。

       Host = "Host" ":" host [ ":" port ] ; Section 3.2.2

没有尾部端口信息的Host意味着为请求服务采用默认端口(例如,HTTP URL默认是“80”)。例如,在源服务器上请求http://www.w3.org/pub/WWW/会合适地包含:

       GET /pub/WWW/ HTTP/1.1
       Host: www.w3.org

客户端必须在所有HTTP/1.1请求消息中包含Host头字段。如果请求URI不包含所请求服务的网络主机名称,那么Host头字段必须给一个空值。HTTP/1.1代理必须确保其转发的任何请求消息都包含有一个合适的唯一标识了被代理请求服务的Host头。所有基于网络的HTTP/1.1服务器都必须给缺少Host头的HTTP/1.1请求响应400(错误请求)状态码。

有关Host的其它要求请参考5.2节或19.6.1.1节。

14.24 If-Match

If-Match请求头字段与一个方法一起使用,使其成为条件字段。从资源处预先获取一个或多个实体的客户端能够通过在If-Match头字段中包含这些实体关联的实体标签的列表来证实这些实体中的某一个是实时的(current)。3.11节定义了实体标签。此特性的目的就是用最低限度的事务开销进行已缓存信息的有效更新。它也可以用在更新请求的时候,为了防止对资源版本的疏忽大意的错误更新。作为特殊案例,值“*”会匹配到资源的任何一个当前实体。

       If-Match = "If-Match" ":" ( "*" | 1#entity-tag )

如果实体标签列表中的某一个匹配到了对相似的GET请求(没有If-Match头)的响应中将要被返回的实体的实体标签,或者给出了“*”且存在任意的该资源下的当前实体,则服务器可能会执行请求方法就如同If-Match头不存在一样。

服务器必须使用强比较函数(参考13.3.3节)来比较在If-Match中的实体标签。

如果没有匹配到实体标签,或者如果给定了“/*”却不存在当前实体,则服务器禁止执行该请求方法,且必须返回412(前置条件失败)响应。当客户端想要阻止一个更新的方法时这个行为是最有用的,比如PUT方法,在客户端最后一次检索请求资源以来,资源已经发生了改变,便无法再修改此资源。

如果请求在没有If- match头字段的情况下会导致2xx或412状态以外的任何结果,那么必须忽略If- match头。

“If-Match:/*”的含义就是:如果源服务器(或者是缓存,可能使用Vary机制,参考14.44节)选择的代表存在,方法就应该被执行,禁止在代表不存在时执行方法。

打算更新资源(例如,PUT请求)的请求可能会包含一个If-Match头来传达:当该If-Match头(单个实体标签)对应的实体不再代表目标资源时禁止执行该方法。这允许用户表明他们不想在对资源的改变不知情的情况下让请求执行成功。例如:

       If-Match: "xyzzy"
       If-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
       If-Match: *

本规范没有定义含有两个头其中一个是If-Match头,另外一个要么是If-None-Match,要么是If-Modified-Since头的请求的结果。

14.25 If-Modified-Since

If-Modified-Since请求头字段与方法一起使用使请求成为条件性的。如果请求变体(the requested variant)在此字段指定的时间以来没有发生修改,则服务器不会返回实体。相反,将会返回一个没有任何消息体的304(not modified)响应。

       If-Modified-Since = "If-Modified-Since" ":" HTTP-date

此字段的案例如下:

       If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT

一个含有If-Modified-Since头且没有Range头的GET请求的实体只有当该实体从If-Modified-Since头中指定的时间开始发生了改变才会被传输。决定这样做的算法包含以下情况:

  1. 如果请求会正常地导致200(OK)状态以外的响应,或传输的If-Modified-Since字段值是无效的,则返回的响应与正常的GET请求一致。超后于服务器当前时间的日期值是无效的。
  2. 如果变体从If-Modified-Since指定的时间以来发生了改变,则响应与普通GET请求无异。
  3. 如果变体从If-Modified-Since指定的有效时间以来没有被修改过,则服务器应该返回304(not modified)响应。

此特性的目的就是让已缓存的信息以最低限度的事务开销来保持高效更新。

注意:Range请求头字段修改了If-Modified-Since的含义;全部细节请参考14.35节。

注意:If-Modified-Since指定的时间是由服务器解释的,该服务器的时钟可能不与客户端同步。

注意:当处理If-Modified-Since字段的时候,一些服务器将会使用精确的日期比较方法而不是小于方法来决定是否返回304(not modified)响应。当为缓存验证发送If-Modified-Since头字段的时候,为了获得最好的结果,我们建议客户端只要有可能都应该使用从之前Last-Modified头字段接收到的精确的日期字符串。

注意:如果客户端在If-Modified-Since字段中使用了一个任意的不是取自于Last-Modified字段的日期用于相同的请求,则客户端应该意识到一个事实:这个日期是基于服务器对时间的理解被解释的。由于客户端和服务器对时间有不同编码,所以客户端需要考虑非同步的时钟和四舍五入的问题。这些问题包括在第一次请求的时间和后续请求的If-Modified-Since指定的时间之间的文档发生改变时的竟态条件的可能性,和If-Modified-Since的日期是来自于未从服务器时钟得到修正的客户端时钟时的时钟相对偏转问题的可能性。由于网络延迟,基于客户端和服务器之间不同时间的修正最多只能是近似的。

本规范未定义同时拥有If-Modified-Since和If-Match(或If-Unmodified-Since)头的请求的结果。

14.26 If-None-Match

If-None-Match请求头字段与方法一起使用让请求成为条件性的。预先从资源处获取到一个或多个实体的客户端可以证实:目前这些实体关联的标签中没有任何一个处于If-None-Match头字段指定的列表当中。此特性的目的就是让已缓存信息以最低限度的事务开销进行高效更新。它也可以用来防止方法(例如,PUT)不经意地对服务器存在但客户端认为不存在的资源的修改。

作为特例,值“*”匹配资源上任何当前的实体。

       If-None-Match = "If-None-Match" ":" ( "*" | 1#entity-tag )

如果这些实体标签中的任何一个匹配到了在此资源上的类似的请求(没有If-None-Match头)的响应中本来要返回的实体的实体标签,或如果给定了“*”且存在该资源的任意的当前实体,则服务器禁止执行该请求方法,除非该资源的修改日期不匹配请求中If-Modified-Since头字段指定的日期。反之,如果请求方法是GET或HEAD,则服务器应该响应304(not modified)状态码,还要包括匹配到的某一个实体的缓存相关的字段(特别是ETag)。对于所有其它请求方法来说,服务器必须响应412(Precondition Failed)状态码。

关于如何确定两个实体标签是否匹配的规则请参考13.3.3节。弱比较方法只能用于GET或HEAD请求。

如果没有匹配到实体标签,则服务器可能会执行该请求方法,就好像If-None-Match头不存在一样,但也必须忽略请求中出现的任何If-Modified-Since头。也就是说,如果没有实体标签匹配,那么服务器禁止响应304。

如果请求在没有If-None-Match头的情况下可能会导致除2xx或304以外的响应,则If-None-Match头必须被忽略。(关于请求中同时出现If-Modified-Since和If-None-Match时的服务器行为的讨论请参考13.3.4节。)

“If-None-Match:*”的含义就是:如果源服务器(或者缓存,可能使用Vary机制,参考14.44节)选择的资源存在,则该请求禁止执行,并且在资源不存在时应该执行。此特性是打算用来防止多个PUT操作之间的竞争。

例如:

       If-None-Match: "xyzzy"
       If-None-Match: W/"xyzzy"
       If-None-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
       If-None-Match: W/"xyzzy", W/"r2d2xxxx", W/"c3piozzzz"
       If-None-Match: *

本规范未定义同时拥有If-None-Match和If-Match(或If-Unmodified-Since)头的请求的结果。

14.27 If-Range

如果客户端在其缓存中拥有实体的部分副本,并且希望能够缓存整个实体,那么它可以使用一个带有Range请求头的条件性GET请求(请求中含有If-Unmodified-Since和If-Match中的一个或两个)。然而,如果这个条件因为实体已经被修改而判定失败,那么客户端就不得不发送第二个请求来获取整个当前实体。

If-Range头允许客户端让第二个请求“短路”。非正式地,它的含义就是“如果实体没有发生改变,就发给我缺失的部分;否则,发给我全部的新的实体”。

        If-Range = "If-Range" ":" ( entity-tag | HTTP-date )

如果客户端没有某个实体的实体标签,但是有一个Last-Modified日期,它可能会在If-Range头中使用这个日期。(服务器可以通过检查不超过两个字符的方式来区分有效的HTTP-date和任意格式的实体标签。)

If-Range头应该只和Range头一起使用,并且当请求不包含Range头或服务器不支持子区间操作(sub-range operation)的时候必须忽略它,

如果If-Range头中给定的实体标签匹配上了该实体的当前实体标签,那么服务器应该返回206(Partial content)响应,并提供指定的实体子区间。如果实体标签不匹配,那么服务器应该返回含有整个实体的200(OK)响应。

14.28 If-Unmodified-Since

If-Unmodified-Since请求头字段与方法一起使用让请求成为条件性的。如果请求的资源从该字段指定的时间开始没有被修改过,则服务器应该执行该请求操作,就好像If-Unmodified-Since头不存在一样。

如果该请求变体在指定的时间以来被修改了,那么服务器禁止执行该请求操作,并且必须响应412(Precondition Failed)。

      If-Unmodified-Since = "If-Unmodified-Since" ":" HTTP-date

字段使用案例如下:

       If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT

如果该请求正常情况下(例如,不含If-Unmodified-Since头)会导致除2xx或412以外的响应,则If-Unmodified-Since应该被忽略。

如果该字段指定的值是无效的,会忽略该字段。

本规范未定义同时拥有If-Unmodified-Since和If-None-Match(或If-Modified-Since)头的请求的结果。

14.29 Last-Modified

Last-Modified实体头字段表明源服务器相信变体最后被修改的日期和时间。

       Last-Modified  = "Last-Modified" ":" HTTP-date

使用案例如下:

       Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT

此头字段的确切含义依赖于源服务器的实现和原始资源的本质。对于文件,它可能只是文件系统的最后修改时间。对于动态包含的实体,它可能是实体组成部分最近的最后修改时间的集合。对于数据库网关,它可能是记录的最后更新日期的时间戳。对于虚拟对象,它可能是内部状态改变的最新的时间。

源服务器禁止发送一个Last-Modified时间比消息生成时间还小的响应。在资源的最后修改时间表示未来某个时间的场景下,源服务器必须用消息的生成日期替换该日期。

源服务器应该以尽可能接近于生成响应Date字段值的时间去获取实体的Last-Modified值。这让消息接收者能够精确地评估实体的修改时间,特别是当实体的修改处于响应生成时间附近时。

HTTP/1.1服务器只要有可能都应该发送Last-Modified头。

14.30 Location

Location响应头字段是用来为了完成请求或标识新资源而重定向接收者到一个除Request-URI以外的地址。对于201(Created)响应,Location就是被请求创建的新资源的位置。对于3xx响应,Location应该表示服务器自动重定向到资源的偏好的URI。此字段值由一个单独的绝对URI组成。

       Location       = "Location" ":" absoluteURI

使用案例如下:

       Location: http://www.w3.org/pub/WWW/People.html

注意:Content-Location头字段区别于Location字段的地方就在于Content-Location标识了封装在请求中的实体的原始位置。因此有可能在响应中同时包含Location和Content-Location字段。关于某些方法的缓存要求也请参考13.10节。

14.31 Max-Forwards

Max-Forwards请求头与TRACE和OPTIONS方法一起提供了一种机制,该机制能够限制代理或网关转发请求到下一个入站服务器的数量。它在客户端试图跟踪一个看起来像是失败或在中链循环的请求链的时候很有用。

       Max-Forwards   = "Max-Forwards" ":" 1*DIGIT

Max-Forwards头的值是一个十进制整数,它表示该请求消息可能被转发的剩余次数。

网关或代理接收者在转发包含Max-Forwards头的TRACE或OPTIONS请求前必须检查并更新此头字段的值。如果接收到的值是零(0),则禁止转发该请求;反之,必须作为最终的接收者进行响应。如果接收到的值大于零,那么转发的消息中必须包含已经更新的Max-Forwards头,此时头字段的值在原来的基础上减1。

对于本规范定义的其它方法和未显式地提及其作为该方法定义的一部分的任何扩展方法都可以忽略Max-Forwards字段。

14.32 Pragma

Pragma通用头字段用来表示可能适用于请求/响应链上任意接收者的特殊实现的指令。以下称之为“编译指示”指令。所有编译指示指令都从协议的角度指定了可选的行为,然而,一些系统可能要求该行为与指令保持一致(意为行为不是可选的,必须遵守)。

       Pragma            = "Pragma" ":" 1#pragma-directive
       pragma-directive  = "no-cache" | extension-pragma
       extension-pragma  = token [ "=" ( token | quoted-string ) ]

当消息中提供了no-cache指令,即使应用已经有一个该请求的缓存副本了,还是应该转发此请求到源服务器上。Pragma:no-cache指令与no-cache缓存指令(参考14.9节)的语义一样,此处的定义是为了向后兼容HTTP/1.0。当不能缓存的请求发送到不知道是否服从HTTP/1.1规范的服务器时,客户端应该包含这两个头字段。

编译指示指令必须通过代理或网关应用(意为代理或网关在转发请求时必须也同时转发此指令),而不管它们对该应用的意义,因为这些指令可能适用于请求/响应链上的所有接收者。不可能为一个特殊的接收者指定一个Pragma指令。然而,接收者应该忽略与其不相关的Pragma指令。

HTTP/1.1缓存应该将Pragma:no-cache指令当作客户端发送了Cache-Control:no-cache指令对待。HTTP将不会再定义新的Pragma指令。

注意:由于没有实际指定响应中“Pragma:no-cache”指令的含义,所有并没有提供响应中“Cache-Control:no-cache”指令的可靠替代品。

14.33 Proxy-Authenticate

Proxy-Authenticate响应头字段必须作为一个407(需要代理认证)响应的一部分被发送。此字段值由一个挑战(challenge)组成,它表示适用于此Request-URI代理的认证模式和参数。

       Proxy-Authenticate  = "Proxy-Authenticate" ":" 1#challenge

“HTTP Authentication:Basic and Digest Access Authentication”描述了HTTP的访问授权处理。不像WWW-Authenticate,Proxy-Authenticate头字段只适用于当前连接,并且不应该传递给下游客户端。然而,中间代理可能通过它们向下游客户端发送请求以获取自己的证书,在某些情况下,这看起来就像是代理正在转发Proxy-Authenticate头字段一样。

14.34 Proxy-Authorization

Proxy-Authorization请求头允许客户端向需要认证的代理标识它自己(或其用户)。Proxy-Authorization字段值由为了代理和(或)被请求资源的领域的用户代理的认证信息组成。

       Proxy-Authorization     = "Proxy-Authorization" ":" credentials

“HTTP Authentication:Basic and Digest Access Authentication”描述了HTTP的访问授权处理。不像Authorization,Proxy-Authorization头字段只适用于强烈要求用Proxy-Authorization字段进行认证的下一个出站代理。当在请求/响应链中使用了多重代理时,Proxy-Authorization头字段会被希望接收到证书的第一个出站代理消耗。如果是多个代理合作地认证一个请求,那么一个代理可能会把从客户端请求获取的证书转发给下一个代理。

14.35 Range

14.35.1 Byte Ranges

因为所有HTTP消息中的实体都是以字节序列表示的,所以对任何HTTP实体来说,字节区间(byte range)都是有意义的。(然而,并不是所有客户端和服务器都需要支持字节区间操作。)

HTTP规范中的字节区间适用于实体体(entity-body)的字节序列(没必要与消息体(message-body)一致)。

       ranges-specifier = byte-ranges-specifier
       byte-ranges-specifier = bytes-unit "=" byte-range-set
       byte-range-set  = 1#( byte-range-spec | suffix-byte-range-spec )
       byte-range-spec = first-byte-pos "-" [last-byte-pos]
       first-byte-pos  = 1*DIGIT
       last-byte-pos   = 1*DIGIT

byte-range-spec中的first-byte-pos给出了一个区间中第一个字节的字节偏移量(byte-offset)。last-byte-pos给出了区间中最后一个字节的字节偏移量。也就是说,指定的字节位置是首尾包含的。字节偏移量从0开始。

如果提供了last-byte-pos,则它的值必须大于或等于first-byte-pos,否则byte-range-spec在语法结构上就是无效的。接收者必须忽略包含一个或多个在语法结构上无效的byte-range-specbyte-range-set字段。

如果last-byte-pos缺席,或其值大于或等于实体体的当前长度,则last-byte-pos被认为是小于当前实体长度的值。

通过last-byte-pos的选择,客户端可以在不知道实体长度的情况下限制检索字节的数量。

       suffix-byte-range-spec = "-" suffix-length
       suffix-length = 1*DIGIT

suffix-byte-range-spec被用来指定实体体的后缀,用suffix-length指定的值表示。(即,这种格式指定了实体体的最后N个字节。)如果实体比指定的suffix-length更短,就使用全部实体。

如果一个在语法结构上是有效的至少包含一个byte-range-spec,该byte-range-specfirst-byte-pos小于实体的当前长度,或至少包含一个拥有非0suffix-lengthsuffix-byte-range-spec,则该byte-range-set是满意的;否则,就是不满意的。如果byte-range-set不满足要求,则服务器应该响应416(请求区间不满足);否则,服务器应该响应一个包含实体满意区间的206状态码。

byte-ranges-specifier使用案例如下(假设实体长度为10000):

      - The first 500 bytes (byte offsets 0-499, inclusive):bytes=0-499
      - The second 500 bytes (byte offsets 500-999, inclusive):bytes=500-999
      - The final 500 bytes (byte offsets 9500-9999, inclusive):bytes=-500
      - Or bytes=9500-
      - The first and last bytes only (bytes 0 and 9999):  bytes=0-0,-1
      - Several legal but not canonical specifications of the second 500
        bytes (byte offsets 500-999, inclusive):
         bytes=500-600,601-999
         bytes=500-700,601-999

14.35.2 Range Retrieval Requests

使用条件性或非条件性GET方法的HTTP检索请求与Range请求头一起使用可以请求一个或多个实体的子区间(sub-range),而非全部实体,Range头应用于作为请求结果返回的实体。

      Range = "Range" ":" ranges-specifier

服务器可以忽略Range头。然而,HTTP/1.1源服务器和中间缓存只要有可能都应该支持字节区间,因为Range头支持部分失败传输的有效恢复,和支持大型实体的部分有效检索。

如果服务器支持Range头并且指定的一个或多个区间适合请求实体,则:

  • 在非条件性GET请求中的Range头的出现修改了当请求成功时的响应。换句话说,携带状态码206(Partial Content)的响应替换了200(OK)的响应。
  • 在条件性GET请求(一个使用了其中一个或两个If-Modified-Since和If-None-Match头,或使用了其中一个或两个If-Unmodified-Since和If-Match头的请求)中的Range头的出现修改了当请求成功并且条件为真时的响应。它并不影响条件为假时返回的304(Not Modified)响应。

在某些情况下,除Range头外,使用If-Range头(参考14.27节)可能更加合适。

如果支持区间操作的代理接收到了一个Range头,并转发了此请求到一个入站服务器上,然后接收到了整个实体,那么此代理应该只返回请求的区间给客户端(而不是整个实体)。如果接收到的整个实体与代理的缓存配置策略一致的话,代理应该将响应缓存起来。

14.36 Referer

Referer(实际应该是“referrer”,这里的头字段是个拼写错误,但为了兼容,沿用至今)请求头字段允许客户端为了服务器的利益指定来自于获取Request-URI的资源的地址。Referer请求头允许服务器为了兴趣、日志、缓存的优化等等生成资源的反向链接的列表。它也允许为了维护而跟踪过时的或拼写错误的链接。当Request-URI来自于一个没有自己URI的资源的时候(例如,来自用户键盘的输入),禁止在请求中发送Referer头。

       Referer        = "Referer" ":" ( absoluteURI | relativeURI )

案例:

       Referer: http://www.w3.org/hypertext/DataSources/Overview.html

如果字段值是一个相对路径,它就应该相对于Request-URI被解释。该字段值禁止包含碎片。有关安全性的考虑请参考15.1.3节。

14.37 Retry-After

Retry-After响应头字段能够和503(服务不可用)响应一起使用来表示该服务对于请求客户端来说还有多久可用。此字段也可与任何3xx(转发)响应一起使用来表示用户代理在转发请求之前需要等待的最短时间。字段的值可以要么是一个HTTP-date,要么是一个单位是秒且在响应时间之后的十进制整数。

       Retry-After  = "Retry-After" ":" ( HTTP-date | delta-seconds )

使用的两个案例如下:

       Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
       Retry-After: 120

在后面一个案例中,延迟是2分钟。

14.38 Server

Server响应头字段包含了源服务器用来处理请求的软件信息。该字段可以包含多个产品令牌和用于识别服务器和任何重大子产品的注释。这些产品令牌是以辨别应用的重要性的顺序列出。

       Server         = "Server" ":" 1*( product | comment )

案例:

       Server: CERN/3.0 libwww/2.17

如果响应被代理转发,则代理禁止修改Server响应头。否则,应该包含一个Via字段(如14.45节所述)。

注意:暴露服务器使用软件的特定版本号可能使服务器比已知包含有安全漏洞的软件更易遭受攻击。我们鼓励服务器的实现者将此字段变为一个可配置的选项。

14.39 TE

TE请求头字段表示希望在响应中接收的扩展传输编码和是否愿意接收使用“chunked”传输编码的尾部字段。该字段值可以由关键字“trailers”和(或)以逗号分割的含有可选接收参数(如3.6节所述)的扩展传输编码名称的列表组成。

       TE        = "TE" ":" #( t-codings )
       t-codings = "trailers" | ( transfer-extension [ accept-params ] )

关键字“trailers”的出现表示客户端愿意接收使用“chunked”传输编码的尾部字段,如3.6.1节所定义的那样。此关键字保留用作传输编码的值,即使它自己并不表示一个传输编码。

使用案例如下:

       TE: deflate
       TE:
       TE: trailers, deflate;q=0.5

TE头字段只适用于即时连接。因此,只要HTTP/1.1消息中出现了TE头,那么Connection头字段(参考14.10节)必须提供该关键字。

服务器根据TE头使用以下规则判断某个传输编码是否是可接收的:

  1. “chunked”传输编码始终是可接收的。如果列出了“trailers”,则表示客户端愿意为了它自己和下游客户端接收出现在分块响应中的尾部字段。言下之意就是,如果给定了“trailers”,客户端就说是要么所有下游客户端都愿意接收转发的响应中的尾部字段,要么就为了下游接收者尝试去缓冲响应。
    注意:HTTP/1.1没有定义任何限制“chunked”响应大小的以至于客户端能够确信缓冲全部响应的方法。
  2. 如果被检测的传输编码是TE头中传输编码列表中的一员,则该编码是可接收的,除非它伴随着一个为0的质量值(即,q=0)。(如3.9节中定义,为0的质量值表示“不可接受”)
  3. 如果有多个传输编码都可接受,则拥有最高的非0质量值的传输编码优先。“chunked”传输编码的质量值总是1。

如果TE字段值为空或没有提供此字段,则唯一的传输编码就是“chunked”。没有传输编码的消息总是可接收的。

14.40 Trailer

Trailer通用头字段表示给定的头字段集出现在使用chunked传输编码的消息的尾部。

       Trailer  = "Trailer" ":" 1#field-name

HTTP/1.1消息应该在使用chunked传输编码的消息中包含有一个非空的Trailer头字段。这样做的目的就是让接收者知道在消息尾部哪个头字段是它们想要的。

如果没有提供Trailer字段,则消息的尾部就不应该包含任何字段。有关在chunked传输编码中尾部字段的使用限制请参考3.6.1节。

以下头字段禁止出现在Trailer头中:

  • Transfer-Encoding
  • Content-Length
  • Trailer

14.41 Transfer-Encoding

Transfer-Encoding(传输编码)通用头字段表示消息体应用的何种转换类型以便安全地在发送者和接收者之间传输。它不同于内容编码的地方就是传输编码是消息而不是实体的一个属性。

     Transfer-Encoding  = "Transfer-Encoding" ":" 1#transfer-coding

3.6节定义了传输编码。案例如下:

     Transfer-Encoding: chunked

如果多个编码应用到实体,则传输编码必须以它们被应用的顺序列出。有关编码参数的额外信息可以在其它非本规范定义的实体头字段里找到。

许多更旧的HTTP/1.0应用并不理解Transfer-Encoding头。

14.42 Upgrade

Upgrade通用头允许客户端指定它额外支持的通信协议,和在服务器适合切换的情况下,更愿意使用的通信协议。服务器必须与101(协议切换)响应一起使用Upgrade头来表明哪个协议被切换了。

       Upgrade        = "Upgrade" ":" 1#product

例如:

       Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11

Upgrade头字段打算提供一种从HTTP/1.1过渡到其它不兼容协议的简单机制。它通过让客户端通知服务器其期望使用的其它协议来达成,比如更高主版本号的HTTP更新版本,即使当前请求是用HTTP/1.1生成的。通过让客户端用更加一般性支持的协议来初始化请求同时向服务器表明其更愿意使用的一个“更好的”(哪个是“更好的”由服务器决定,可能根据请求方法和(或)请求资源的本质)可用的协议,可以缓和在不兼容协议之间过渡的困难。

Upgrade头字段只适用于切换在现行的传输层连接之上的应用层协议。不能使用Upgrade头来强调协议更改;服务器对其的接收和使用是可选的。协议切换之后的应用层通信的能力和本质彻底地依赖于新选择的协议,尽管切换协议后的第一个行动就是响应包含此Upgrade头的初始化HTTP请求。

Upgrade头字段只适用于即时连接。因此,只要HTTP/1.1消息中出现了Upgrade头,就必须也在Connection头字段(参考14.10节)中提供Upgrade关键字。

Upgrade头字段不能在不同的连接之上表示协议的切换。为了此目的,建议使用301、302、303或305重定向响应。

本规范仅定义供超文本传输协议系列使用的协议名称“HTTP”,如第3.1节HTTP版本规则和本规范的未来更新所定义。任何令牌都可以用作协议名称;然而,只有客户端和服务器将此名称关联到相同的协议才有用。

14.43 User-Agent

User-Agent请求头包含了有关发起请求的用户代理的信息。这是为了统计目的、跟踪协议违反情况和自动识别用户代理(以便对响应进行裁剪以避免特定的用户代理限制)。用户代理应该在请求中包含此字段。此字段可以包含多个产品令牌(参考3.8节)和用于识别代理和任何组成代理重要部件的子产品的注释。按照惯例,这些产品令牌以识别应用的重要性的顺序列出。

       User-Agent     = "User-Agent" ":" 1*( product | comment )

案例:

       User-Agent: CERN-LineMode/2.15 libwww/2.17b3

14.44 Vary

Vary字段值表示一堆请求头字段的集合,这些头字段可以完全确定当响应未过期时,是否允许缓存在不需要重新验证的情况下将此响应用于后续的请求。对于不可缓存或过期的响应,Vary字段建议用户代理有关选择代表的条件。值为“*”的Vary字段意味着缓存不能从后续请求的请求头字段中决定此响应是否是合适的代表。有关缓存使用Vary头字段的方法请参考13.6节。

       Vary  = "Vary" ":" ( "*" | 1#field-name )

HTTP/1.1服务器应该在任何从属于服务器驱动协商的可缓存的响应中包含Vary头字段。这样做的目的在于让缓存在目标资源上正确地解释未来的请求并通知用户代理有关协商的出现。服务器也可以在从属于服务器驱动协商但不可缓存的响应中包含Vary头字段,因为这可能会向用户代理提供关于响应在响应时发生变化的维度的有用信息。

Vary头字段是由一堆字段名组成的,这些字段名表明为响应所选择的代表基于一种选择算法,该算法在选择最合适的代表时只考虑列出的请求头字段值。缓存可以假设,在响应未过期的时间里,用相同的字段名称列表在未来的请求中可以做出相同的选择。

Vary中给定的字段名称不限于本规范定义的标准请求头字段。字段名是忽略大小写的。

Vary字段值“*”表示未指定的参数(不限于请求头字段,例如,客户端的网络地址)在响应代表的选择中扮演了一种角色。“*”值禁止由代理服务器生成;只能由源服务器生成。

14.45 Via

Via通用头字段必须被网关和代理使用以表明在请求时的用户代理和服务器(或在响应时的源服务器和客户端)之间的中间协议和接收者。它类似于RFC 822中定义的“Received”字段,并打算用来跟踪消息转发、避免请求循环和识别请求/响应链上的所有发送者的协议能力。

      Via =  "Via" ":" 1#( received-protocol received-by [ comment ] )
      received-protocol = [ protocol-name "/" ] protocol-version
      protocol-name     = token
      protocol-version  = token
      received-by       = ( host [ ":" port ] ) | pseudonym
      pseudonym         = token

received-protocol表示在请求/响应链的每个阶段被服务器或客户端接收到的消息的协议版本。当消息被转发的时候协议版本就添加到Via字段值的后面,这样有关上游应用协议能力的信息就对所有接收者保持可见。

protocol-name只有当是HTTP协议的时候是可选的。received-by字段通常是随后转发该消息的接收服务器或客户端的主机和可选的端口号。然而,如果真实的主机是敏感信息,则可以用一个假名替换。如果没有指定端口号,则可以假定使用接收协议的默认端口号。

多个Via字段值表示转发该消息的每一个代理或网关。每一个接收者必须添加它自己的信息,这样一来根据转发应用的序列最终的结果就是有序的。

Via头字段中可以使用注释以标识接收代理或网关的软件,类似于User-Agent和Server头。然而,在Via头字段中所有注释都是可选的,任何接收者在转发消息之前都可以移除它们。

例如,一个来自于HTTP/1.0的用户代理的请求消息被发送给一个代号为“fred”的内部代理,该代理使用HTTP/1.1将此请求转发给一个地址为nowhere.com的公共代理,此公共代理以将该请求转发给地址为www.ics.uci.edu的源服务器来完成请求。那么被www.ics.uci.edu接收到的请求就含有以下Via头字段:

       Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)

通过网络防火墙而作为传送门使用的代理和网关默认不应该在防火墙区域内转发主机的名称和端口。只有当被显式地启用时才能够传播这些信息。如果没有启用,则任何绑定到防火墙上的received-by主机都应该用一个合适的假名替换。

对于有强烈隐私要求想要隐藏内部结构的组织来说,代理可以结合拥有相同received-protocol值的Via头字段的有序序列变成单个入口。例如:

       Via: 1.0 ricky, 1.1 ethel, 1.1 fred, 1.0 lucy

可以合并为:

       Via: 1.0 ricky, 1.1 mertz, 1.0 lucy

应用一般不应该合并多个入口,除非它们都处于同一个组织控制之下,并且所有主机都已经用假名替换过了。应用禁止合并不同协议的入口。

14.46 Warning

Warning通用头字段用来携带有关消息状态或转换的额外信息,这些信息可能不会在消息中反映出来。这些信息通常用来警告有关来自于应用到消息体上的缓存操作或转换的语义透明性的缺失。

Warning使用以下方式与响应一起发送:

       Warning    = "Warning" ":" 1#warning-value
       warning-value = warn-code SP warn-agent SP warn-text
                                             [SP warn-date]
       warn-code  = 3DIGIT
       warn-agent = ( host [ ":" port ] ) | pseudonym
                       ; the name or pseudonym of the server adding
                       ; the Warning header, for use in debugging
       warn-text  = quoted-string
       warn-date  = <"> HTTP-date <">

一个响应可以携带多个Warning头。

warn-text应该使用自然语言和对接收响应的人类用户最可能理解的字符集。这个决定基于任何可用的知识,比如缓存或用户的位置、请求中的Accept-Language字段、响应中的Content-Language字段等等。默认的语言是英语,默认的字符集是ISO-8859-1。

如果使用了默认以外的字符集,就必须使用RFC 2047中描述方法将其编码在warn-text中。

Warning头通常可以适用于任何消息,然而,一些特殊的warn-codes只针对于特殊的缓存并且只能用于响应消息。新的Warning头应该添加到现有Warning头的后面。缓存禁止删除接收消息中的任何Warning头。然而,如果缓存成功地验证了一个缓存条目,它就应该移除任何预先附加到该条目上的Warning头,除了为特殊Warning码指定的以外。然而就应该添加任何接收到的Warning头到已验证的响应中。换句话说,Warning头就是一些会附加到最近相关响应的字段。

当有多个Warning头附加到响应中时,用户代理应该用它们出现在响应中的顺序尽可能多地通知到用户。如果不能全部通知到用户,用户代理应该使用以下这些启发法:

  • 与响应相近的Warning比其它较远的头有更高的优先级。
  • 使用用户偏好字符集的Warning比使用其它字符集但拥有一致的warn-codeswarn-agents字段的头有更高的优先级。

生成多种Warning头的系统应该用此用户代理已有的逻辑给它们排序。

有关警告缓存行为的要求在13.1.2节中有规定。

这里有一个现有定义的警告码的列表,每一个都带有一个英语说明的文本和其含义的描述。

  • 110。响应过期了。只要响应过期,就应该包含此警告头。
  • 111。重新验证失败。如果缓存因为由于无法到达服务器而试图重新验证响应失败时而返回一个过期的响应。
  • 112。断开连接操作。如果缓存故意断开网络一段时间。
  • 113。启发式过期。如果缓存启发式地选择了一个新鲜度生命周期大于24小时的响应,且该响应已经存在超过24小时。
  • 119。复杂警告。警告文本可能包含任意信息发送给人类用户或者日志记录。接收到此警告的系统禁止采取任何自动化行为,除发送此警告给用户之外。
  • 214。中间缓存或代理必须添加的所应用的转换。如果该应用的转换改变了响应的内容编码或媒体类型、或者响应的实体,除非该警告已经出现在响应中了。
  • 299。复炸的持久警告。警告文本可能包含任意信息发送给人类用户或者日志记录。接收到此警告的系统禁止采取任何自动化行为。

如果一个实现者接收到一个含有warn-date的Warning头的消息,且该警告日期的值与响应中Date字段的值不一致,那么在此消息被存储、转发或使用之前必须删除该警告头的warning-value(这阻止了Warning头字段的幼稚缓存的糟糕结果)。如果因为这个原因所有的警告值都被删除了,那么Warning头也必须被删除。

14.47 WWW-Authenticate

WWW-Authenticate响应头必须被包含在401(未认证)响应消息中。此字段值由至少一个挑战(challenge)组成,该挑战表明了认证模式和应用到Request-URI上的参数。

       WWW-Authenticate  = "WWW-Authenticate" ":" 1#challenge

“HTTP Authentication:Basic and Digest Access Authentication”文档描述了HTTP访问授权过程。我们建议用户代理在解析WWW-Authenticate字段时要特别小心,因为它可能包含多个挑战,或如果提供了多个WWW-Authenticate头,单个挑战的内容可以包含一个以逗号分割的授权参数。

15 安全考虑

本章节意为是要通知应用开发者、信息提供者和本文档描述的HTTP/1.1安全限制的用户。讨论不包含相关问题的最终解决方案,尽管它为减少安全风险提供了一些建议。

15.1 个人信息

HTTP客户端经常能够了解大量的个人信息(例如,用户名、地理位置、邮件地址、密码、加密密钥等等),应该非常小心地防止通过其它来源的HTTP协议无意识地泄露了这些信息。我们非常强烈地建议为用户提供一个方便的接口用来控制这些信息的传播,该接口的设计者和实现者要特别小心这块区域。历史证明此区域产生的错误经常造成严重的安全和(或)隐私问题,并且为实现者的公司产生非常不利的影响。

15.1.1 服务器日志信息的滥用

服务器在适当的位置保存了有关用户请求的个人数据,这些数据可能标识了用户的读取模式或感兴趣的主题。这些信息自然明显是机密的且它们的处理在某些国家是被法律约束的。使用HTTP协议提供数据的人们有责任确保这些材料在没有得到公布结果辨认的任何个人的许可下不会被分散。

15.1.2 敏感信息的传输

和任何一般数据传输协议一样,HTTP协议不能调节传输的数据内容,也不存在一个先天的方法可以决定任何给定请求内容的任何特殊数据块是否是敏感的。因此,应用程序应该为信息的提供者提供尽可能多的对这些信息的控制。在此上下文中有四个头字段值得特别关注:Server、Via、Referer和From。有关这些头字段的详细注意事项请参考本规范中它们对应的定义。

我们建议,尽管没有要求,为用户提供一个方便的开关接口用于开启或关闭From和Referer信息的发送。

User-Agent(14.43节)或Server(14.38节)头字段有时候可以用来判断一个特殊的客户端或服务器是否有一个可被利用的特殊安全漏洞。不幸的是,同样的信息经常被用于其它HTTP目前没有更好机制的有价值的目的。

15.1.3 URI敏感信息的编码

因为一个链接的来源可能是私有信息或者可能暴露出其它的私有信息来源,我们强烈建议用户有能力选择是否发送Referer字段。例如,浏览器客户端可以有一个为开放浏览或匿名浏览的开关,这些开关可以分别地开启或关闭Referer和From信息的发送。

如果涉及的页面使用了安全的协议传输,客户端不应该在一个不安全的HTTP请求中包含一个Referer头。
使用HTTP协议的服务作者不应该使用GET基本形式提交敏感数据,因为会导致这些数据编码在请求URI中。许多现有的服务器、代理和用户代理都会在某个地方记录请求URI,而这些地方可能被第三方可见。服务器可以使用基于POST的提交替换。

15.1.4 连接到Accept头的隐私问题

Accept请求头可以向被访问的所有服务器暴露用户信息。特别是Accept-Language头可以显示用户认为属于私有性质的信息,因为对特定语言的理解往往是与某一特定种族的成员关系密切。我们强烈建议提供选项配置Accept-Language头内容的用户代理允许配置处理过程中携带一个信息,该信息能够让用户意识到涉及隐私的缺失。

让用户代理限制隐私缺失的一个方法就是默认不发送Accept-Language头,通过检测服务器生成的任何Vary响应头,如果检测到就询问用户是否开始发送Accept-Language头到服务器,这样的发送可以改善服务的质量。在每个请求中发送的精心设计的用户自定义accept头字段(特别是如果它们包含质量值)可以被服务器用作相对可靠和长期有效的用户标识符。这样的用户标识符将允许内容提供者进行点击轨迹跟踪,并允许协作的内容提供者匹配单个用户的跨服务器点击轨迹或表单提交。请注意,对于许多没有使用代理的用户,运行用户代理的主机的网络地址也将作为长期用户标识符。在使用代理来增强隐私的环境中,用户代理应该在向最终用户提供accept标头配置选项方面保持保守。作为一种极端的隐私措施,代理可以过滤中继请求中的accept标头。

15.2 基于文件和路径名的攻击

HTTP源服务器的实现应该小心地将HTTP请求返回的文档限制为仅供服务器管理员使用的文档。如果HTTP服务器将HTTP uri直接转换为文件系统调用,则服务器必须特别注意不要提供不打算传递给HTTP客户机的文件。例如,UNIX、Microsoft Windows和其他操作系统使用“…”作为路径组件来指示当前目录级别之上的目录级别。在这样的系统上,HTTP服务器必须禁止请求uri中的任何此类构造,否则它将允许访问那些打算通过HTTP服务器访问的资源之外的资源。类似地,仅在服务器内部引用的文件(如访问控制文件、配置文件和脚本代码)必须受到保护,以避免不适当的检索,因为它们可能包含敏感信息。经验表明,此类HTTP服务器实现中的小错误已经变成了安全风险。

15.3 DNS欺骗

使用HTTP的客户端严重依赖域名服务,因此通常容易受到基于故意将IP地址和DNS名称错误关联的安全攻击。在假定IP号/DNS名称关联持续有效时,客户需要谨慎。

特别是,HTTP客户机应该依赖它们的名称解析器来确认IP号/DNS名称关联,而不是缓存以前主机名查找的结果。许多平台已经可以在适当的时候在本地缓存主机名查找,应该将它们配置为这样做。但是,只有当名称服务器报告的TTL(生存时间)信息使缓存的信息可能仍然有用时,缓存这些查找才是合适的。

如果HTTP客户机缓存主机名查找的结果以实现性能改进,它们必须观察DNS报告的TTL信息。

如果HTTP客户机不遵守这一规则,那么当以前访问的服务器的IP地址发生更改时,它们可能被欺骗。随着网络重编号预计将越来越普遍的[24],这种形式的攻击的可能性将增长。因此,观察这个需求可以减少这个潜在的安全漏洞。

这一要求还改进了使用相同DNS名称的复制服务器的客户机的负载平衡行为,并减少了用户在访问使用该策略的站点时发生故障的可能性。

15.4 Location头和欺骗

如果一台单独的服务器支持多个互相不信任的组织,那么它就必须检查在这些组织控制之下生成的响应中Location和Content-Location头的值,以确保它们不会让没有权限的资源无效。

15.5 Content-Disposition问题

RFC 1806,HTTP中经常实现的Content-Disposition头就是它这里衍生出来的,有一大堆非常严重的安全注意事项。Content-Disposition不是HTTP标准的一部分,但因为其被广泛实现,我们就为用户编制了它的使用方法和相关风险。有关详情请参考RFC 2183。

15.6 认证证书和闲置客户端

现有的HTTP客户端和用户代理通常都无限期地保留认证信息。HTTP/1.1没有给服务器提供一个方法来指导客户端抛弃这些缓存的证书。证书缓存可以干涉应用安全模型的情况包括但不限于以下几种:

  • 已经闲置的客户端为了证书的延长期会遵循服务器可能希望导致客户端的方式重新提示用户。
  • 包含有一个会话终止指示的应用在应用的服务端“知道”没有更深的理由让客户端保留证书之后。

目前正处于单独的研究当中。关于这个问题有许多可变通的方法,我们鼓励在屏幕保护、闲置超时和其它能够减轻此问题内在的安全问题的密码保护的使用。特别地,我们鼓励缓存证书的用户代理提供一个容易访问的机制在用户控制之下来丢弃已缓存的证书。

15.7 代理和缓存

从本质上讲,HTTP代理是中间人,它为中间人攻击提供了机会。代理运行的系统受到攻击可能会导致严重的安全和隐私问题。代理可以访问与安全相关的信息、关于单个用户和组织的个人信息,以及属于用户和内容提供者的专有信息。一个被破坏的代理,或一个不考虑安全性和隐私的实现或配置的代理,可能会被用于各种潜在的攻击。

代理操作符应该像保护包含或传输敏感信息的任何系统一样保护运行代理的系统。特别是,从代理服务器收集的日志信息通常包含高度敏感的个人信息和/或组织信息。应仔细保护日志信息,并制定和遵循适当的使用指南。

缓存代理提供了额外的潜在漏洞,因为缓存的内容是恶意攻击的目标。由于缓存内容在HTTP请求完成后仍然存在,因此对缓存的攻击可能会在用户认为信息已从网络中删除后很久才会显示信息。因此,缓存内容应该作为敏感信息进行保护。

代理实现者应该考虑他们的设计和编码决策以及他们提供给代理操作人员的配置选项(特别是默认配置)的隐私和安全影响。

代理的用户需要知道,他们并不比运行代理的人更值得信任;HTTP本身不能解决这个问题。
在适当的情况下,明智地使用密码学可能足以防止广泛的安全和隐私攻击。这种加密超出了HTTP/1.1规范的范围。

15.7.1 代理上的拒绝服务攻击

它们确实存在,而且很难防御。研究仍在继续。当心


  1. Hypertext Transfer Protocol – HTTP/1.1 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值