Internet Engineering Task Force (IETF) R. Fielding, Editor
Request for Comments: 9110 Adobe
Obsoletes: 2818, 7230, 7231, 7232, 7233, 7235, 7538, 7615, 7694 M. Nottingham, Editor
STD: 97 Fastly
Updates: 3864 J. Reschke, Editor
Category: Standards Track greenbytes
ISSN: 2070-1721 June 2022

HTTP 语义


摘要

超文本传输协议(HTTP)是一种无状态的应用层协议,用于分布式、协作型的超文本信息系统。本文档描述了HTTP的整体架构,确立了通用术语,并定义了所有版本共享的协议相关内容。该定义包括核心协议要素、可扩展机制,以及“http”和“https”统一资源标识符(URI)方案。

本文件更新了RFC 3864,并取代了RFC 2818、7231、7232、7233、7235、7538、7615、7694及7230的部分内容。

本备忘录的状态

这是互联网标准轨道文档。

本文档由互联网工程任务组(IETF)制定。它代表了IETF社区的共识。该文档已接受公开评审,并获得互联网工程指导组(IESG)的批准发布。关于互联网标准的更多信息,请参见RFC 7841第2节

关于本文档的当前状态、任何勘误以及如何反馈的信息,请访问https://www.rfc-editor.org/info/rfc9110

Copyright Notice

Copyright (c) 2022 IETF Trust and the persons identified as the document authors. All rights reserved.

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.

This document may contain material from IETF Documents or IETF Contributions published or made publicly available before November 10, 2008. The person(s) controlling the copyright in some of this material may not have granted the IETF Trust the right to allow modifications of such material outside the IETF Standards Process. Without obtaining an adequate license from the person(s) controlling the copyright in such materials, this document may not be modified outside the IETF Standards Process, and derivative works of it may not be created outside the IETF Standards Process, except to format it for publication as an RFC or to translate it into languages other than English.

1. 简介

1.1. 目的

超文本传输协议(HTTP)是一系列无状态、应用层的请求/响应协议,这些协议共享通用接口、可扩展语义以及自描述消息,从而实现与基于网络的超文本信息系统的灵活交互。

HTTP 通过向客户端呈现与所提供资源类型无关的统一接口,隐藏了服务实现的细节。同样,服务器也无需了解每个客户端的具体用途:一个请求可以被单独考虑,而无需与特定客户端类型或预定的应用步骤序列相关联。这使得通用实现能够有效应用于多种不同场景,降低了交互复杂性,并支持系统的独立演进。

HTTP 还被设计用于作为中介协议,在该协议中,代理和网关可以将非 HTTP 的信息系统转换为更通用的接口。

这种灵活性的一个结果是,协议无法用接口背后发生的事情来定义。相反,我们只能限定通信的语法、通信的意图,以及接收方的预期行为。如果将通信单独考虑,则成功的操作应当在服务器所提供的可观察接口中反映出相应的变化。然而,由于多个客户端可能并行并且可能目标相悖,我们无法要求这些变化在单一响应的作用域之外可被观察到。

1.2. 历史与演进

自 1990 年问世以来,HTTP 一直是万维网的主要信息传输协议。它最初是一种简单的低延迟请求机制,只有一种方法(GET),用于请求传输通过路径名标识的假定超文本文档。随着 Web 的发展,HTTP 被扩展为在消息中封装请求和响应,使用类似 MIME 的媒体类型传输任意数据格式,并通过中介路由请求。这些协议最终被定义为 HTTP/0.9 和 HTTP/1.0(见[HTTP/1.0])。

HTTP/1.1 旨在完善协议功能,同时保持与现有基于文本的消息语法的兼容性,提高其在 Internet 上的互操作性、可扩展性和健壮性。这包括用于固定和动态(分块)内容的基于长度的数据分隔符、一致的内容协商框架、用于条件请求的不透明验证器、更好的缓存一致性的缓存控制、用于部分更新的范围请求,以及默认的持久连接。HTTP/1.1 于 1995 年引入,并于 1997 年作为标准轨道文档发布[RFC2068],1999 年修订[RFC2616],2014 年再次修订([RFC7230][RFC7235])。

HTTP/2([HTTP/2])在现有 TLS 和 TCP 协议之上引入了多路复用会话层,以高效字段压缩和服务器推送方式交换并发 HTTP 消息。HTTP/3([HTTP/3])则通过使用 QUIC 作为基于 UDP 的安全多路复用传输层取代了 TCP,实现了并发消息的更高独立性。

HTTP 的这三大主版本都依赖于本文档定义的语义。它们没有相互取代,因为每个版本在不同使用场景下都有其独特优势和局限。实现应根据实际场景选择最合适的传输和消息语法。

本次 HTTP 修订将语义定义(本文档)与缓存([CACHING])与当前的 HTTP/1.1 消息语法([HTTP/1.1])分离,以便每个主要协议版本能够独立发展,同时引用相同的核心语义。

1.3. 核心语义

HTTP 通过发送操作或传输表示(第 3.2 节)的消息,为与资源(第 3.1 节)的交互提供了统一接口——无论其类型、性质或实现如何。

每个消息要么是请求,要么是响应。客户端构造请求消息以表达自身意图,并将这些消息路由到指定的源服务器。服务器监听请求,解析每条收到的消息,结合目标资源解释消息语义,并用一个或多个响应消息对请求作出回应。客户端检查收到的响应,以确认自己的意图是否实现,并根据收到的状态码和内容决定后续操作。

HTTP 语义包括每个请求方法定义的意图(第 9 节)、请求首部字段中可能描述的语义扩展、描述响应的状态码(第 15 节),以及响应字段中可能包含的其他控制数据和资源元数据。

语义还包括描述内容如何被接收方解释的表示元数据、可能影响内容选择的请求首部字段,以及统称为内容协商第 12 节)的各种选择算法。

1.4. 被本文件废止的规范

表 1
标题 参考文献 参见
HTTP Over TLS [RFC2818] B.1
HTTP/1.1 消息语法与路由 [*] [RFC7230] B.2
HTTP/1.1 语义与内容 [RFC7231] B.3
HTTP/1.1 条件请求 [RFC7232] B.4
HTTP/1.1 范围请求 [RFC7233] B.5
HTTP/1.1 认证 [RFC7235] B.6
HTTP 状态码 308(永久重定向) [RFC7538] B.7
HTTP Authentication-Info 和 Proxy-Authentication-Info 响应首部字段 [RFC7615] B.8
HTTP 客户端发起的内容编码 [RFC7694] B.9

[*] 本文档仅废止了RFC 7230中与 HTTP/1.1 消息语法和连接管理无关的部分;RFC 7230剩余部分由“HTTP/1.1”[HTTP/1.1]废止。

2. 一致性

2.1. 语法符号

本规范采用[RFC5234]的扩展巴科斯范式(ABNF)表示法,并结合[RFC7405]中定义的字符串区分大小写符号。

还使用了第 5.6.1 节定义的列表扩展,它允许用“#”运算符紧凑地定义以逗号分隔的列表(类似地,“*”运算符表示重复)。附录 A展示了所有列表运算符展开为标准 ABNF 表达式后的完整语法。

作为惯例,以“obs-”为前缀的 ABNF 规则名表示因历史原因保留的过时语法规则。

以下核心规则按引用方式引入,定义见RFC5234 附录 B.1:ALPHA(字母)、CR(回车)、CRLF(回车换行)、CTL(控制字符)、DIGIT(十进制 0-9)、DQUOTE(双引号)、HEXDIG(十六进制 0-9/A-F/a-f)、HTAB(水平制表符)、LF(换行)、OCTET(任意 8 位数据序列)、SP(空格)、VCHAR(任意可见 US-ASCII 字符)。

第 5.6 节定义了一些字段值的通用语法组件。

本规范中的“character”、“character encoding scheme”、“charset”和“protocol element”等术语采用[RFC6365]中的定义。

2.2. 需求表示法

本文件中的关键字“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“NOT RECOMMENDED”、“MAY”和“OPTIONAL”,应按 BCP 14 [RFC2119] [RFC8174] 的说明进行解释,且仅当这些词以全大写形式出现时才按上述含义解释。

本规范针对 HTTP 通信参与者的角色规定一致性判定标准。因此,要求会根据行为约束对象分别施加于发送方、接收方、客户端、服务器、用户代理、中介、源服务器、代理、网关或缓存。对于超出单一通信范围的情况,还会对实现、资源所有者和协议元素注册提出其他要求。

当某项要求仅针对创建协议元素的实现(而非转发收到元素的实现)时,使用“generate”而非“send”。

只要实现符合其在 HTTP 中所承担角色相关的所有要求,则视为符合规范。

发送方不得生成与相应 ABNF 规则语法不符的协议元素。在同一条消息中,发送方不得生成仅允许其他角色参与者生成的协议元素或语法备选项(即发送方在该消息中并不具备该角色)。

HTTP 的一致性既包括对所用协议版本特定消息语法的遵循,也包括对发送的协议元素语义的遵循。例如,声称符合 HTTP/1.1 的客户端若无法识别 HTTP/1.1 接收方所需支持的特性,就会无法与据此调整响应的服务器互操作。反映用户选择的特性(如内容协商和用户选择的扩展)可影响协议流之外的应用行为;发送与用户选择不符的协议元素将使用户困惑并限制其选择。

当实现未能做到语义一致时,其消息的接收方最终会开发相应的变通方法以调整自身行为。如果这些变通仅针对有问题的实现,接收方可以在保持协议一致性的前提下采用。例如,服务器常常分析 User-Agent 字段值的部分内容,用户代理也常分析 Server 字段值,以便针对已知的缺陷或不合理默认值调整自身行为。

2.3. 长度要求

接收方应当以防御性方式解析收到的协议元素,仅对该元素符合 ABNF 语法和适合合理缓冲区长度抱有有限期望。

HTTP 对许多协议元素没有具体长度限制,因为适用长度因部署环境和实现目的的不同而差异巨大。因此,发送方和接收方之间的互操作性取决于对每种协议元素“合理长度”的共同认知。此外,过去三十年间,部分协议元素的“合理长度”已发生变化,未来预计还会继续变化。

至少,接收方必须能够解析和处理其在其他消息中为同类协议元素生成的最大长度。例如,源服务器如果向自身资源发布了很长的 URI 引用,则需要能够解析和处理作为目标 URI 收到的同样引用。

许多收到的协议元素只需解析到能够识别并转发该元素即可。例如,中介可能只将收到的字段解析为字段名和字段值,然后直接转发而不进一步解析字段值内部结构。

2.4. 错误处理

接收方必须根据本规范(包括扩展内容)为其定义的语义解释收到的协议元素,除非接收方已通过经验或配置确定发送方错误实现了这些语义。例如,如果根据 User-Agent 首部字段的内容判断某个特定实现版本收到某些内容编码会失败,则源服务器可忽略 Accept-Encoding 首部字段的内容。

除非另有说明,接收方可以尝试从无效结构中恢复出可用的协议元素。HTTP 不定义具体的错误处理机制,除非其直接影响安全性,因为协议的不同应用需要不同的错误处理策略。例如,Web 浏览器可能希望在 Location 首部字段不符合 ABNF 语法时透明地恢复响应,而系统控制客户端可能会认为任何形式的错误恢复都是危险的。

第 9.2.2 节所述,部分请求在底层连接失败时可由客户端自动重试。

2.5. 协议版本

HTTP 的版本号由两个十进制数字组成,中间用“.”(句点或小数点)分隔。第一个数字(主版本)表示消息语法,第二个数字(次版本)表示发送方所兼容(即能理解用于未来通信的)该主版本下的最高次版本。

虽然 HTTP 的核心语义在不同协议版本间不变,其“线上的”表达方式可能会发生变化,因此当消息格式出现不兼容变更时,HTTP 的版本号也会变化。此外,HTTP 允许通过定义的扩展点(第 16 节)对协议进行增量的向后兼容变更,而无需改变版本号。

整体的协议版本号表明发送方遵循了该版本所对应规范中的所有要求。例如,“HTTP/1.1”由本文件、“HTTP 缓存”[CACHING]和“HTTP/1.1”[HTTP/1.1]的联合规范定义。

当引入不兼容的消息语法时,HTTP 的主版本号递增。次版本号在协议变更带来消息语义扩展或暗示发送方具有额外能力时递增。

即使发送方仅使用协议的向后兼容子集,次版本号也会表明其通信能力,从而使接收方(如服务器)知道可以在响应中使用更高级特性,或使客户端在后续请求中使用更高级特性。

当 HTTP 某主版本未定义任何次版本时,默认次版本为“0”。当协议元素需要次版本标识符时,使用“0”表示该协议。

3. 术语与核心概念

HTTP 是为万维网(WWW)架构创建的,并随着全球超文本系统的可扩展性需求不断演进。HTTP 的许多架构理念都体现在其术语定义中。

3.1. 资源

HTTP 请求的目标称为资源。HTTP 不限制资源的本质;它仅定义了可用于与资源交互的接口。大多数资源通过统一资源标识符(URI)标识,具体见第 4 节

HTTP 的一个设计目标是将资源标识与请求语义分离,这通过将请求语义赋予请求方法(第 9 节)及少量请求修饰首部字段得以实现。资源不能以与请求方法语义不一致的方式处理请求。例如,虽然资源的 URI 可能暗示了不安全的语义,但当客户端使用安全方法发起请求时(见第 9.2.1 节),可以期望资源避免执行不安全的操作。

HTTP 依赖统一资源标识符(URI)标准[URI]来指明目标资源(第 7.1 节)及资源之间的关系。

3.2. 表示

表示是指以协议易于传输的格式,反映某个资源过去、当前或期望状态的信息。表示由一组表示元数据和可能无限的数据流组成(第 8 节)。

HTTP 通过其统一接口以可传输的资源状态表示进行通信,从而实现“信息隐藏”,而不是直接传输资源本身。这使得由 URI 标识的资源可以是任何内容,包括“拉古纳海滩当前天气”这样的时态函数,同时能在生成消息时提供代表该资源的信息[REST]

统一接口类似一扇窗口,人们只能通过向另一端的独立参与者发送消息来观察和操控对象。需要一种共享抽象,在通信中代表(“代替”)该对象的当前或期望状态。当表示为超文本时,它既能展现资源状态,也能提供处理指令,帮助引导接收方后续的交互。

一个目标资源可能拥有或能生成多个表示,每个都反映资源的当前状态。通常基于内容协商第 12 节)的算法会选择最适合某次请求的表示。这个选定表示为条件请求的评估(第 13 节)、以及构造 GET(第 9.3.1 节)的 200 (OK)206 (Partial Content)304 (Not Modified) 响应提供数据和元数据。

3.3. 连接、客户端与服务器

HTTP 是一种基于可靠传输层或会话层连接运行的客户端/服务器协议。

HTTP 客户端是为发送一个或多个 HTTP 请求而建立与服务器连接的程序。HTTP 服务器是为服务 HTTP 请求、通过发送 HTTP 响应而接受连接的程序。

客户端和服务器仅指这些程序在特定连接中扮演的角色。相同程序在不同连接上既可作为客户端,也可作为服务器。

HTTP 被定义为无状态协议,意味着每个请求消息的语义都可以独立理解,连接与消息之间的关系不会影响消息的解释。例如,CONNECT 请求(第 9.3.6 节)或带有 Upgrade 首部字段的请求(第 7.8 节)可以在连接的任意时刻出现,不局限于首条消息。许多实现依赖 HTTP 的无状态设计来复用代理连接或在多服务器间动态负载均衡请求。

因此,服务器不得假定同一连接上的两个请求来自同一用户代理,除非该连接是安全且专属于该代理的。一些非标准 HTTP 扩展(如[RFC4559])已知违反这一要求,导致安全与互操作性问题。

3.4. 消息

HTTP 是用于在连接上传递消息的无状态请求/响应协议。发送方接收方分别指发送或接收某条消息的任意实现。

客户端通过请求消息向服务器发送请求,包括方法(第 9 节)和请求目标(第 7.1 节)。请求还可包含用于修饰请求、客户端信息和表示元数据的首部字段(第 6.3 节)、根据方法处理的内容(第 6.4 节),以及用于传递发送内容时收集信息的尾部字段(第 6.5 节)。

服务器通过发送一个或多个响应消息回复客户端的请求,每条响应都包含状态码(第 15 节)。响应还可包含服务器信息、资源元数据、表示元数据的首部字段、需按状态码解释的内容,以及用于传递发送内容时收集信息的尾部字段。

3.5. 用户代理

用户代理指发起请求的各种客户端程序。

最常见的用户代理是通用 Web 浏览器,但这只占实现的一小部分。其他常见用户代理包括网络爬虫(spider)、命令行工具、广告牌屏幕、家用电器、秤、灯泡、固件升级脚本、移动应用以及形态各异的通信设备等。

成为用户代理并不意味着请求时有真人直接与软件交互。许多情况下,用户代理被安装或配置为后台运行,将结果保存以供后续查看(或仅保存感兴趣或异常的子集)。例如,蜘蛛通常被赋予起始 URI,并配置为在遍历 Web 超文本图时遵循特定行为。

许多用户代理无法或选择不向用户交互建议或对安全与隐私问题做出充分警告。在本规范要求向用户报告错误的极少数情形下,将报告内容仅在错误控制台或日志文件中可见也是可以接受的。同样,要求自动操作前需用户确认的,也可通过预先配置、运行时选项或简单避免不安全操作来满足;确认并不要求必须有特定用户界面或打断正常流程,只要用户已做出相应选择。

3.6. 源服务器

源服务器指能够为指定目标资源生成权威响应的程序。

最常见的源服务器是大型公共网站。但如同用户代理不全等于浏览器,不能认为所有源服务器都一样。常见的源服务器还包括家居自动化单元、可配置网络组件、办公设备、自主机器人、新闻源、交通摄像头、实时广告选择器和视频点播平台等。

大多数 HTTP 通信为对某 URI 标识资源的表示发起的检索请求(GET)。最简单情况下,这可通过用户代理(UA)与源服务器(O)之间的单一双向连接(===)完成。

         request   >
    UA ======================================= O
                <   response

3.7. 中介

HTTP 支持通过连接链路中的中介来满足请求。常见的 HTTP 中介有三种:代理、网关和隧道。有时,单个中介可根据每个请求的性质在源服务器、代理、网关或隧道之间切换角色。

         >             >             >             >
    UA =========== A =========== B =========== C =========== O
               <             <             <             <

上图展示了用户代理与源服务器之间存在三个中介(A、B、C)。一条请求或响应消息若经过全链路,会经过四段独立连接。部分 HTTP 通信选项可能只适用于与最近、非隧道邻居的连接,也可能仅适用于链路两端,或适用于链路上所有连接。尽管图示为线性结构,每个参与方可同时参与多路通信。例如,B 可同时从除 A 外的许多客户端接收请求,和/或转发给除 C 外的多个服务器,同时处理 A 的请求。同理,后续请求往往会走不同的连接路径,常因动态负载均衡配置。

术语上游下游描述与消息流向相关的方向性要求:所有消息自上游流向下游。入站出站描述与请求路由相关的方向性要求:入站指“朝向源服务器”,出站指“朝向用户代理”。

代理是被客户端选定(通常通过本地配置规则)负责接收某些绝对 URI 请求并尝试通过 HTTP 接口转发满足请求的消息转发代理。有些代理仅做最小转发(如“http”URI 代理请求),有些则需与完全不同的应用层协议互转。代理常用于将组织的 HTTP 请求集中经由公共中介,以实现安全、注释或共享缓存等服务。部分代理会在转发过程中对选定消息或内容进行转换,见第 7.7 节

网关(也称反向代理)是在出站连接上扮演源服务器,但将收到请求转发入站至其他服务器的中介。网关常用于封装遗留或不可信信息服务,通过加速器缓存提升服务性能,或用于 HTTP 服务的分区与负载均衡。

所有适用于源服务器的 HTTP 要求也适用于网关的出站通信。网关与入站服务器通信可用任意协议,包括超出本规范范围的 HTTP 私有扩展。但 HTTP 到 HTTP 网关若需与第三方 HTTP 服务器互通,则必须符合用户代理对网关入站连接的要求。

隧道在两段连接间作为盲转发中继,不更改消息。一旦激活,隧道不再被视为 HTTP 通信参与方,尽管隧道可能由 HTTP 请求启动。隧道在转发连接两端关闭时终止。隧道用于通过中介扩展虚拟连接,例如通过共享防火墙代理建立保密通信时使用传输层安全(TLS,[TLS13])。

上述中介分类仅考虑作为 HTTP 通信参与方的中介。还有一些中介可在网络协议栈更底层对 HTTP 流量进行过滤或重定向,且消息发送方并不知情或授权。从协议角度看,网络中介与路径攻击者无法区分,常因错误违反 HTTP 语义而引入安全漏洞或互操作性问题。

例如,拦截代理[RFC3040](也常称为透明代理[RFC1919])与 HTTP 代理不同,它不是由客户端选定,而是过滤或重定向出站 TCP 80 端口(有时还有其它常见端口)流量。拦截代理常见于公共网络接入点,用于强制账户订阅后才能使用非本地互联网服务,也常见于企业防火墙强制执行网络使用政策。

3.8. 缓存

缓存是用于保存过往响应消息的本地存储及其控制消息存储、检索与删除的子系统。缓存存储可缓存响应,以减少未来等价请求的响应时间和网络带宽消耗。任何客户端或服务器可以使用缓存,但作为隧道时不能使用缓存。

缓存的作用是:当链路中的某参与方有适用的缓存响应时,请求/响应链路将被缩短。下图展示了如果 B 拥有 O(经 C)早前响应的缓存副本且 UA 或 A 未缓存该请求时的链路结构。

            >             >
       UA =========== A =========== B - - - - - - C - - - - - - O
                  <             <

如果允许缓存为后续请求存储响应消息副本,则该响应被认为是可缓存的。即使响应可缓存,客户端或源服务器也可能对缓存响应在特定请求中的使用施加额外约束。HTTP 对缓存行为与可缓存响应的要求见[CACHING]

世界各地和大型组织内部部署了多种缓存架构和配置,包括用于节省带宽、降低时延的国家级代理缓存层级,利用网关缓存优化区域和全球热门站点分发的内容分发网络,通过广播或组播共享缓存条目的协作系统,用于离线或高时延环境的预抓取缓存归档等。

3.9. 消息交换示例

下例展示了针对 URI "http://www.example.com/hello.txt" 的典型 HTTP/1.1 GET 请求(第 9.3.1 节)消息交换过程:

客户端请求:

GET /hello.txt HTTP/1.1
User-Agent: curl/7.64.1
Host: www.example.com
Accept-Language: en, mi

服务器响应:

HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain

Hello World! My content includes a trailing CRLF.

4. HTTP 中的标识符

统一资源标识符(URIs) [URI] 在整个 HTTP 中被用作识别资源的手段(第 3.1 节)。

4.1. URI 引用

URI 引用用于指向请求目标、指示重定向以及定义关系。

“URI-reference”、“absolute-URI”、“relative-part”、“authority”、“port”、“host”、“path-abempty”、“segment”和“query”的定义采用自 URI 通用语法。为能够包含非空路径分量的协议元素定义了“absolute-path”规则。(该规则与 RFC 3986 的 path-abempty 规则略有不同,后者允许空路径;也不同于 path-absolute 规则,后者不允许以 "//" 开头的路径。)为能够包含相对 URI 但不包含片段分量的协议元素定义了“partial-URI”规则。

  URI-reference = <URI-reference, see [URI], Section 4.1>
  absolute-URI  = <absolute-URI, see [URI], Section 4.3>
  relative-part = <relative-part, see [URI], Section 4.2>
  authority     = <authority, see [URI], Section 3.2>
  uri-host      = <host, see [URI], Section 3.2.2>
  port          = <port, see [URI], Section 3.2.3>
  path-abempty  = <path-abempty, see [URI], Section 3.3>
  segment       = <segment, see [URI], Section 3.3>
  query         = <query, see [URI], Section 3.4>

  absolute-path = 1*( "/" segment )
  partial-URI   = relative-part [ "?" query ]

HTTP 中允许 URI 引用的每个协议元素将在其 ABNF 产生式中指明该元素是否允许任意形式的引用(URI-reference)、仅允许绝对形式的 URI(absolute-URI)、仅允许路径和可选查询分量(partial-URI),或上述组合。除非另有说明,URI 引用相对于目标 URI 进行解析(第 7.1 节)。

建议所有发送者和接收者至少支持在协议元素中长度为 8000 字节的 URI。注意,这意味着某些结构和在线表示(例如 HTTP/1.1 的请求行)在某些情况下必然会更大。

4.2. 与 HTTP 相关的 URI 方案

IANA 在 https://www.iana.org/assignments/uri-schemes/ 上维护 URI 方案登记([BCP35])。尽管请求可能针对任何 URI 方案,以下方案与 HTTP 服务器固有相关:

表 2
URI 方案 说明
http 超文本传输协议 4.2.1
https 安全超文本传输协议 4.2.2

注意,出现 "http" 或 "https" URI 并不意味着在所标识的源上总有一个 HTTP 服务器在监听连接。任何人都可以创建一个 URI,无论是否存在服务器以及该服务器当前是否将该标识符映射到一个资源。已注册名称和 IP 地址的委派性质创造了一个联邦命名空间,无论是否存在 HTTP 服务器。

4.2.1. http URI 方案

“http” URI 方案在此被定义,用于在由潜在 HTTP 源服务器在给定端口上监听 TCP([TCP])连接所管理的层级命名空间内铸造标识符。

  http-URI = "http" "://" authority path-abempty [ "?" query ]

“http” URI 的源服务器由 authority 组件标识,该组件包括主机标识符([URI], 第 3.2.2 节) 和可选端口号([URI], 第 3.2.3 节)。如果端口子组件为空或未给出,默认使用 TCP 端口 80(WWW 服务的保留端口)。源决定谁有权对标识的资源作出权威性响应,如 第 4.3.2 节 所定义。

发送者不得生成带有空主机标识符的 "http" URI。处理此类 URI 引用的接收者必须将其视为无效并拒绝。

层级路径组件和可选查询组件在该源的命名空间内标识目标资源。

4.2.2. https URI 方案

“https” URI 方案在此被定义,用于在由潜在源服务器在给定端口上监听 TCP 连接并能够建立用于 HTTP 通信的 TLS([TLS13])连接并对其进行保护的层级命名空间内铸造标识符。在此上下文中,“已保护”特指服务器已被认证为代表所标识的 authority 行事,并且与该服务器的所有 HTTP 通信都具有对客户端和服务器均可接受的机密性和完整性保护。

  https-URI = "https" "://" authority path-abempty [ "?" query ]

“https” URI 的源服务器由 authority 组件标识,该组件包括主机标识符([URI], 第 3.2.2 节) 和可选端口号([URI], 第 3.2.3 节)。如果端口子组件为空或未给出,默认使用 TCP 端口 443(用于通过 TLS 的 HTTP 的保留端口)。源决定谁有权对标识的资源作出权威性响应,如 第 4.3.3 节 所定义。

发送者不得生成带有空主机标识符的 "https" URI。处理此类 URI 引用的接收者必须将其视为无效并拒绝。

层级路径组件和可选查询组件在该源的命名空间内标识目标资源。

客户端在将其对 “https” 资源的 HTTP 请求传输之前,必须确保这些请求是已被保护的,并且仅接受对这些请求的受保护响应。请注意,什么样的加密机制对客户端和服务器是可接受的通常是协商决定的,并可能随时间变化。

通过 “https” 方案提供的资源与 “http” 方案没有共享身份。它们是具有独立命名空间的不同来源。然而,定义为适用于同一主机的所有来源的 HTTP 扩展(例如 Cookie 协议 [COOKIE])允许由一个服务设置的信息影响与匹配主机域组内其他服务的通信。这样的扩展应谨慎设计,以防止从受保护连接获得的信息被无意地在不安全的上下文中交换。

4.2.3. http(s) 规范化与比较

带有 "http" 或 "https" 方案的 URI 按照 第 6 节 中定义的方法进行规范化和比较,使用上述为每个方案描述的默认值。

HTTP 不要求使用特定的方法来确定等价性。例如,缓存键可在语法级规范化之后作为简单字符串进行比较,或在基于方案的规范化之后进行比较。

“http”和“https” URI 的基于方案的规范化(参见 第 6.2.3 节) 涉及以下附加规则:

  • 如果端口等于某方案的默认端口,则正常形式是省略端口子组件。
  • 当不被用作 OPTIONS 请求的目标时,空路径组件等同于绝对路径 “/”,因此正常形式是提供路径 “/”。
  • 方案和主机不区分大小写,通常以小写提供;所有其他组件以区分大小写的方式比较。
  • 除“reserved”集合中的字符外,其他字符等价于它们的百分号编码八位组:正常形式是不对它们进行编码(参见 第 2.1 节第 2.2 节)。

例如,以下三个 URI 是等价的:

   http://example.com:80/~smith/home.html
   http://EXAMPLE.com/%7Esmith/home.html
   http://EXAMPLE.com:/%7esmith/home.html

两个在规范化后等价的 HTTP URI(使用任何方法)可以被认为标识相同的资源,任何 HTTP 组件可以执行规范化。因此,不应使用在规范化后等价的 HTTP URI 来标识不同的资源(参见 第 6.2 节)。

4.2.4. 在 http(s) URI 中弃用 userinfo

URI 通用语法中的 authority 也包括 userinfo 子组件([URI], 第 3.2.1 节)用于在 URI 中包含用户认证信息。在该子组件中,使用 "user:password" 格式已被弃用。

一些实现利用 userinfo 组件用于认证信息的内部配置,例如命令调用选项、配置文件或书签列表中,即使这样的使用可能会暴露用户标识或密码。

当在消息中生成作为目标 URI 或字段值的 "http" 或 "https" URI 引用时,发送者不得生成 userinfo 子组件(及其 "@" 分隔符)。

在使用来自不受信任来源的 "http" 或 "https" URI 引用之前,接收者应解析 userinfo 并将其存在视为错误;它很可能被用于掩盖 authority 以实施网络钓鱼攻击。

4.2.5. 带片段标识符的 http(s) 引用

片段标识符允许对次级资源进行间接识别,这与 URI 方案无关,如 第 3.5 节 所定义的一样。某些引用 URI 的协议元素允许包含片段,而其他协议元素则不允许。它们通过使用允许包含片段的元素的 ABNF 规则来区分;否则,使用排除片段的特定规则。

4.3. 权威访问

权威访问指以客户端认为具有权威(由资源所有者控制)的方式对给定标识符进行解引用,以便访问被标识的资源。确定是否授予访问的过程由 URI 方案定义,通常使用 URI 组件内的数据,例如在使用通用语法时的 authority 组件。然而,权威访问不限于被标识的机制。

第 4.3.1 节 定义了 origin 的概念以辅助此类用途,随后的子节说明如何确认对等方有权代表某个 origin。

有关建立权威性的安全注意事项,请参见 第 17.1 节

4.3.1. URI 起源

给定 URI 的 origin 是在将方案和主机规范化为小写并将端口规范化以去除任何前导零之后的三元组(scheme, host, port)。如果 URI 中省略了端口,则使用该方案的默认端口。例如,URI

   https://Example.Com/happy.js

将具有起源

   { "https", "example.com", "443" }

也可以描述为始终带端口的规范化 URI 前缀:

   https://example.com:443

每个 origin 定义其自己的命名空间并控制该命名空间内标识符如何映射到资源。反过来,origin 对有效请求的响应方式在时间上的一致性决定了用户将与 URI 关联的语义,而这些语义的有用性最终将使这些机制成为用户未来引用和访问的资源。

如果两个 origin 在方案、主机或端口上存在差异,则它们是不同的。即使可以验证同一实体控制两个不同的 origin,除非源权威服务器显式地为其别名,否则这两个 origin 下的命名空间仍然是不同的。

Origin 也在 HTML 及相关的 Web 协议中使用,超出本文档的范围,如 [RFC6454] 所述。

4.3.2. http 起源

虽然 HTTP 与传输协议无关,但 “http” 方案(第 4.2.1 节)特指将 authority 与在所指示端口上为所识别主机监听 TCP 连接的 origin 服务器的控制权相关联。这是一种非常弱的权威感,因为它依赖于客户端特定的名称解析机制以及可能未被路径上攻击者保护的通信。不过,在受信任的环境中,它足以将 "http" 标识符绑定到 origin 服务器以实现一致解析。

如果主机标识符以 IP 地址提供,则 origin 服务器是该 IP 地址上在所指示 TCP 端口监听的主机(如果存在)。如果主机是注册名称,则该注册名称是与名称解析服务(例如 DNS)一起使用的间接标识符,以查找适当的 origin 服务器的地址。

当在要求访问所指示资源的上下文中使用 "http" URI 时,客户端可以尝试通过将主机标识符解析为 IP 地址、在所指示端口向该地址建立 TCP 连接,并在该连接上发送包含与客户端目标 URI 匹配的请求目标的 HTTP 请求消息来访问该资源(第 7.1 节)。

如果服务器对该请求以非临时的 HTTP 响应消息作出响应(如 第 15 节 所述),则该响应被视为对客户端请求的权威性答复。

注意,以上并不是获得权威性响应的唯一手段,也不意味着始终需要权威性响应(参见 [CACHING])。例如,Alt-Svc 头字段([ALTSVC])允许 origin 服务器标识对该 origin 也具有权威性的其他服务。对由 "http" 标识的资源的访问也可能由本文档范围之外的协议提供。

4.3.3. https 起源

“https” 方案(第 4.2.2 节)基于服务器能够使用与客户端认为对所标识的 origin 服务器可信的证书相对应的私钥来确定权威性。客户端通常依赖于从某个预先安排或配置的信任锚传递来的信任链来认定证书是可信的(参见 第 4.3.4 节)。

在 HTTP/1.1 及更早版本中,只有当客户端与该 URI origin 的主机在已成功建立并受保护的连接上通信时,才会将权威性归属于服务器。连接建立和证书验证被用作权威性的证明。

在 HTTP/2 和 HTTP/3 中,当客户端在已成功建立并受保护的连接上通信,且 URI origin 的主机与服务器证书中出现的任一主机匹配并且客户端认为它可以为该 URI 向该主机打开连接时,客户端会将权威性归属于服务器。实际上,客户端将执行 DNS 查询以检查 origin 的主机是否与已建立连接的服务器 IP 地址相同。源服务器通过发送等效的 ORIGIN 帧可以移除此限制(参见 [RFC8336])。

请求目标的主机和端口值在每个 HTTP 请求中传递,用以识别 origin 并将其与可能由同一服务器控制的其他命名空间区分开(第 7.2 节)。origin 有责任确保任何被授予其证书私钥控制权的服务同样负责管理相应的 “https” 命名空间,或至少准备拒绝看起来被错误定向的请求(参见 第 7.4 节)。

即使 origin 有权处理某些目标 URI 的请求,origin 服务器也可能不愿意处理这些请求。例如,当某主机在不同端口(例如 443 和 8000)上运行不同服务时,需要在 origin 服务器处检查目标 URI(即使连接已被保护),因为网络攻击者可能导致某个端口的连接被路由到另一个端口。未检查目标 URI 可能允许攻击者用另一个端口的看似权威的响应(例如 "https://example.com:8000/foo")替换对某一目标 URI(例如 "https://example.com/foo")的响应。

注意,“https” 方案不依赖于 TCP 和已连接的端口号来关联权威性,因为两者都在受保护通信之外,因此不能被信任为决定性的。因此,HTTP 通信可能通过任何被定义为已保护的通道进行(见 第 4.2.2 节),包括不使用 TCP 的协议。

当在要求访问所指示资源的上下文中使用 "https" URI 时,客户端可以通过将主机标识符解析为 IP 地址、在所指示端口向该地址建立 TCP 连接、在该 TCP 上成功启动 TLS 并在端到端上对连接进行保密性和完整性保护,然后在该连接上发送包含与客户端目标 URI 匹配的请求目标的 HTTP 请求消息来尝试访问该资源(第 7.1 节)。

如果服务器对该请求以非临时的 HTTP 响应消息作出响应(如 第 15 节 所述),则该响应被视为对客户端请求的权威性答复。

注意,以上并不是获得权威性响应的唯一手段,也不意味着始终需要权威性响应(参见 [CACHING])。

4.3.4. https 证书验证

为建立用于解引用 URI 的受保护连接,客户端必须验证服务的身份与 URI 的 origin 服务器相符且可接受。证书验证用于防止路径上攻击者或控制名称解析的攻击者冒充服务器。该过程要求客户端配置一组信任锚。

一般情况下,客户端必须使用 第 6 节 中定义的验证过程来验证服务身份(参见 [RFC6125])。客户端必须从服务的主机构造一个参考标识:如果主机是字面 IP 地址(参见 第 4.3.5 节),参考标识为 IP-ID,否则主机为名称且参考标识为 DNS-ID。

客户端不得使用 CN-ID 类型的参考标识。如在 第 6.2.1 节 中所述,较旧的客户端可能会使用 CN-ID 类型的参考标识。

客户端可能被专门配置为接受替代形式的服务器身份验证。例如,客户端可能连接到地址和主机名动态变化的服务器,期望该服务出示特定证书(或匹配某个外部定义参考标识的证书),而不是与目标 URI 的 origin 匹配的证书。

在特殊情况下,客户端仅忽略服务器身份可能是合适的,但必须理解这会使连接暴露于主动攻击。

如果证书对于目标 URI 的 origin 无效,用户代理必须在继续之前从用户处获得确认(见 第 3.5 节)或以错误证书错误终止连接。自动化客户端必须将该错误记录到适当的审计日志(如可用)并且应终止连接(以错误证书错误)。自动化客户端可以提供一个配置项以禁用此检查,但必须提供一个启用该检查的设置。

4.3.5. IP-ID 参考标识

在 "https" URI 的 "host" 字段中使用 IP 地址字面量标识的服务器具有类型为 IP-ID 的参考标识。IPv4 地址使用 "IPv4address" ABNF 规则,IPv6 地址使用带有 "IPv6address" 选项的 "IP-literal" 产生式;见 第 3.2.2 节。IP-ID 类型的参考标识包含 IP 地址的解码字节。

IPv4 地址为 4 个八位组,IPv6 地址为 16 个八位组。IP-ID 的使用未为其他 IP 版本定义。证书 subjectAltName 扩展中的 iPAddress 选项并未显式包含 IP 版本,因此依赖地址长度来区分版本;见 第 4.2.1.6 节

类型为 IP-ID 的参考标识在地址与证书的 subjectAltName 扩展中的 iPAddress 值完全相同时匹配。

5. 字段

HTTP 使用 字段 以可扩展的 名称/值 对形式提供数据,并具有注册的键命名空间。字段在报文的头部和尾部部分中发送和接收(第 6 节)。

5.1. 字段名称

字段名称标识相应字段值所具有的由该名称定义的语义。例如,Date 头字段在 第 6.6.1 节 中被定义为包含其出现的报文的起始时间戳。

字段名称不区分大小写,并且应在 “Hypertext Transfer Protocol (HTTP) Field Name Registry” 中注册;参见 第 16.3.1 节

字段的解释在同一主版本的较小版本之间不会改变,尽管在缺少该字段时接收方的默认行为可能会改变。除非另有说明,字段适用于所有 HTTP 版本。特别是,无论是否声明符合 HTTP/1.1,所有 HTTP 实现都应识别 HostConnection 字段。

如果新字段的定义语义允许不被识别的接收方安全地忽略它们,则可以在不更改协议版本的情况下引入新字段;参见 第 16.3 节

代理必须转发未识别的头字段,除非该字段名称被列在 Connection 头字段中(第 7.6.1 节),或者代理被专门配置为阻止或以其他方式转换此类字段。其他接收方应忽略未识别的头字段和尾字段。遵守这些要求允许在不更新或移除已部署中间件的情况下扩展 HTTP 的功能。

5.2. 字段行与合并字段值

字段部分由任意数量的 字段行 组成,每一行都有一个标识该字段的 字段名称(见 第 5.1 节),以及传递该字段实例数据的 字段行值

当某个字段名称在一个部分中仅出现一次时,该字段的合并 字段值 由相应的字段行值构成。当字段名称在一个部分内重复时,其合并字段值由该部分内相应字段行值的列表按顺序连接而成,每个字段行值之间以逗号分隔。

例如,下列部分:

Example-Field: Foo, Bar
Example-Field: Baz

包含两行字段,均具有字段名称 "Example-Field"。第一行的字段行值为 "Foo, Bar",第二行字段行值为 "Baz"。因此 "Example-Field" 的字段值为列表 "Foo, Bar, Baz"。

5.3. 字段顺序

接收方可以将字段部分内具有相同字段名称的多行字段合并为一行字段,且不改变报文语义,方法是按顺序将每个后续字段行值附加到初始字段行值之后,并以逗号 (",") 及可选空白(OWS,定义见 第 5.6.3 节)分隔。为一致起见,建议使用逗号 SP。

因此,接收到相同名称的字段行的顺序对字段值的解释是重要的;代理在转发报文时不得改变这些字段行值的顺序。

这意味着,除下文所述的知名例外之外,发送者不得在报文中生成具有相同名称的多行字段(无论在头部还是尾部),也不得在报文中已存在同名字段行时追加字段行,除非该字段的定义允许将多个字段行值重新组合为逗号分隔的列表(即该字段定义中的至少一个替代形式允许逗号分隔列表,例如在 第 5.6.1 节 中定义的 #(values) ABNF 规则)。

在一个部分中接收到不同字段名称的字段行的顺序不具有重要性。不过,发送包含附加控制数据的头字段时,优先发送这些字段是良好做法,例如在请求中发送 Host,在响应中发送 Date,以便实现早期决定不处理某些报文的实现能够尽早做出决定。

服务器在收到完整的请求头部部分之前不得将请求应用于目标资源,因为随后到达的头字段行可能包含条件、认证凭证或故意误导的重复头字段,这些都可能影响请求处理。

5.4. 字段限制

HTTP 对每一字段行、字段值或整个头部或尾部部分的长度没有预定义限制,如 第 2 节 所述。实践中存在各种随意的单个长度限制,通常依赖于特定字段的语义。

当服务器收到的请求头字段行、字段值或字段集合大于其愿意处理的长度时,服务器必须使用适当的 4xx (Client Error) 状态码进行响应。忽略此类头字段会增加服务器遭受请求走私攻击的风险(参见 第 11.2 节)。

如果字段语义允许丢弃值而不会改变报文分帧或响应语义,客户端可以丢弃或截断接收到的超过其愿意处理的字段行。

5.5. 字段值

HTTP 字段值由按字段语法定义的字符序列组成。每个字段的语法通常使用 ABNF 来定义([RFC5234])。

字段值不包括前导或尾随空白。当特定版本的 HTTP 允许此类空白出现在报文中时,字段解析实现必须在评估字段值之前排除这些空白。

字段值通常限制在 US-ASCII 字符范围内(参见 [USASCII])。需要更大字符范围的字段可以使用编码,例如在 [RFC8187] 中定义的编码。历史上,HTTP 允许使用 ISO-8859-1 字符集的字段内容(参见 [ISO-8859-1]),其他字符集仅通过使用 [RFC2047] 编码来支持。为新定义的字段规定的值应当限制为可见 US-ASCII 八位组(VCHAR)、SP 和 HTAB。接收方应将字段内容中允许的其他八位组(即 obs-text)视为不透明数据。

包含 CR、LF 或 NUL 字符的字段值是无效且危险的,因为实现可能以不同方式解析和解释这些字符;接收到字段值中含有 CR、LF 或 NUL 的接收方必须要么拒绝该报文,要么在进一步处理或转发该报文之前将每个此类字符替换为 SP。包含其他 CTL 字符的字段值也同样无效;但是,当这些字符出现在安全的上下文中(例如不会被任何下游 HTTP 解析器处理的应用特定引用字符串)时,接收方可以为了健壮性保留这些字符。

仅预期单个成员作为字段值的字段称为 单例字段

允许多个成员作为字段值的字段称为 基于列表的字段。列表运算符扩展(见 第 5.6.1 节)用于定义可包含多个成员的字段值的通用记法。

由于逗号 (",") 用作成员之间的分隔符,如果成员中允许包含逗号,则需要小心处理。这对基于列表的字段和单例字段均适用,因为单例字段可能被错误地发送为多个成员,检测此类错误有助于互操作性。预计在成员内部包含逗号的字段(例如 HTTP-dateURI-reference 元素)应使用分隔符围住该元素,以将该数据内部的逗号与可能的列表分隔符区分开来。

例如,文本日期和 URI(任一者可能包含逗号)可以安全地放在如下基于列表的字段值中:

Example-URIs: "http://example.com/a.html,foo",
              "http://without-a-comma.example.com/"
Example-Dates: "Sat, 04 May 1996", "Wed, 14 Sep 2005"

注意,双引号分隔符几乎总是与 quoted-string 产生式一起使用(见 第 5.6.4 节);在双引号内使用不同语法可能会导致不必要的混淆。

许多字段(例如在 Content-Type 中定义的字段,见 第 8.3 节)使用用于参数的通用语法,该语法允许参数值既可以为未引用的 token,也可以为引用字符串(quoted-string)(见 第 5.6.6 节)。使用通用语法允许接收方重用现有的解析组件。当允许两种形式时,参数值的含义应当在作为 token 或 quoted-string 接收时相同。

5.6. 定义字段值的通用规则

5.6.1. 列表(#rule ABNF 扩展)

[RFC5234] 的 ABNF 规则的 #rule 扩展用于在某些基于列表的字段值定义中提高可读性。

构造符 "#" 的定义类似于 "*",用于定义以逗号分隔的元素列表。完整形式为 "<n>#<m>element",表示至少 <n> 且至多 <m> 个元素,每个元素由单个逗号 (",") 及可选空白(OWS,定义见 第 5.6.3 节)分隔。

5.6.1.1. 发送者要求

在任何使用列表构造的产生式中,发送者不得生成空的列表元素。换言之,发送者必须生成满足以下语法的列表:

  1#element => element *( OWS "," OWS element )

并且:

  #element => [ 1#element ]

并且对于 n >= 1 且 m > 1:

  <n>#<m>element => element <n-1>*<m-1>( OWS "," OWS element )

附录 A 展示了在展开列表构造之后发送者的汇总 ABNF。

5.6.1.2. 接收者要求

空元素不计入存在元素的计数。接收方必须解析并忽略一定数量的空列表元素:足以处理发送者合并值时的常见错误,但不能多到可被用作拒绝服务的手段。换言之,接收方必须接受满足下列语法的列表:

  #element => [ element ] *( OWS "," OWS [ element ] )

注意,由于可能存在空列表元素,RFC 5234 的 ABNF 无法强制列表元素的基数,因此所有情况都按未指定基数的方式映射。

例如,给定这些 ABNF 产生式:

  example-list      = 1#example-list-elmt
  example-list-elmt = token ; see Section 5.6.2

那么下列值对于 example-list 来说是有效的(不含双引号,仅用于分隔显示):

  "foo,bar"
  "foo ,bar,"
  "foo , ,bar,charlie"

相比之下,以下值是无效的,因为 example-list 产生式要求至少有一个非空元素:

  ""
  ","
  ",   ,"

5.6.2. 标记(Tokens)

Tokens 是不包含空白或分隔符的简短文本标识符。

  token          = 1*tchar

  tchar          = "!" / "#" / "$" / "%" / "&" / "'" / "*"
                 / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
                 / DIGIT / ALPHA
                 ; any VCHAR, except delimiters

许多 HTTP 字段值使用通用语法组件定义,这些组件由空白或特定的分隔字符分隔。分隔符从不可用作 token 的 US-ASCII 可见字符集中选择(DQUOTE 及 "(),/:;<=>?@[\]{}")。

5.6.3. 空白

本规范使用三条规则来表示线性空白的使用:OWS(可选空白)、RWS(必需空白)和 BWS(“不良”空白)。

OWS 规则用于可能出现零个或多个线性空白八位组的地方。对于偏好可选空白以提高可读性的协议元素,发送者应生成单个 SP 作为可选空白;否则,发送者不应生成可选空白,除非在就地消息过滤过程中需要覆盖无效或不需要的协议元素。

RWS 规则用于需要至少一个线性空白八位组以分隔字段标记的情况。发送者应将 RWS 生成为单个 SP。

OWS 和 RWS 在语义上等同于单个 SP。任何已知定义为 OWS 或 RWS 的内容在解释或向下游转发报文之前可以用单个 SP 替换。

BWS 规则用于语法仅出于历史原因允许可选空白的地方。发送者不得在报文中生成 BWS。接收方必须解析此类不良空白并在解释协议元素之前将其移除。

BWS 没有语义。任何已知定义为 BWS 的内容在解释或向下游转发报文之前可以被移除。

  OWS            = *( SP / HTAB )
                 ; optional whitespace
  RWS            = 1*( SP / HTAB )
                 ; required whitespace
  BWS            = OWS
                 ; "bad" whitespace

5.6.4. 带引号的字符串

如果使用双引号将文本字符串括起,该字符串会被解析为单个值。

  quoted-string  = DQUOTE *( qdtext / quoted-pair ) DQUOTE
  qdtext         = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text

反斜杠八位组 ("\") 可作为 quoted-string 和注释构造中的单八位组引用机制。处理 quoted-string 值的接收方必须将 quoted-pair 视为被反斜杠后面的八位组所替代。

  quoted-pair    = "\" ( HTAB / SP / VCHAR / obs-text )

发送者除非必要(用于引用出现在字符串中的 DQUOTE 和反斜杠八位组),否则不应在 quoted-string 中生成 quoted-pair。发送者除非必要(用于引用注释中出现的括号 ["(" 和 ")"] 以及反斜杠八位组),否则不应在注释中生成 quoted-pair。

5.6.5. 注释

在某些 HTTP 字段中可以通过用圆括号将注释文本括起来来包含注释。注释仅允许出现在字段值定义中包含 "comment" 的字段中。

  comment        = "(" *( ctext / quoted-pair / comment ) ")"
  ctext          = HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text

5.6.6. 参数

参数是名称/值 对的实例;它们常用于字段值中,作为附加辅助信息到某个项的通用语法。每个参数通常由前置分号立即分隔。

参数名称不区分大小写。参数值可能区分或不区分大小写,取决于参数名称的语义。参数示例及一些等效形式可见于媒体类型(见 第 8.3.1 节)和 Accept 头字段(见 第 12.5.1 节)。

匹配 token 产生式的参数值可以作为 token 或在 quoted-string 中传输。引用和值未引用的值是等价的。

5.6.7. 日期/时间 格式

在 1995 年之前,服务器用于传达时间戳的三种不同格式被广泛使用。为兼容旧实现,这三种格式都在此定义。首选格式是互联网信息报文格式所使用的日期和时间规范的定长且单时区的子集(参见 [RFC5322])。

首选格式的一个示例是

Sun, 06 Nov 1994 08:49:37 GMT    ; IMF-fixdate

两种过时格式的示例如下

Sunday, 06-Nov-94 08:49:37 GMT   ; obsolete RFC 850 format
Sun Nov  6 08:49:37 1994         ; ANSI C's asctime() format

解析 HTTP 字段中时间戳值的接收方必须接受所有三种 HTTP-date 格式。当发送者生成包含一个或多个定义为 HTTP-date 的时间戳的字段时,发送者必须以 IMF-fixdate 格式生成这些时间戳。

HTTP-date 值表示为协调世界时(UTC)的一个时刻实例。前两种格式通过三字母缩写 "GMT" 来指示 UTC;asctime 格式的值假定为 UTC。

时钟 是能够提供当前 UTC 时刻的合理近似的实现。时钟实现应使用 NTP(参见 [RFC5905])或类似协议与 UTC 同步。

首选格式:

  IMF-fixdate  = day-name "," SP date1 SP time-of-day SP GMT
  ; fixed length/zone/capitalization subset of the format
  ; see Section 3.3 of [RFC5322]

  day-name     = %s"Mon" / %s"Tue" / %s"Wed"
               / %s"Thu" / %s"Fri" / %s"Sat" / %s"Sun"

  date1        = day SP month SP year
               ; e.g., 02 Jun 1982

  day          = 2DIGIT
  month        = %s"Jan" / %s"Feb" / %s"Mar" / %s"Apr"
               / %s"May" / %s"Jun" / %s"Jul" / %s"Aug"
               / %s"Sep" / %s"Oct" / %s"Nov" / %s"Dec"
  year         = 4DIGIT

  GMT          = %s"GMT"

  time-of-day  = hour ":" minute ":" second
               ; 00:00:00 - 23:59:60 (leap second)

  hour         = 2DIGIT
  minute       = 2DIGIT
  second       = 2DIGIT

过时格式:

  rfc850-date  = day-name-l "," SP date2 SP time-of-day SP GMT
  date2        = day "-" month "-" 2DIGIT
               ; e.g., 02-Jun-82

  day-name-l   = %s"Monday" / %s"Tuesday" / %s"Wednesday"
               / %s"Thursday" / %s"Friday" / %s"Saturday"
               / %s"Sunday"

HTTP-date 区分大小写。注意,第 4.2 节 对缓存接收方放宽了这一点(参见 [CACHING])。

发送者不得在 HTTP-date 中生成文法之外的额外空白。day-namedaymonthyeartime-of-day 的语义与 Internet Message Format 中相应名称的构造定义相同(参见 [RFC5322]第 3.3 节)。

接收 rfc850-date 格式时间戳值的接收方(该格式使用两位数年份)必须将看起来比当前时间晚超过 50 年的时间戳解释为最近的过去年份中具有相同后两位数字的年份。

除非字段定义另有约束,接收时间戳值的接收方应在解析时间戳时保持健壮。例如,报文有时会从非 HTTP 源通过 HTTP 转发,该源可能会生成 Internet Message Format 定义的任意日期和时间格式。

6. 消息抽象

每个主要版本的 HTTP 定义其用于传输报文的语法。本节基于这些报文特性、共有结构和传递语义的能力,对 HTTP 报文定义了一个抽象数据类型。此抽象用于定义对发送方和接收方的要求,使其与具体 HTTP 版本无关,从而允许一个版本的报文在通过其他版本中继时不改变其含义。

报文 包括下列各项:

  • 用于描述和路由报文的控制数据,
  • 一个头字段查找表(name/value 对),用于扩展该控制数据并传达关于发送方、报文、内容或上下文的附加信息,
  • 一个可能无界的内容流,和
  • 一个尾字段查找表(name/value 对),用于传达在发送内容时获得的信息。

先发送分帧和控制数据,随后是包含用于头表的字段的头部部分。当报文包含内容时,内容在头部部分之后发送,之后可能跟随包含用于尾表字段的尾部部分。

报文预计作为流进行处理,在读取过程中该流的目的和继续处理的要求会逐步显现。因此,控制数据描述接收方需要立即知道的内容,头字段描述在接收内容之前需要知道的事项,内容(若存在)通常包含接收方为完成报文语义所想要或需要的东西,而尾字段则提供在发送内容之前未知的可选元数据。

报文旨在是 自描述的:接收方所需了解的关于报文的一切都可以通过查看报文本身来确定(在对在传输中被压缩或省略的部分进行解码或重构之后),而不需要理解发送方的当前应用状态(通过先前报文建立)。然而,客户端在解析、解释或缓存相应响应时 MUST 保留对请求的相关信息。例如,对 HEAD 方法的响应看起来就像对 GET 的响应的开头,但不能用相同方式来解析。

注意,该消息抽象是跨多个 HTTP 版本的概括,包含一些在某些版本中可能不存在的特性。例如,尾字段最初在 HTTP/1.1 的分块传输编码中作为内容之后的尾部部分引入。在 HTTP/2 和 HTTP/3 中存在等效特性,体现在终止每个流的头块中。

6.1. 分帧与完备性

报文分帧指明每个报文如何开始和结束,从而使得每个报文能够与同一连接上的其他报文或噪声区分开来。每个主要 HTTP 版本定义其自身的分帧机制。

HTTP/0.9 以及早期部署的 HTTP/1.0 使用底层连接的关闭来结束响应。为向后兼容,在 HTTP/1.1 中也允许这种隐式分帧。然而,隐式分帧在连接过早关闭时可能无法区分不完整响应。因此,几乎所有现代实现都使用显式分帧,以长度定界的数据序列形式传输报文数据。

当其分帧指示的所有八位组都可用时,报文被认为是 完整的。注意,当没有使用显式分帧时,由底层连接关闭而结束的响应报文被视为完整,尽管它可能与不完整响应无法区分,除非传输层错误表明它并不完整。

6.2. 控制数据

报文以描述其主要目的的控制数据开始。请求报文的控制数据包括请求方法(第 9 节)、请求目标(第 7.1 节)和协议版本(第 2.5 节)。响应报文的控制数据包括状态码(第 15 节)、可选的原因短语和协议版本。

在 HTTP/1.1([HTTP/1.1])及更早版本中,控制数据作为报文的首行发送。在 HTTP/2([HTTP/2])和 HTTP/3([HTTP/3])中,控制数据作为带有保留名称前缀的伪头字段发送(例如 ":authority")。

每个 HTTP 报文都有一个协议版本。根据所使用的版本,它可能在报文中被显式标识或通过接收报文的连接推断。接收方使用该版本信息来确定与该发送方后续通信时的限制或可能性。

当报文被中间件转发时,协议版本会更新为反映该中间件所使用的版本。Via 头字段(第 7.6.3 节)用于在被转发的报文中传达上游的协议信息。

客户端 SHOULD 发送等于客户端所遵从的且其主版本不高于已知服务器支持的最高版本的请求版本(如果已知)。客户端 MUST NOT 发送其不遵从的版本。

如果已知服务器错误实现了 HTTP 规范,客户端 MAY 发送较低的请求版本,但仅在客户端至少尝试过一次正常请求并从响应状态码或头字段(例如 Server)判断服务器不能正确处理更高请求版本之后才这样做。

服务器 SHOULD 发送等于服务器所遵从且其主版本小于或等于请求中接收的主版本的最高版本的响应版本。服务器 MUST NOT 发送其不遵从的版本。服务器可以在任何原因下选择发送 505 (HTTP Version Not Supported) 响应以拒绝客户端的主要协议版本。

接收方在收到具有其实现的主版本号但次版本号高于其实现版本的报文时,SHOULD 将该报文作为该主版本中其所遵从的最高次版本来处理。接收方可以假定,当向尚未表明支持更高版本的接收方发送具有更高次版本的报文时,该报文与该主版本的实现足够向后兼容,可由任何同一主版本的实现安全地处理。

6.3. 头字段

在内容之前发送或接收的字段(见 第 5 节)称为“头字段”(口语中简称“headers”)。

报文的 头部部分 由一系列头字段行组成。每个头字段可能修改或扩展报文语义、描述发送方、定义内容或提供附加上下文。

6.4. 内容

HTTP 报文常以完整或部分表示作为报文的 内容 进行传输:在头部部分之后发送的一串八位组,按报文分帧划定。

此内容的抽象定义反映在内容从报文分帧中提取之后的数据。例如,一个 HTTP/1.1 的消息体(见 第 6 节)可能由使用分块传输编码编码的数据流组成——数据块序列、一个零长度块和一个尾部部分——而该消息的内容则仅包括在传输编码解码之后的数据流;它不包括块长度、分块分帧语法,也不包括尾字段(见 第 6.5 节)。

6.4.1. 内容语义

请求中内容的目的由方法语义定义(见 第 9 节)。

例如,PUT 请求内容中的表示(见 第 9.3.4 节)表示在请求成功应用后 目标资源 的期望状态,而 POST 请求内容中的表示(见 第 9.3.3 节)表示要由目标资源处理的信息。

在响应中,内容的目的由请求方法、响应状态码(见 第 15 节)以及描述该内容的响应字段来定义。例如,对 GET(见 第 9.3.1 节) 的 200 (OK) 响应的内容表示在报文发生时刻(见 第 6.6.1 节)观察到的 目标资源 的当前状态,而在对 POST 的响应中相同状态码的内容可能表示处理结果或应用处理后目标资源的新状态。

对 GET 的 206 (Partial Content) 响应的内容包含所选表示的单个部分或包含该表示多个部分的多部分消息体,如 第 15.3.7 节 所述。

带有错误状态码的响应消息通常包含表示错误条件的内容,使得内容描述错误状态以及建议的解决步骤。

对 HEAD 请求方法(见 第 9.3.2 节)的响应从不包含内容;相关的响应头字段仅指示若方法为 GET(见 第 9.3.1 节)时其值会是什么。

对 CONNECT 请求方法的 2xx (Successful) 响应会将连接切换为隧道模式,而不是包含内容(见 第 9.3.6 节)。

所有 1xx (Informational)204 (No Content)304 (Not Modified) 响应均不包含内容。

所有其他响应都包含内容,尽管该内容可能长度为零。

6.4.2. 标识内容

当完整或部分表示作为报文内容被传输时,发送方提供或接收方确定与该特定表示对应的资源标识符通常是可取的。例如,请求某资源的客户端可能希望获得特定于返回内容的标识符(例如“20210720T1711 时 Laguna Beach 的天气报告”)。对于来自预期随时间变化表示的资源的内容,这对共享或收藏链接很有用。

对于请求报文:

  • 如果请求含有 Content-Location 头字段,则发送方断言该内容是由 Content-Location 字段值标识的资源的一个表示。然而,除非可以通过其他手段(本规范未定义)验证,否则该断言无法被信任。该信息仍可能对修订历史链接有用。
  • 否则,HTTP 对该内容不提供标识,但更具体的标识符可能在内容本身中提供。

对于响应报文,按以下规则顺序检查直到找到匹配项:

  1. 如果请求方法是 HEAD 或响应状态码是 204 (No Content)304 (Not Modified),则响应中无内容。
  2. 如果请求方法是 GET 且响应状态码是 200 (OK),则内容是 目标资源 的一个表示(见 第 7.1 节)。
  3. 如果请求方法是 GET 且响应状态码是 203 (Non-Authoritative Information),则内容是由中间件提供的可能被修改或增强的 目标资源 的表示。
  4. 如果请求方法是 GET 且响应状态码是 206 (Partial Content),则内容是目标资源表示的一个或多个部分。
  5. 如果响应具有 Content-Location 头字段且其字段值引用与目标 URI 相同的 URI,则内容是目标资源的一个表示。
  6. 如果响应具有 Content-Location 头字段且其字段值引用与目标 URI 不同的 URI,则发送方断言该内容是由 Content-Location 字段值标识的资源的一个表示。然而,除非可以通过其他手段验证,否则该断言无法被信任。
  7. 否则,HTTP 对该内容不提供标识,但更具体的标识符可能在内容本身中提供。

6.5. 尾字段

位于 尾部部分 中的字段(见 第 5 节)称为“尾字段”(口语中简称“trailers”)。尾字段可用于提供报文完整性校验、数字签名、传递度量或后处理状态信息。

尾字段应与头部部分中的字段分开处理和存储,以避免与头部部分完成时已知的报文语义发生矛盾。某些头字段的存在或缺失可能会在收到尾字段之前影响对报文整体的路由或处理选择;这些选择无法被随后发现的尾字段撤销。

6.5.1. 尾字段使用的限制

仅当所使用的 HTTP 版本支持且由显式分帧机制启用时,才可能存在尾部部分。例如,HTTP/1.1 的分块传输编码允许在内容之后发送尾部部分(参见 第 7.1.2 节)。

许多字段无法在头部部分之外处理,因为它们的评估在接收内容之前就是必要的,例如那些描述报文分帧、路由、认证、请求修饰、响应控制或内容格式的字段。发送方 MUST NOT 生成尾字段,除非发送方知道相应头字段名称的定义允许该字段在尾部中发送。

尾字段对于将报文从一个协议版本转发到另一个版本的中间件来说可能难以处理。如果整个报文可以在传输途中缓冲,某些中间件可以在转发之前将尾字段合并到头部部分(如适当)。然而在大多数情况下,尾字段只是被丢弃。接收方 MUST NOT 将尾字段合并到头部部分,除非接收方理解相应头字段的定义并且该定义明确允许且定义了如何安全地合并尾字段值。

在请求的 TE 头字段(见 第 10.1.4 节)中出现关键字 "trailers" 表明客户端愿意接受尾字段,代表其自身和任何下游客户端。对于来自中间件的请求,这意味着所有下游客户端都愿意在转发的响应中接受尾字段。注意,出现 "trailers" 并不意味着客户端(们)会处理响应中的任何特定尾字段;仅表示尾部部分不会被任何客户端丢弃。

由于尾字段在传输中可能被丢弃,服务器 SHOULD NOT 生成它认为用户代理必须接收的尾字段。

6.5.2. 尾字段的处理

“Trailer” 头字段(见 第 6.6.2 节)可以被发送以指示可能在尾部部分发送的字段名,这允许接收方在处理内容之前为接收这些指示的元数据做准备。例如,如果某个字段名表明在接收内容时应动态计算校验和并在收到尾字段值后立即检查,这会很有用。

与头字段类似,具有相同名称的尾字段按接收顺序处理;多个同名尾字段行的语义等同于将多个值作为成员列表追加。可能在一条消息中被多次生成的尾字段 MUST 被定义为基于列表的字段,即使每个成员值仅在接收到每个字段行时处理一次。

在报文结束时,接收方 MAY 将接收到的一组尾字段视为一个与头字段类似但独立的 name/value 对数据结构。针对打算在尾部使用的字段,其字段规范中可以定义额外的处理期望(如果有的话)。

6.6. 报文元数据

描述报文本身的字段,例如报文何时以及如何生成,可以出现在请求和响应中。

6.6.1. Date

“Date” 头字段表示报文的起始日期和时间,具有与 Internet Message Format 中定义的 Origination Date Field (orig-date) 相同的语义。字段值为 HTTP-date,定义见 第 5.6.7 节

一个示例如下

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

生成 Date 头字段的发送方 SHOULD 生成其字段值作为报文生成时间的最佳可用近似。理论上,该日期应表示在生成报文内容之前的那一刻。实际上,发送方可以在报文生成的任何时刻生成该日期值。

具有时钟的源服务器(参见 第 5.6.7 节MUST 在所有 2xx (Successful)3xx (Redirection)4xx (Client Error) 响应中生成 Date 头字段,并且 MAY1xx (Informational)5xx (Server Error) 响应中生成 Date 头字段。

没有时钟的源服务器 MUST NOT 生成 Date 头字段。

具有时钟的接收方在收到没有 Date 头字段的响应报文时,MUST 记录其接收时间,并在将该报文缓存或转发下游时向报文头部追加相应的 Date 头字段。

具有时钟的接收方在收到带有无效 Date 头字段值的响应时 MAY 用响应的接收时间替换该值。

用户代理 MAY 在请求中发送 Date 头字段,尽管通常不会这样做,除非认为它向服务器传达的信息是有用的。例如,HTTP 的定制应用可能在用户代理与服务器的时钟存在差异时期望服务器基于该差异调整对用户请求的解释,因此会传送 Date。

6.6.2. Trailer

“Trailer” 头字段提供发送方预计将在该报文的尾部部分发送的字段名列表。这允许接收方在开始处理内容之前为接收指示的元数据做准备。

例如,发送方可以指示在内容流传输时会计算签名,并在尾字段中提供最终签名值。这允许接收方在接收内容的同时进行相同的校验。

打算在报文中生成一个或多个尾字段的发送方 SHOULD 在该报文的头部部分生成 Trailer 头字段,以指示哪些字段可能出现在尾部。

如果中间件在传输过程中丢弃了尾部部分,Trailer 字段可以提示哪些元数据丢失,尽管不能保证 Trailer 的发送方总是会发送所命名的字段。

7. HTTP 报文路由

HTTP 请求报文的路由由每个客户端根据目标资源、客户端的代理配置以及入站连接的建立或重用来决定。相应的响应路由沿着相同的连接链返回到客户端。

7.1. 确定目标资源

尽管 HTTP 在各种应用中被使用,大多数客户端依赖与通用 Web 浏览器相同的资源标识机制和配置技术。即使通信选项在客户端配置中是硬编码的,我们也可以将它们的组合效果视为一个 URI 引用(第 4.1 节)。

URI 引用会解析为其绝对形式以获得 target URI。目标 URI 排除了引用的片段分量(如果有),因为片段标识符保留用于客户端处理([URI], 第 3.5 节)。

为对 target resource 执行操作,客户端发送一个请求报文,其中包含其已解析 target URI 的足够分量,以便接收方能够识别相同的资源。出于历史原因,解析后的 target URI 分量统称为 request target,并作为报文控制数据以及 Host 头字段在报文中发送(第 7.2 节)。

有两种不寻常的情况,其请求目标分量采用方法特定的形式:

  • 对于 CONNECT(第 9.3.6 节),请求目标为隧道目的地的主机名和端口号,中间以冒号分隔。
  • 对于 OPTIONS(第 9.3.7 节),请求目标可以是单个星号 ("*")。

详情见各方法定义。这些形式不得与其他方法混用(MUST NOT)。

在收到客户端请求后,服务器根据其本地配置和传入连接上下文,从接收到的分量重建目标 URI。该重建依赖于每个主要协议版本。例如,第 3.3 节[HTTP/1.1] 定义了服务器如何确定 HTTP/1.1 请求的目标 URI。

7.2. Host and :authority

请求中的 "Host" 头字段提供来自目标 URI 的主机和端口信息,使源服务器在为多个主机名处理请求时能够区分不同的资源。

在 HTTP/2 [HTTP/2] 和 HTTP/3 [HTTP/3] 中,Host 头字段在某些情况下由请求控制数据的 ":authority" 伪头字段取代。

  Host = uri-host [ ":" port ] ; Section 4

目标 URI 的 authority 信息对处理请求至关重要。除非将该信息作为 ":authority" 伪头字段发送,否则用户代理 MUST 在请求中生成 Host 头字段。发送 Host 的用户代理 SHOULD 将其作为请求头部部分的第一个字段发送。

例如,对源服务器 的 GET 请求将以如下内容开始:

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

由于主机和端口信息作为应用层路由机制,它经常成为恶意软件的目标,用于污染共享缓存或将请求重定向到非预期服务器。如果拦截代理在未先验证被拦截的连接是否针对该主机的有效 IP 地址的情况下,依赖主机和端口信息将请求重定向到内部服务器,或将其用作共享缓存的缓存键,则该代理尤为脆弱。

7.3. 入站请求路由

一旦确定了目标 URI 及其 origin,客户端会决定是否需要发起网络请求以完成所需语义,如果需要,则决定该请求应定向到何处。

7.3.1. 到缓存

如果客户端具有缓存 [CACHING] 且请求可以由其满足,则通常会先将请求定向到缓存。

7.3.2. 到代理

如果请求不能由缓存满足,典型客户端会检查其配置以确定是否应使用代理来满足该请求。代理配置依实现而异,但通常基于 URI 前缀匹配、选择性 authority 匹配或两者兼有,并且代理自身通常由 "http" 或 "https" URI 标识。

如果适用 "http" 或 "https" 代理,客户端通过建立(或重用)到该代理的入站连接,然后向其发送一个包含与客户端 target URI 匹配的 request target 的 HTTP 请求报文来进行连接。

7.3.3. 到 origin

如果没有适用的代理,典型客户端将调用一个处理例程(针对目标 URI 的 scheme 特定)以获取对已识别资源的访问。如何实现取决于目标 URI 的 scheme,并由其关联规范定义。

第 4.3.2 节 定义了如何通过建立(或重用)到所识别 origin 的入站连接并向其发送包含与客户端 target URI 匹配的 request target 的 HTTP 请求报文来获取对 "http" 资源的访问。

第 4.3.3 节 定义了如何通过建立(或重用)到对所识别 origin 具有权威性的 origin 服务器的受保护连接,并向其发送包含与客户端 target URI 匹配的 request target 的 HTTP 请求报文来获取对 "https" 资源的访问。

7.4. 拒绝错误定向的请求

一旦服务器接收到并足够解析了请求以确定其目标 URI,服务器便决定是自行处理该请求、将该请求转发到另一个服务器、重定向客户端到不同资源、以错误响应,还是断开连接。该决定可能受请求或连接上下文的任意信息影响,但特别针对服务器是否被配置为处理该目标 URI 的请求以及连接上下文是否适合该请求。

例如,请求可能被故意或意外地错误定向,致使接收到的 Host 头字段中的信息与连接的主机或端口不一致。如果连接来自受信任的网关,则此类不一致可能是预期的;否则,它可能表示试图绕过安全过滤器、诱使服务器交付非公开内容或污染缓存的企图。有关消息路由的安全注意事项,请参见 第 17 节

除非连接来自受信任的网关,否则如果目标 URI 的任何方案特定要求未得到满足,origin 服务器 MUST 拒绝该请求。特别地,针对 "https" 资源的请求 MUST 被拒绝,除非该请求是在通过对该目标 URI 的 origin 有效的证书保护的连接上接收的,如 第 4.2.2 节 所定义。

响应中的 421 (Misdirected Request) 状态码表示 origin 服务器已拒绝该请求,因为它看起来被错误地定向了(参见 第 15.5.20 节)。

7.5. 响应关联

一个连接可能用于多次请求/响应交换。用于在请求和响应报文之间进行关联的机制依赖于版本;某些 HTTP 版本使用报文的隐式顺序,而其他版本使用显式标识符。

所有响应,无论状态码如何(包括 中间 响应),都可以在请求被接收后任何时间发送,即使该请求尚未完成。一个响应可以在其对应的请求完成之前就完成(参见 第 6.1 节)。同样,客户端不应被期望等待任何特定时间来接收响应。如果在合理时间内未收到响应,客户端(包括中间件)可能放弃该请求。

当客户端在仍在发送关联请求时收到响应,客户端 SHOULD 继续发送该请求,除非收到明确的相反指示(参见例如 第 9.5 节[HTTP/1.1]第 6.4 节[HTTP/2])。

7.6. 报文转发

第 3.7 节 所述,中间件在处理 HTTP 请求和响应时可以承担多种角色。有些中间件用于提高性能或可用性,其他用于访问控制或过滤内容。由于 HTTP 流具有类似管道-过滤器架构的特性,中间件对流的任一方向都可以进行任意程度的增强(或干预),没有内在限制。

期望中间件即便遇到未被识别的协议元素(例如新方法、状态码或字段名)时也能转发报文,因为这能为下游接收方保留可扩展性。

非作为隧道操作的中间件 MUST 实现 Connection 头字段(如 第 7.6.1 节 所规定),并在转发时排除那些仅用于入站连接的字段。

中间件 MUST NOT 将报文转发回自身,除非它能够防止无限请求循环。一般情况下,中间件应该识别自身的服务器名称,包括任何别名、本地变体或字面 IP 地址,并直接响应此类请求。

HTTP 报文可以作为流进行解析以实现增量处理或向下游转发。然而,发送方和接收方不能依赖于部分报文的增量交付,因为某些实现可能为了网络效率、安全检查或内容转换而缓冲或延迟报文转发。

7.6.1. Connection

"Connection" 头字段允许发送方列出当前连接所期望的控制选项。

连接选项不区分大小写。

当除 Connection 之外的字段用于为当前连接提供控制信息时,发送方 MUST 在 Connection 头字段中列出相应的字段名。注意某些 HTTP 版本禁止使用字段来传达此类信息,因此也不允许 Connection 字段。

中间件在转发消息之前 MUST 解析收到的 Connection 头字段,并对该字段中的每个 connection-option,从消息中移除与该 connection-option 同名的任何头字段或尾字段,然后移除 Connection 头字段本身(或以中间件为转发消息设置的控制选项替换之)。

因此,Connection 头字段提供了一种声明性方式,用以区分仅针对直接接收方(“hop-by-hop”)的字段与针对链上所有接收方(“end-to-end”)的字段,使消息具有自描述性,并允许在不担心旧中间件盲目转发的情况下部署将来针对连接的扩展。

此外,中间件 SHOULD 在应用字段语义后移除或替换已知在转发前需要移除的字段,无论它们是否出现在 connection-option 中。这包括但不限于:

发送方 MUST NOT 发送对应于面向所有内容接收方的字段的连接选项。例如,Cache-Control 从不适合作为连接选项(见 第 5.2 节[CACHING])。

连接选项并不总是对应于消息中存在的字段,因为如果连接选项没有关联参数,则可能不需要连接特定字段。相反,如果收到连接特定字段但没有相应的连接选项,通常表明该字段被中间件错误地转发,应由接收方忽略。

在定义新的不对应字段的连接选项时,规范作者应当仍然保留相应的字段名以避免将来的冲突。这类保留字段名在 “Hypertext Transfer Protocol (HTTP) Field Name Registry”(第 16.3.1 节)中注册。

7.6.2. Max-Forwards

"Max-Forwards" 头字段为 TRACE(第 9.3.8 节)和 OPTIONS(第 9.3.7 节)请求方法提供了一种机制,用于限制请求被代理转发的次数。当客户端尝试跟踪似乎在链中失败或循环的请求时,这会很有用。

Max-Forwards 的值是一个十进制整数,指示该请求报文可以被转发的剩余次数。

每个接收到包含 Max-Forwards 头字段的 TRACE 或 OPTIONS 请求的中间件 MUST 在转发请求之前检查并更新其值。如果接收到的值为零 (0),中间件 MUST NOT 转发请求;相反,中间件 MUST 作为最终接收方进行响应。如果接收到的 Max-Forwards 值大于零,中间件 MUST 在转发的报文中生成一个更新后的 Max-Forwards 字段,其字段值为 a) 接收值减一 或 b) 接收方对 Max-Forwards 支持的最大值,两者中较小者。

接收方 MAY 忽略在其他请求方法中收到的 Max-Forwards 头字段。

7.6.3. Via

"Via" 头字段指示用户代理与服务器之间(在请求上)或源服务器与客户端之间(在响应上)存在的中间协议和接收方,类似于电子邮件中的 "Received" 头字段(参见 RFC5322 第 3.6.7 节)。Via 可用于跟踪消息转发、避免请求循环以及识别请求/响应链上发送方的协议能力。

Via 字段值的每个成员表示已转发该消息的代理或网关。每个中间件追加其关于消息接收方式的信息,最终结果按转发接收方的顺序排列。

代理 MUST 在其转发的每个消息中发送适当的 Via 头字段,如下所述。HTTP 到 HTTP 的网关 MUST 在每个入站请求消息中发送适当的 Via 头字段,并且在转发的响应消息中 MAY 发送 Via 头字段。

对于每个中间件,received-protocol 指示上游发送该消息时使用的协议和协议版本。因此,Via 字段值记录了请求/响应链上声明的协议能力,使其对下游接收方可见;这对于决定在响应中或后续请求中使用哪些向后不兼容的特性可能是安全的很有用。如需简洁,当 received-protocol 为 HTTP 时可省略 protocol-name。

received-by 部分通常为随后转发消息的接收方服务器或客户端的主机及可选端口号。然而,如果真实主机被认为是敏感信息,发送方 MAY 用伪名替换之。如果未提供端口,接收方 MAY 将其解释为在 received-protocol 的默认端口(如果有)上接收。

发送方 MAY 生成注释以标识每个接收方的软件,类似于 User-AgentServer 头字段。然而,Via 中的注释是可选的,接收方 MAY 在转发消息之前移除它们。

例如,一个请求消息可以由 HTTP/1.0 的用户代理发送到一个代号为 "fred" 的内部代理,后者使用 HTTP/1.1 将请求转发到位于 p.example.net 的公共代理,公共代理再将请求转发到 www.example.com 的源服务器。www.example.com 收到的请求将包含如下 Via 头字段:

Via: 1.0 fred, 1.1 p.example.net

充当网络防火墙门户的中间件 SHOULD NOT 转发防火墙区域内主机的名称和端口,除非已显式启用此行为。如果未启用,此类中间件 SHOULD 将接收到的任何来自防火墙内部主机的 received-by 主机替换为该主机的适当伪名。

中间件 MAY 将 Via 头字段列表中具有相同 received-protocol 值的有序子序列合并为单个成员。例如,

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

可以折叠为

Via: 1.0 ricky, 1.1 mertz, 1.0 lucy

发送方 SHOULD NOT 合并多个列表成员,除非它们均在相同的组织控制之下且主机已被替换为伪名。发送方 MUST NOT 合并具有不同 received-protocol 值的成员。

7.7. 报文变换

一些中间件提供用于变换报文及其内容的功能。例如,代理可能会在图像格式之间转换以节省缓存空间或减少慢链路上的流量。然而,当这些变换应用于用于关键应用(如医学影像或科学数据分析)的内容时,尤其在使用完整性校验或数字签名来确保接收内容与原始内容一致的情况下,可能会出现操作性问题。

如果一个 HTTP 到 HTTP 的代理被设计或配置为以语义上有意义的方式修改报文(即超出正常 HTTP 处理所要求的修改,且会以某种方式对原始发送方或下游接收方产生重要影响),则称其为 transforming proxy。例如,transforming proxy 可能充当共享注释服务器(修改响应以包含指向本地注释数据库的引用)、恶意软件过滤器、格式转码器或隐私过滤器。这样的变换被假定为由选择该代理的客户端(或客户端组织)所期望。

如果代理收到的目标 URI 所含主机名不是完全合格域名(FQDN),则代理 MAY 在转发请求时向其收到的主机名添加自己的域名。如果目标 URI 包含完全合格域名,代理 MUST NOT 更改该主机名。

代理 MUST NOT 在将接收到的目标 URI 转发给下一个入站服务器时修改该 URI 的 "absolute-path" 和 "query" 部分,除非该转发协议要求这样做。例如,通过 HTTP/1.1 将请求转发到源服务器的代理将根据请求方法替换空路径为 "/"(参见 HTTP/1.1 第 3.2.1 节)或 "*"(参见 HTTP/1.1 第 3.2.4 节)。

如果响应报文包含 no-transform 缓存指令(参见 第 5.2.2.6 节),代理 MUST NOT 变换该响应消息的内容(见 第 6.4 节)。注意,这并不适用于不改变内容的报文变换,例如添加或移除传输编码(参见 HTTP/1.1 第 7 节)。

对于不包含 no-transform 缓存指令的报文,代理 MAY 对其内容进行变换。对 200 (OK) 响应内容进行变换的代理可以通过将响应状态码更改为 203 (Non-Authoritative Information) 来告知下游接收方已应用变换(参见 第 15.3.4 节)。

代理 SHOULD NOT 修改提供有关通信链端点、资源状态或所选表示(不含内容本身)的头字段,除非该字段的定义明确允许此类修改或为了隐私或安全而认为有必要进行修改。

7.8. Upgrade

"Upgrade" 头字段旨在提供在同一连接上从 HTTP/1.1 过渡到某个其他协议的简单机制。

客户端 MAY 在请求的 Upgrade 头字段中发送协议名列表,以按优先顺序(从高到低)邀请服务器在发送最终响应之前切换到一个或多个所列协议。服务器 MAY 忽略收到的 Upgrade 头字段,如果它希望在该连接上继续使用当前协议。Upgrade 不能用于强制协议变更。

尽管协议名在注册时有首选大小写,接收方在匹配 protocol-name 与其支持的协议时 SHOULD 使用不区分大小写的比较。

发送 101 (Switching Protocols) 响应的服务器 MUST 发送 Upgrade 头字段以指示连接正在切换到的新协议;如果正在切换多个协议层,发送方 MUST 按层升序列出协议。服务器 MUST NOT 切换到未在相应请求的 Upgrade 头字段中由客户端指示的协议。服务器 MAY 在选择新协议时忽略客户端指示的优先顺序,并基于其他因素(例如请求的性质或服务器当前负载)进行选择。

发送 426 (Upgrade Required) 响应的服务器 MUST 发送 Upgrade 头字段以指示可接受的协议,按优先顺序从高到低排列。

服务器 MAY 在任何其他响应中发送 Upgrade 头字段,以便在适合未来请求时按优先顺序(从高到低)宣告其支持切换到所列协议。

下例为客户端发送的一个假设示例:

GET /hello HTTP/1.1
Host: www.example.com
Connection: upgrade
Upgrade: websocket, IRC/6.9, RTA/x11

协议变更后应用层通信的能力和性质完全取决于所选的新协议。然而,在发送 101 (Switching Protocols) 响应之后,服务器应继续以新协议等效的方式响应原始请求(即在协议更改后服务器仍有一个未完成的请求需要满足,且应在不要求重复请求的情况下完成该请求)。

例如,如果在 GET 请求中收到 Upgrade 头字段并且服务器决定切换协议,它首先以 HTTP/1.1 发送一个 101 (Switching Protocols) 消息,然后立即发送新协议中对应于对目标资源的 GET 请求的响应。这允许将连接升级到具有与 HTTP 相同语义的协议,而无需额外的往返延迟。服务器 MUST NOT 切换协议,除非接收到的报文语义可以被新协议所遵守;OPTIONS 请求可以由任何协议遵守。

下例为上面假设请求的一个示例响应:

HTTP/1.1 101 Switching Protocols
Connection: upgrade
Upgrade: websocket

[... data stream switches to websocket with an appropriate response
(as defined by new protocol) to the "GET /hello" request ...]

Upgrade 的发送方 MUST 还在 Connection 头字段中发送 "Upgrade" 连接选项(见 第 7.6.1 节),以告知中间件不要转发该字段。接收到在 HTTP/1.0 请求中的 Upgrade 头字段的服务器 MUST 忽略该 Upgrade 字段。

客户端在完全发送完请求消息之前不能开始在连接上使用已升级协议(即客户端不能在消息中途改变其正在发送的协议)。如果服务器在收到带有 "100-continue" 期待的 Expect 头字段的 Upgrade 时,服务器 MUST 在发送 101 (Switching Protocols) 响应之前先发送一个 100 (Continue) 响应。

Upgrade 头字段仅适用于在现有连接之上切换协议;它不能用于切换底层连接(传输)协议,也不能用于将现有通信切换到不同连接。对于这些用途,更合适的做法是使用 3xx (Redirection) 响应(见 第 15.4 节)。

本规范仅为超文本传输协议族定义协议名 "HTTP",其定义由 第 2.5 节 的 HTTP 版本规则和对本规范的未来更新给出。其他协议名应通过 第 16.7 节 中定义的注册程序进行注册。

8. 表示数据与元数据

8.1. 表示数据

与 HTTP 报文关联的表示数据要么作为报文的内容提供,要么由报文语义和目标 URI 引用。表示数据的格式和编码由表示元数据头字段定义。

表示数据的数据类型通过头字段 Content-TypeContent-Encoding 来确定。它们定义了一个两层、按顺序的编码模型:

  representation-data := Content-Encoding( Content-Type( data ) )

8.2. 表示元数据

表示头字段提供关于表示的元数据。当报文包含内容时,表示头字段描述如何解释该数据。在对 HEAD 请求的响应中,表示头字段描述了如果相同请求为 GET 时本应包含在内容中的表示数据。

8.3. Content-Type

“Content-Type” 头字段指示所关联表示的媒体类型:要么是包含在报文内容中的表示,要么是由报文语义确定的 selected representation。所指示的媒体类型定义了数据格式以及在解码任何由 Content-Encoding 指示的内容编码后,接收方应如何在接收到的报文语义范围内处理该数据。

媒体类型在 第 8.3.1 节 中定义。一个字段示例如下:

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

生成包含内容的报文的发送方 SHOULD 在该报文中生成 Content-Type 头字段,除非发送方不知道所包含表示的预期媒体类型。如果未出现 Content-Type 头字段,接收方 MAY 假定媒体类型为 "application/octet-stream"(参见 RFC2046 第 4.5.1 节)或检查数据以确定其类型。

在实践中,资源所有者并不总是正确配置其源服务器以为给定表示提供正确的 Content-Type。一些用户代理会检查内容并在某些情况下覆盖接收到的类型(例如,参见 Sniffing)。这种“MIME 嗅探”有可能对数据得出错误结论,从而使用户暴露于额外的安全风险(例如“特权提升”)。此外,不同的媒体类型常常共享一种通用的数据格式,仅在如何处理数据上有所不同,而仅通过检查数据本身无法区分这些差异。如果实现了嗅探,建议实现者提供一种方式让用户禁用它。

虽然 Content-Type 被定义为单例字段,但有时会被错误地多次生成,导致合并后的字段值看起来像一个列表。接收方常尝试通过使用列表中最后一个语法上有效的成员来处理该错误;如果不同实现对错误处理行为不同,这会导致互操作性和安全问题。

8.3.1. 媒体类型

HTTP 在 Content-Type(第 8.3 节)和 Accept(第 12.5.1 节) 头字段中使用媒体类型(参见 RFC2046),以提供开放且可扩展的数据类型和类型协商。媒体类型定义数据格式及各种处理模型:根据报文上下文如何处理该数据。

type 和 subtype 标记不区分大小写。

type/subtype MAY 后跟以分号分隔的参数(见 第 5.6.6 节),以 name/value 对形式出现。参数的有无可能对媒体类型的处理有重要意义,取决于其在媒体类型注册表中的定义。参数值可能区分大小写,也可能不区分,取决于参数名的语义。

例如,下面的媒体类型在描述使用 UTF-8 字符编码的 HTML 文本数据时是等价的,但第一个为一致性首选(在 RFC2046 第 4.1.2 节中定义,"charset" 参数值为不区分大小写):

  text/html;charset=utf-8
  Text/HTML;Charset="utf-8"
  text/html; charset="utf-8"
  text/html;charset=UTF-8

媒体类型应根据 [BCP13] 中定义的程序向 IANA 注册。

8.3.2. 字符集

HTTP 使用 charset 名称来指示或协商文本表示的字符编码方案(参见 RFC6365 第 2 节)。在本文档定义的字段中,charset 名称要么出现在参数中(如 Content-Type),要么在 Accept-Encoding 中作为普通 token 出现。在两种情况下,charset 名称都是不区分大小写匹配的。

字符集名称应按照 RFC2978 第 2 节中定义的程序在 IANA 的 "Character Sets" 注册表中注册(见 https://www.iana.org/assignments/character-sets)。

8.3.3. 多部分类型

MIME 提供多种 "multipart" 类型 —— 在单个消息体中封装一个或多个表示。所有 multipart 类型共享一个通用语法(参见 RFC2046 第 5.1.1 节),并在媒体类型值中包含 boundary 参数。消息体本身是一个协议元素;发送方 MUST 仅生成 CRLF 来表示各个主体部分之间的换行。

HTTP 报文分帧并不使用 multipart 边界作为消息体长度的指示,尽管生成或处理内容的实现可能会使用它。例如,"multipart/form-data" 类型经常用于在请求中携带表单数据(参见 RFC7578),而 "multipart/byteranges" 类型由本规范定义,用于某些 206 (Partial Content) 响应(参见 第 15.3.7 节)。

8.4. Content-Encoding

“Content-Encoding” 头字段指示在表示上已应用了哪些内容编码(超出媒体类型固有的编码),从而指出为了获得由 Content-Type 指示的媒体类型需要应用哪些解码机制。Content-Encoding 主要用于允许压缩表示的数据而不丧失其底层媒体类型的标识。

其用法示例为:

Content-Encoding: gzip

如果对表示应用了一个或多个编码,应用这些编码的发送方 MUST 生成一个 Content-Encoding 头字段,列出按应用顺序排列的内容编码。注意,名为 "identity" 的编码由于在 Accept-Encoding 中具有特殊角色,因此 SHOULD NOT 被包含。

关于编码参数的其他信息可以由本规范未定义的其他头字段提供。

与 Transfer-Encoding(见 HTTP/1.1 第 6.1 节)不同,列在 Content-Encoding 中的编码是表示的一个特性;表示以编码后的形式定义,除非元数据定义另有说明,否则关于该表示的所有其他元数据均针对编码后的形式。通常表示仅在呈现或类似使用之前才被解码。

如果媒体类型包含固有编码(例如始终压缩的数据格式),则即使该编码恰好与某个内容编码算法相同,也不会在 Content-Encoding 中重复说明。只有在该编码被再次应用以形成表示时,才会将其列出。同样,源服务器可能会选择将相同数据发布为多个表示,这些表示只在编码是作为 Content-Type 的一部分还是作为 Content-Encoding 上有所区别,因为某些用户代理在处理每种响应时的行为不同(例如,弹出“另存为...”对话框而不是自动解压并呈现内容)。

如果请求报文中的表示使用了不可接受的内容编码,源服务器 MAY 使用 415 (Unsupported Media Type) 状态码进行响应。

8.4.1. 内容编码

Content coding 值指示已对表示应用或可应用的编码变换。Content codings 主要用于允许对表示进行压缩或以其他有用方式变换而不丢失其底层媒体类型标识且无信息损失。通常,表示以编码形式存储并直接传输,只有最终接收方才解码。

所有 content codings 不区分大小写,并应在 “HTTP Content Coding Registry” 中注册(见 第 16.6 节)。

content-coding 值用于 Accept-Encoding(第 12.5.3 节)和 Content-Encoding(第 8.4 节) 头字段。

8.4.1.1. Compress Coding

“compress” 编码是自适应的 Lempel-Ziv-Welch (LZW) 编码(参见 Welch),通常由 UNIX 的 "compress" 程序生成。接收方 SHOULD 将 "x-compress" 视为与 "compress" 等价。

8.4.1.2. Deflate Coding

“deflate” 编码是一个 "zlib" 数据格式(参见 RFC1950),其包含使用 LZ77 压缩算法与 Huffman 编码相结合的 "deflate" 压缩数据流(参见 RFC1951)。

8.4.1.3. Gzip Coding

“gzip” 编码是基于 LZ77 的编码并带有 32 位循环冗余校验 (CRC),通常由 gzip 压缩程序生成(参见 RFC1952)。接收方 SHOULD 将 "x-gzip" 视为与 "gzip" 等价。

8.5. Content-Language

“Content-Language” 头字段描述面向该表示的目标受众的自然语言(可能不等同于表示中使用的所有语言)。

语言标签在 第 8.5.1 节 中定义。Content-Language 的主要用途是允许用户根据其偏好的语言识别和区分表示。因此,如果内容仅面向丹麦语读者,适当的字段为:

Content-Language: da

如果未指定 Content-Language,默认假定内容面向所有语言受众。这可能意味着发送方认为内容不特定于任何自然语言,或发送方不知道该内容面向哪种语言受众。

对于面向多种受众的内容,可以列出多种语言(MAY)。例如,若同时以毛利语和英语呈现《怀唐伊条约》原文与译文,则可使用:

Content-Language: mi, en

然而,仅因为表示中出现了多种语言并不意味着它面向多种语言受众。例如,《拉丁语入门》这类入门教材明显面向英语读者,在这种情况下 Content-Language 应仅包含 "en"。

Content-Language MAY 应用于任何媒体类型 —— 它不限于文本文档。

8.5.1. 语言标签

语言标签按 RFC5646 定义,用以标识人类用于交流信息的自然语言(口语、书写或其他方式)。明确排除了计算机语言。

HTTP 在 Accept-LanguageContent-Language 头字段中使用语言标签。Accept-Language 使用更宽泛的 language-range 产生式(见第 12.5.4 节),而 Content-Language 使用下文定义的 language-tag 产生式。

  language-tag = <Language-Tag, see [RFC5646], Section 2.1>

语言标签由一个或多个不区分大小写的子标签组成,子标签之间用连字符 ("-", %x2D) 分隔。通常,语言标签由一个主语言子标签组成,用以标识一组相关语言(例如 "en" = 英语),可选地后跟一系列子标签以细化该语言的范围(例如 "en-CA" = 加拿大英语)。语言标签中不允许出现空白。示例标签包括:

  fr, en-US, es-419, az-Arab, x-pig-latin, man-Nkoo-GN

更多信息请参见 RFC5646

8.6. Content-Length

“Content-Length” 头字段指示关联表示的数据长度,为一个以十进制表示的非负整数,表示八位组数。当作为内容传输表示时,Content-Length 专指所封装数据的数量,以便用于定界分帧(例如见 HTTP/1.1 第 6.2 节)。在其他情况下,Content-Length 指示所选表示的当前长度,接收方可用其估算传输时间或与先前存储的表示比较。

示例:

Content-Length: 3495

当方法定义了对封装内容的意义且未发送 Transfer-Encoding 时,用户代理 SHOULD 在请求中发送 Content-Length。例如,用户代理通常在 POST 请求中发送 Content-Length,即使值为 0(表示空内容)。当请求消息不包含内容且方法语义不预期此类数据时,用户代理 SHOULD NOT 发送 Content-Length 头字段。

服务器 MAY 在对 HEAD 请求的响应中发送 Content-Length(见第 9.3.2 节);但服务器 MUST NOT 在此类响应中发送 Content-Length,除非其字段值等于在相同请求改为 GET 时在内容中将要发送的八位组数。

服务器 MAY 在对条件 GET 请求的 304 (Not Modified) 响应中发送 Content-Length;但除非该字段值等于对相同请求的 200 (OK) 响应中将发送的内容字节数,否则服务器 MUST NOT 发送 Content-Length。

服务器 MUST NOT 在任何 1xx (Informational)204 (No Content) 状态码的响应中发送 Content-Length。服务器 MUST NOT 在对 CONNECT 请求的任何 2xx (Successful) 响应中发送 Content-Length(见第 9.3.6 节)。

除上述情形外,在没有 Transfer-Encoding 的情况下,当内容大小在发送完整头部部分之前已知时,源服务器 SHOULD 发送 Content-Length 头字段。这将允许下游接收方测量传输进度、知道接收到的消息何时完整,并可能重用连接以进行额外请求。

任何大于或等于零的 Content-Length 字段值都是有效的。由于内容长度没有预定义上限,接收方 MUST 预期可能出现很大的十进制数,并防止由于整数转换溢出或精度损失导致的解析错误(见第 17.5 节关于协议元素长度攻击的说明)。

由于 Content-Length 在 HTTP/1.1 中用于消息定界,其字段值即使在立即连接未使用 HTTP/1.1 的情况下也会影响下游接收方如何解析消息。如果消息被下游中间件转发,与接收到的消息分帧不一致的 Content-Length 字段值可能导致请求走私或响应分割等安全失效。

因此,发送方 MUST NOT 转发已知不正确的 Content-Length 头字段值的消息。

同样,发送方 MUST NOT 转发一个不符合上述 ABNF 的 Content-Length 头字段值,但有一个例外:如果接收方收到的 Content-Length 字段值由相同的十进制值以逗号分隔的列表重复组成(例如 "Content-Length: 42, 42"),接收方 MAY 拒绝该消息为无效,或将该无效字段值替换为该十进制值的单一实例,因为这很可能表示重复生成或被上游消息处理器合并。

8.7. Content-Location

“Content-Location” 头字段引用一个 URI,该 URI 可用作标识与本消息内容中的表示对应的特定资源。换言之,如果在本消息生成时对该 URI 执行 GET 请求,则 200 (OK) 响应将包含与本消息中封装的表示相同的表示。

字段值要么为 absolute-URI,要么为 partial-URI。在后者情况下(见第 4 节),被引用的 URI 相对于目标 URI(参见 URI 第 5 节)。

Content-Location 的值并不是目标 URI 的替代。它是表示元数据。其语法和语义与 MIME body parts 中同名头字段在 RFC2557 第 4 节中定义的相同。然而,它出现在 HTTP 消息中对 HTTP 接收方具有一些特殊含义。

如果在一个 2xx (Successful) 响应消息中包含 Content-Location 并且其值在转换为绝对形式后与目标 URI 相同,则接收方 MAY 在消息生成时间认为该内容为该资源的当前表示。对于 GET 或 HEAD 请求,这与服务器未提供 Content-Location 时的默认语义相同。对于像 PUT 或 POST 这样的状态改变请求,这意味着服务器的响应包含该资源的新表示,从而可区别于仅报告操作结果(例如 “操作成功!”)的表示。这允许创作应用在无需随后 GET 请求的情况下更新其本地副本。

如果在一个 2xx (Successful) 响应消息中包含 Content-Location 并且其字段值引用的 URI 与目标 URI 不同,则源服务器声明该 URI 是对应于所封装表示的另一个资源标识符。仅当两个标识符属于同一资源所有者时,该声明才可信,这一点无法通过 HTTP 在程序上确定。

  • 对于对 GET 或 HEAD 请求的响应,这是一个指示:目标 URI 属于受内容协商约束的资源,而 Content-Location 字段值是所 selected representation 的更具体标识符。
  • 对于状态改变方法的 201 (Created) 响应,如果 Content-Location 字段值与 Location 字段值相同,则表明该内容是新创建资源的当前表示。
  • 否则,该 Content-Location 表示该内容是关于请求操作状态的一个报告,并且相同的报告可通过未来对该 URI 的 GET 访问获得。例如,通过 POST 执行的一次购买交易可能在 200 (OK) 响应的内容中包含一份收据文档;Content-Location 字段值为将来检索该收据副本提供了一个标识符。

在请求消息中发送 Content-Location 的用户代理表明其值引用用户代理最初获取该表示内容的位置(在用户代理所做的任何修改之前)。换言之,用户代理提供了指向原始表示来源的反向链接。

接收到请求消息中 Content-Location 字段的源服务器 MUST 将该信息视为瞬时的请求上下文,而不是应原样保存为表示一部分的元数据。源服务器 MAY 使用该上下文来指导请求处理或将其保存用于其他用途(例如源链接或版本控制元数据)。然而,源服务器 MUST NOT 使用该上下文信息来更改请求语义。

例如,如果客户端对一个可协商的资源执行 PUT 并且源服务器接受该 PUT(且无重定向),则该资源的新状态应与 PUT 中提供的那个表示一致;Content-Location 不能被用作反向内容选择标识符来仅更新已协商表示中的某一项。如果用户代理希望具有后者语义,应直接对 Content-Location URI 执行 PUT。

8.8. 验证器字段

如果资源元数据可用于在前置条件中(见 第 13.1 节)进行条件请求(见 第 13 节),则该元数据称为 validator。验证器字段传达与所 selected representation 相关的当前验证器(参见 第 3.2 节)。

在对安全请求的响应中,验证器字段描述源服务器在处理响应时选择的表示。注意,取决于方法和状态码语义,一个响应的所选表示未必与响应内容中包含的表示相同。

在对状态改变请求的成功响应中,验证器字段描述已经替代先前所选表示的新表示。

例如,在 201 (Created) 响应中的 ETag 字段传达新创建资源表示的实体标签,以便在后续的条件请求中使用该实体标签作为验证器来防止“丢失更新”问题。

本规范定义了两种常用的用于观察资源状态和测试前置条件的元数据形式:修改日期(见 第 8.8.2 节)和不透明实体标签(见 第 8.8.3 节)。反映资源状态的其他元数据由各类 HTTP 扩展定义,例如 WebDAV(超出本规范范围)。

8.8.1. 弱验证器与强验证器

验证器有两种类型:strong 或 weak。弱验证器易于生成但用于比较时用途较少。强验证器适合比较但可能非常难以(有时甚至不可能)高效生成。HTTP 通过暴露使用的验证器类型并对何时可以将弱验证器用作前置条件施加限制,从而避免要求所有资源都使用相同强度的验证器。

strong validator 是这样的表示元数据:只要对在对 GET 的 200 (OK) 响应内容中可观察到的表示数据发生任何更改,其值就会改变。

强验证器可能因表示元数据的语义性变更而改变(例如 Content-Type),但源服务器应尽量仅在需要使远端缓存和创作工具中的存储响应失效时才更改其值。

缓存条目可能会长期存在,不论过期时间如何。因此,缓存可能尝试使用在遥远过去获得的验证器来验证条目。强验证器在时间上对与特定资源相关的所有表示版本都是唯一的。然而,这并不意味着跨不同资源的表示唯一(即相同的强验证器可同时用于多个资源的表示,并不意味着这些表示等同)。

实践中存在多种强验证器。最佳方式基于严格的版本控制:每一次对表示的更改都会在表示对外可见之前分配唯一的节点名和修订标识符。如果在发送响应头字段之前可获得数据并且摘要不需要在每次验证请求时重新计算,则对表示内容应用抗碰撞哈希函数亦能胜任。然而,如果资源具有仅在元数据上不同但数据格式相同的不同表示(例如在内容协商时),源服务器需要在验证器中加入额外信息以区分这些表示。

相反,weak validator 是可能不会在表示数据每次更改时都改变的表示元数据。这种弱性可能源于计算值的限制(例如时钟分辨率)、无法确保对资源所有可能表示的唯一性,或资源所有者希望将表示按某个自定的等价集合进行分组而非按数据序列唯一区分。

源服务器 SHOULD 在其认为旧的表示不再可接受作为当前表示替代时更改弱实体标签。换言之,当源服务器希望缓存使旧响应失效时,应更改弱实体标签。

例如,天气报告的表示可能每秒变化一次。源服务器可能将这些表示分组并对每组使用相同弱验证器,以允许缓存表示在合理时间内有效(可根据服务器负载或天气质量动态调整)。同样,如果表示的修改时间仅以秒为单位分辨,则当表示在同一秒内被修改两次并在这些修改之间检索时,修改时间可能是弱验证器。

如果某资源的两个或多个同时存在的表示共享相同验证器,则该验证器为弱,除非这些表示在表示数据上完全相同。例如,如果源服务器为带有 gzip 内容编码的表示与未做内容编码的表示发送相同验证器,则该验证器为弱。然而,如果两个同时存在的表示仅在表示元数据上不同(例如两种不同媒体类型对相同表示数据),它们可能共享相同的强验证器。

强验证器可用于所有条件请求,包括缓存验证、部分内容范围请求和防止“丢失更新”。弱验证器仅在客户端不要求与先前获得的表示数据完全相等时可用,例如在验证缓存条目或限制 Web 遍历为最近更改时使用。

8.8.2. Last-Modified

响应中的 “Last-Modified” 头字段提供一个时间戳,指示源服务器在处理请求结束时认为所 selected representation 最后被修改的日期和时间。

示例:

Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
8.8.2.1. 生成

源服务器 SHOULD 在任何其能合理且一致地确定最后修改日期的所选表示上发送 Last-Modified,因为其在条件请求和评估缓存新鲜度时的使用(参见 CACHING)可以显著减少不必要的传输并提高可用性和可伸缩性。

一个表示通常由资源接口后面的许多部分组成。last-modified 时间通常为这些部分中最近一次更改的时间。如何为给定资源确定该值为实现细节,超出本规范范围。

源服务器 SHOULD 尽可能在生成响应的 Date 字段值时或其附近获取表示的 Last-Modified 值。这有助于接收方在表示在响应生成时间附近发生更改时准确评估其修改时间。

具有时钟的源服务器(参见 第 5.6.7 节MUST NOT 生成晚于服务器报文生成时间(Date)的 Last-Modified 日期。如果最后修改时间由实现特定的元数据导出且其值显示为未来时间(相对于源服务器的时钟),则源服务器 MUST 将该值替换为报文生成时间。这可防止将来时间的修改日期对缓存验证产生不利影响。

没有时钟的源服务器 MUST NOT 生成 Last-Modified 日期,除非该日期值由其他系统(大概具有时钟)分配给资源。

8.8.2.2. 比较

当 Last-Modified 时间作为请求中的验证器使用时,除非能根据下列规则推断其为强,否则它隐式为弱:

  • 该验证器正由源服务器与表示的实际当前验证器进行比较,并且,
  • 该源服务器可靠地知道在所给验证器所覆盖的那一秒内相关表示没有发生两次更改;

或者

  • 该验证器将由客户端在 If-Modified-SinceIf-Unmodified-SinceIf-Range 头字段中使用,因为客户端在缓存中有与该表示相关的条目,且
  • 该缓存条目包含一个 Date 值,该值至少比 Last-Modified 值晚一秒,并且客户端有理由相信它们由同一时钟生成,或 Last-Modified 与 Date 值之间的差异足以使时钟同步问题不太可能;

或者

  • 该验证器正由一个中间缓存与其缓存条目中存储的验证器进行比较,并且,
  • 该缓存条目包含一个 Date 值,该值至少比 Last-Modified 值晚一秒,并且缓存有理由相信它们由同一时钟生成,或 Last-Modified 与 Date 值之间的差异足以使时钟同步问题不太可能。

该方法依赖于这样的事实:如果在同一秒内源服务器发送了两个不同的响应但两者具有相同的 Last-Modified 时间,则至少其中一个响应的 Date 值将等于其 Last-Modified 时间。

8.8.3. ETag

响应中的 “ETag” 字段提供所 selected representation 的当前实体标签,该标签在处理请求结束时确定。实体标签是不透明的验证器,用于区分同一资源的多个表示,无论这些不同表示是由于资源状态随时间变化、内容协商导致同一时间存在多个有效表示,还是两者兼而有之。实体标签由一个不透明的带引号字符串组成,可能以弱指示器为前缀。

  ETag       = entity-tag

  entity-tag = [ weak ] opaque-tag
  weak       = %s"W/"
  opaque-tag = DQUOTE *etagc DQUOTE
  etagc      = %x21 / %x23-7E / obs-text
             ; VCHAR except double quotes, plus obs-text

在某些情况下,实体标签比修改日期更可靠,例如当存储修改日期不便、HTTP-date 的一秒分辨率不足或修改日期未被一致维护时。

示例:

ETag: "xyzzy"
ETag: W/"xyzzy"
ETag: ""

实体标签可以是弱或强验证器,默认是强。如果源服务器为某表示提供实体标签且生成该实体标签的方式不满足强验证器的全部特性(见第 8.8.1 节),则源服务器 MUST 通过在不透明值前加上 "W/"(区分大小写)来将该实体标签标记为弱。

发送方 MAY 在尾部部分发送 ETag 字段(见第 6.5 节)。但由于尾部常被忽略,除非实体标签在发送内容时生成,否则优先将 ETag 作为头字段发送。

8.8.3.1. 生成

实体标签的原则是,只有服务作者最了解资源的实现以选择最准确且高效的验证机制,且任何此类机制都可以映射为用于简单比较的一串八位组。由于该值是不透明的,客户端无需了解每个实体标签如何构造。

例如,对所有更改应用实现特定版本控制的资源可能使用内部修订号(可能结合内容协商的差异标识符)来精确区分表示。其他实现可能使用对表示内容的抗碰撞哈希、各种文件属性的组合或具有子秒分辨率的修改时间。

源服务器 SHOULD 在任何其能合理且一致地检测变化的所选表示上发送 ETag,因为实体标签在条件请求和评估缓存新鲜度时的使用能显著减少不必要的传输并提高可用性、可伸缩性和可靠性。

8.8.3.2. 比较

根据比较上下文是否允许使用弱验证器,存在两种实体标签比较函数:

强比较 (Strong comparison)
当且仅当两者均非弱且其 opaque-tags 字符逐个匹配时,两实体标签等同。
弱比较 (Weak comparison)
当且仅当两者的 opaque-tags 字符逐个匹配时,两实体标签等同,不论其是否标记为“弱”。

下面示例展示了一组实体标签对及其在弱比较和强比较下的结果:

Table 3
ETag 1 ETag 2 Strong Comparison Weak Comparison
W/"1" W/"1" no match match
W/"1" W/"2" no match no match
W/"1" "1" no match match
"1" "1" match match
8.8.3.3. 示例:实体标签在内容协商资源中的变化

考虑一个受内容协商约束的资源(见第 12 节),且对 GET 请求的响应表示会根据 Accept-Encoding 请求头字段而不同:

>> Request:

GET /index HTTP/1.1
Host: www.example.com
Accept-Encoding: gzip

在此情况下,响应可能使用或不使用 gzip 内容编码。如果不使用,响应可能如下:

>> Response:

HTTP/1.1 200 OK
Date: Fri, 26 Mar 2010 00:05:00 GMT
ETag: "123-a"
Content-Length: 70
Vary: Accept-Encoding
Content-Type: text/plain

Hello World!
Hello World!
Hello World!
Hello World!
Hello World!

另一个使用 gzip 内容编码的替代表示可能为:

>> Response:

HTTP/1.1 200 OK
Date: Fri, 26 Mar 2010 00:05:00 GMT
ETag: "123-b"
Content-Length: 43
Vary: Accept-Encoding
Content-Type: text/plain
Content-Encoding: gzip

...binary data...

9. 方法

9.1. 概览

请求方法标识符是请求语义的主要来源;它表明客户端发出该请求的目的以及客户端在成功时所期望的结果。

当某些头字段出现在请求中且这些字段的附加语义不与方法冲突时,请求方法的语义可能会被这些头字段的语义进一步细化。例如,客户端可以发送条件请求头字段(第 13.1 节)来使所请求的操作以目标资源的当前状态为条件。

HTTP 被设计为可用作分布式对象系统的接口。请求方法对目标资源调用要应用的操作,其方式类似于向已识别对象发送远程方法调用。

方法标识符区分大小写,因为它可能被用作通向具有区分大小写方法名的面向对象系统的网关。按照惯例,标准化的方法使用全大写的 US-ASCII 字母来定义。

与分布式对象不同,HTTP 中的标准化请求方法并非针对特定资源,因为统一接口在基于网络的系统中提供更好的可见性和重用性(参见 [REST])。一旦定义,标准化方法在应用到任何资源时应具有相同语义,尽管每个资源可自行决定是否实施或允许这些语义。

本规范定义了若干在 HTTP 中常用的标准方法,列示于下表。

表 4
方法名 描述
GET 传输目标资源的当前表示。 9.3.1
HEAD 与 GET 相同,但不传输响应内容。 9.3.2
POST 对请求内容执行资源特定的处理。 9.3.3
PUT 用请求内容替换目标资源的所有当前表示。 9.3.4
DELETE 移除目标资源的所有当前表示。 9.3.5
CONNECT 与标识为目标资源的服务器建立隧道。 9.3.6
OPTIONS 描述目标资源的通信选项。 9.3.7
TRACE 沿到目标资源的路径执行消息回环测试。 9.3.8

所有通用服务器 MUST 支持 GET 和 HEAD 方法。所有其他方法为 OPTIONAL

目标资源允许的方法集合可以在 Allow 头字段中列出(第 10.2.1 节)。但允许的方法集合可能会动态变化。接收未识别或未实现的请求方法的源服务器 SHOULD 使用 501 (Not Implemented) 状态码响应。接收已识别并实现但对目标资源不被允许的方法的源服务器 SHOULD 使用 405 (Method Not Allowed) 状态码响应。

本规范范围之外还定义了其他方法,供 HTTP 使用。所有此类方法应在 “Hypertext Transfer Protocol (HTTP) Method Registry” 中注册(参见 第 16.1 节)。

9.2. 常见方法属性

9.2.1. 安全方法

如果某些请求方法的定义语义基本上为只读,则认为这些方法是 safe;即客户端既不请求也不期望在对目标资源应用安全方法时原点服务器发生任何状态更改。同样,对安全方法的合理使用不应导致任何损害、财产损失或对原点服务器造成不寻常的负担。

对安全方法的此定义并不阻止实现包含潜在有害、非完全只读或在调用安全方法时引起副作用的行为。然而重要的是,客户端并未请求这些额外行为,因此不能为其负责。例如,大多数服务器在完成每个响应时都会将请求信息追加到访问日志文件中,尽管这被认为是安全的,但日志存储可能会填满并导致服务器失败。同样,用户单击网页上的广告通常会产生计费到广告账户的副作用。

在本规范定义的请求方法中,GETHEADOPTIONSTRACE 方法被定义为安全方法。

区分安全与不安全方法的目的是允许自动检索过程(爬虫)和缓存性能优化(预取)在不致造成危害的情况下工作。此外,这还允许用户代理在处理可能不可信内容时对不安全方法的自动使用施加适当约束。

用户代理在向用户呈现潜在操作时 SHOULD 区分安全与不安全方法,以便在请求不安全操作之前使用户能够意识到该操作的不安全性。

当资源被构造为使目标 URI 内的参数具有选择某个动作的效果时,资源所有者有责任确保该动作与请求方法语义一致。例如,网页编辑软件常在查询参数中使用类似 "page?do=delete" 的动作。如果此类资源的目的是执行不安全操作,则资源所有者 MUST 在通过安全请求方法访问时禁用或拒绝该动作。否则,当自动化进程为了链接维护、预取、构建搜索索引等目的对每个 URI 引用执行 GET 时,将产生不良副作用。

9.2.2. 幂等方法

如果对服务器的多次相同请求所产生的预期效果与对该方法的一次请求相同,则认为该请求方法是 idempotent(幂等)。在本规范定义的方法中,PUTDELETE 以及安全请求方法均为幂等。

与安全性定义类似,幂等属性仅适用于用户请求所要求的内容;服务器可以为每个幂等请求单独记录日志、保留修订控制历史或实现其他非幂等的副作用。

幂等方法之所以被区分,是因为如果在客户端在读取服务器响应之前发生通信失败,请求可以被自动重复。例如,如果客户端发送了一个 PUT 请求且底层连接在接收任何响应之前关闭,则客户端可以建立新连接并重试该幂等请求。客户端知道重复该请求将产生相同的预期效果,即使原始请求已成功,响应也可能不同。

除非客户端有某种手段确定请求语义实际上是幂等的(与方法无关),或有手段检测原始请求从未被应用,否则客户端 SHOULD NOT 自动重试非幂等方法的请求。

例如,如果用户代理通过设计或配置知道对该资源的 POST 请求是安全的,则可以自动重复该 POST 请求。同样,专门针对版本控制仓库设计的用户代理可能通过在失败连接后检查目标资源的修订、回退或修复任何部分应用的更改,然后自动重试失败的请求,从而从部分失败条件中恢复。

有些客户端采取更冒险的做法并尝试猜测何时可以自动重试。例如,如果底层传输连接在未接收任何响应部分之前关闭,客户端可能会自动重试 POST 请求,特别是在使用空闲的持久连接时。

代理 MUST NOT 自动重试非幂等请求。客户端 SHOULD NOT 自动重试已失败的自动重试。

9.2.3. 方法与缓存

要使缓存存储并使用一个响应,相关的方法需要明确允许缓存并详细说明在何种条件下响应可用于满足后续请求;未做此说明的方法定义则不能被缓存。有关附加要求,请参见 [CACHING]

本规范为 GET、HEAD 和 POST 定义了缓存语义,尽管绝大多数缓存实现仅支持 GET 和 HEAD。

9.3. 方法定义

9.3.1. GET

GET 方法请求传输目标资源的当前 所选表示。成功响应应反映目标 URI 所识别的“等同性”质量(参见 URI 第 1.2.2 节)。因此,通过 HTTP 检索可识别信息通常是通过对与该信息提供潜力相关的标识符发起 GET 请求来完成的(并期望得到 200 (OK) 响应)。

GET 是信息检索的主要机制,也是几乎所有性能优化的关注点。为每个重要资源生成 URI 的应用能够受益于这些优化,同时让其他应用重用这些 URI,从而产生促进 Web 扩展的网络效应。

将资源标识符视为远程文件系统路径名并将表示视为这些文件内容副本是很诱人的想法。事实上,许多资源确实如此实现(参见与文件和路径名相关的攻击安全考虑 第 17.3 节)。然而在实践中并无此类限制。

资源的 HTTP 接口同样可能实现为内容对象树、对各种数据库记录的程序化视图,或通向其他信息系统的网关。即便 URI 映射机制与文件系统相关联,源服务器也可能配置为在接收请求时执行这些文件并将其输出作为表示发送,而不是直接传输文件。无论如何,只有源服务器需要知道每个资源标识符如何对应于某个实现,以及该实现如何选择并发送目标资源的当前表示。

客户端可以通过在请求中发送 Range 头字段(第 14.2 节)将 GET 的语义更改为“范围请求”,以请求仅传输所选表示的部分内容。

尽管请求消息分帧与所使用的方法无关,但在 GET 请求中接收的内容没有通用定义的语义,不能改变请求的含义或目标,并且可能导致某些实现拒绝该请求并关闭连接,因为它可能被用于请求走私攻击(参见 HTTP/1.1 第 11.2 节)。客户端 SHOULD NOT 在 GET 请求中生成内容,除非该请求直接发向已事先明确(带内或带外)表明此类请求有意义并会被充分支持的源服务器。源服务器 SHOULD NOT 依赖私有协议来接收内容,因为 HTTP 通信的参与者通常并不了解沿请求链的中间件。

对 GET 请求的响应是可缓存的;缓存 MAY 将其用于满足后续的 GET 和 HEAD 请求,除非由 Cache-Control 头字段另有指示(参见 第 5.2 节)。

当使用从用户提供信息构造目标 URI 的机制执行信息检索时(例如使用 GET 的表单查询字段),可能会提供不应在 URI 中披露的敏感数据(见 第 17.9 节)。在某些情况下,可以对数据进行过滤或转换以避免泄露。在其他情况下,尤其当没有缓存响应的好处时,使用 POST 方法(第 9.3.3 节)而不是 GET,可将此类信息放在请求内容中而不是目标 URI 中传输。

9.3.3. POST

POST 方法请求目标资源根据该资源自身的特定语义处理包含在请求中的表示。例如,POST 用于下列功能(其中包括但不限于):

  • 向数据处理过程提供一块数据,例如在 HTML 表单中输入的字段;
  • 向公告板、新闻组、邮件列表、博客或类似的文章组发布消息;
  • 创建尚未由源服务器识别的新资源;以及
  • 向资源的现有表示追加数据。

源服务器通过根据处理 POST 请求的结果选择适当的状态码来指示响应语义;几乎本规范定义的所有状态码都可能在对 POST 的响应中出现(例外为 206 (Partial Content)304 (Not Modified)416 (Range Not Satisfiable))。

如果一个或多个资源在源服务器上因成功处理 POST 请求而被创建,源服务器 SHOULD 发送一个包含 201 (Created) 的响应,其中应包含提供主创建资源标识符的 Location 头字段(见 第 10.2.2 节),以及一个描述请求状态并引用新资源的表示。

只有当包含明确的新鲜度信息(参见 第 4.2.1 节)并且 Content-Location 头字段与 POST 的目标 URI 相同(参见 第 8.7 节)时,POST 的响应才是可缓存的。缓存的 POST 响应可用于满足后续的 GET 或 HEAD 请求。相反,POST 请求不能由缓存的 POST 响应来满足,因为 POST 可能不安全;参见 第 4 节

如果处理 POST 的结果等同于现有资源的某个表示,源服务器 MAY 通过发送带有现有资源标识符的 303 (See Other) 响应(并在 Location 字段中提供该标识符)将用户代理重定向到该资源。这样做的好处是为用户代理提供了一个资源标识符并通过更适合共享缓存的方法传输表示,尽管如果用户代理未缓存该表示,则增加一次请求成本。

9.3.4. PUT

PUT 方法请求用请求消息内容中包含的表示所定义的状态来创建或替换目标资源的状态。对于给定表示的成功 PUT,表明随后对相同目标资源的 GET 请求将返回等效的表示(并以 200 (OK) 响应)。然而,这并不保证这样的状态更改会被观察到,因为目标资源可能在随后 GET 到达之前被其他用户代理并行操作,或被源服务器进行动态处理。成功响应仅意味着在源服务器处理时用户代理的意图已被实现。

如果目标资源当前没有表示且 PUT 成功创建了一个,源服务器 MUST 通过发送 201 (Created) 响应通知用户代理。如果目标资源已有当前表示且该表示按所封装表示的状态成功修改,则源服务器 MUST 发送 200 (OK)204 (No Content) 响应以指示请求成功完成。

源服务器 SHOULD 验证 PUT 表示是否与目标资源的配置约束一致。例如,如果源服务器根据 URI 确定资源的表示元数据,则源服务器需要确保在成功的 PUT 请求中接收的内容与该元数据一致。当 PUT 表示与目标资源不一致时,源服务器 SHOULD 通过转换表示或更改资源配置使其一致,或返回包含足够信息以解释为何该表示不合适的适当错误消息。建议使用 409 (Conflict)415 (Unsupported Media Type) 状态码,后者专用于对 Content-Type 值的限制。

例如,如果目标资源被配置为始终具有 "text/html" 的 Content-Type,而被 PUT 的表示的 Content-Type 为 "image/jpeg",则源服务器应采取下列之一:

  1. 重新配置目标资源以反映新的媒体类型;
  2. 在保存为新的资源状态之前将 PUT 表示转换为与资源一致的格式;或,
  3. 415 (Unsupported Media Type) 响应拒绝请求,表明目标资源仅限于 "text/html",并可能包含指向适于新表示的其他资源的链接。

HTTP 并未精确定义 PUT 方法如何影响源服务器状态,超出用户代理请求意图和源服务器响应语义所能表达的范围。它不定义资源在任何意义上的具体含义,也不定义资源状态如何“存储”、这种存储如何因状态更改而改变或源服务器如何将资源状态翻译为表示。一般而言,服务器隐藏了资源接口背后的所有实现细节。

这同样适用于头字段和尾字段的存储处理;虽然像 Content-Type 这样的常见头字段通常会被存储并在随后的 GET 请求中返回,但头字段和尾字段的处理取决于接收请求的资源。因此,源服务器 SHOULD 忽略在 PUT 请求中接收到的未识别头字段和尾字段(即不将其作为资源状态的一部分保存)。

源服务器 MUST NOT 在对 PUT 的成功响应中发送验证器字段(见 第 8.8 节),例如 ETagLast-Modified,除非请求的表示数据在保存时未对内容进行任何转换(即资源的新表示数据与 PUT 请求接收的内容相同),且验证器字段值反映了该新表示。此要求允许用户代理在发送并保留在内存中的表示即为 PUT 的结果时得知不必再次从源服务器检索表示。响应中收到的新验证器可用于未来的条件请求以防止意外覆盖(见 第 13.1 节)。

POST 与 PUT 方法之间的根本区别在于对所封装表示的不同意图:POST 的目标资源旨在根据资源自身的语义处理封装表示,而 PUT 请求中封装的表示被定义为替换目标资源的状态。因此,PUT 的意图是幂等的并对中间件可见,尽管确切效果仅由源服务器知晓。

对 PUT 请求的正确解释假设用户代理知道期望的目标资源。若服务在接收状态更改请求后代客户选择合适的 URI,则应使用 POST 而非 PUT 来实现。如果源服务器不会对目标资源执行请求的 PUT 状态更改而希望将其应用到另一个资源(例如当资源已被移到不同 URI 时),则源服务器 MUST 发送适当的 3xx (Redirection) 响应;用户代理 MAY 自行决定是否重定向请求。

对目标资源应用的 PUT 请求可能对其他资源产生副作用。例如,文章可能有一个用于标识“当前版本”的 URI(一个资源),它与识别每个特定版本的 URI(不同资源)是分离的。对“当前版本” URI 的成功 PUT 请求可能因此创建一个新的版本资源,除了更改目标资源的状态外,还可能在相关资源之间添加链接。

一些源服务器支持将 Content-Range 头字段(见 第 14.4 节)用作请求修改器来执行部分 PUT,如 第 14.5 节 所述。

对 PUT 方法的响应不可缓存。如果成功的 PUT 请求通过了具有目标 URI 的一个或多个已存储响应的缓存,则这些已存储的响应将被失效(参见 第 4.4 节)。

9.3.5. DELETE

DELETE 方法请求源服务器移除目标资源与其当前功能之间的关联。实际上,该方法类似于 UNIX 中的 "rm" 命令:它表示对源服务器的 URI 映射执行删除操作,而不是期望之前关联的信息被物理删除。

如果目标资源具有一个或多个当前表示,这些表示可能会也可能不会被源服务器销毁,相关存储也可能会也可能不会被回收,这完全取决于资源的性质及其由源服务器的实现(超出本规范范围)。同样,资源的其他实现方面可能需要在 DELETE 后被停用或存档,例如数据库或网关连接。一般假设源服务器仅在其具有实现删除机制的资源上允许 DELETE。

允许 DELETE 的资源相对较少——其主要用途是远程作者环境,在那里用户对其效果有一定指示。例如,先前通过 PUT 创建或在对 POST 的 201 (Created) 响应后的 Location 头字段所标识的资源,可能允许相应的 DELETE 请求撤销那些操作。同样,实现作者功能的自定义用户代理(如使用 HTTP 执行远程操作的修订控制客户端)可能在假设服务器的 URI 空间已被设计为对应版本仓库时使用 DELETE。

如果成功应用 DELETE 方法,源服务器 SHOULD 发送:

  • 如果该操作可能会成功但尚未生效,则发送 202 (Accepted)
  • 如果该操作已生效且不需提供进一步信息,则发送 204 (No Content);或
  • 如果该操作已生效且响应消息包含描述状态的表示,则发送 200 (OK)

尽管请求消息分帧与所用方法独立,但在 DELETE 请求中接收的内容没有通用定义的语义,不能改变请求的含义或目标,并且可能导致某些实现因其作为请求走私攻击的潜在用途而拒绝请求并关闭连接(参见 第 11.2 节)。客户端 SHOULD NOT 在 DELETE 请求中生成内容,除非该请求直接发向已事先明确表明此类请求有意义并会被充分支持的源服务器。源服务器 SHOULD NOT 依赖私有协议来接收内容。

对 DELETE 方法的响应不可缓存。如果成功的 DELETE 请求通过了具有目标 URI 的一个或多个已存储响应的缓存,则这些已存储的响应将被失效(参见 第 4.4 节)。

9.3.6. CONNECT

CONNECT 方法请求接收者为请求目标所标识的目的地源服务器建立隧道,如果成功,随后将其行为限制为盲转发数据(双向),直到隧道关闭为止。隧道通常用于通过一个或多个代理创建端到端的虚拟连接,然后可以使用 TLS(传输层安全,参见 TLS13)对其进行保护。

CONNECT 使用一种对该方法唯一的特殊请求目标形式,仅由隧道目的地的主机和端口号组成,两者以冒号分隔。没有默认端口;客户端 MUST 发送端口号,即便 CONNECT 请求基于包含省略端口的 authority 分量的 URI 引用(参见 第 4.1 节)。例如:

CONNECT server.example.com:80 HTTP/1.1
Host: server.example.com

服务器 MUST 拒绝目标为空或无效端口号的 CONNECT 请求,通常以 400 (Bad Request) 状态码响应。

由于 CONNECT 会改变 HTTP 连接的请求/响应性质,具体 HTTP 版本可能对其语义到协议线格式的映射方式不同。

CONNECT 旨在用于对代理的请求。接收者可以通过直接连接到请求目标所标识的服务器来建立隧道,或在配置为使用另一个代理时将 CONNECT 请求转发给下一个入站代理。源服务器 MAY 接受 CONNECT 请求,但大多数源服务器并不实现 CONNECT。

任何 2xx (Successful) 响应表明发送方(以及所有入站代理)将在响应头部部分之后立即切换到隧道模式;在该头部部分之后接收的数据即来自请求目标所标识的服务器。任何非成功响应表明隧道尚未形成。

当隧道中间件检测到任一侧关闭其连接时,隧道即被关闭:中间件 MUST 尝试将来自已关闭一侧的任何未发送数据发送到另一侧,关闭两个连接,然后丢弃任何剩余未交付的数据。

可能使用代理认证来建立创建隧道的权限。例如:

CONNECT server.example.com:443 HTTP/1.1
Host: server.example.com:443
Proxy-Authorization: basic aGVsbG86d29ybGQ=

对任意服务器建立隧道存在重大风险,尤其当目的地为并非为 Web 流量设计的知名或保留 TCP 端口时。例如,CONNECT 到 "example.com:25" 会暗示代理连接到用于 SMTP 流量的保留端口;若允许,可能会欺骗代理中继垃圾邮件。支持 CONNECT 的代理 SHOULD 将其使用限制在已知端口的有限集合或可配置的安全目标列表中。

服务器 MUST NOT 在对 CONNECT 的 2xx (Successful) 响应中发送任何 Transfer-EncodingContent-Length 头字段。客户端 MUST 忽略在对 CONNECT 的成功响应中接收的任何 Content-Length 或 Transfer-Encoding 头字段。

CONNECT 请求消息没有内容。CONNECT 请求消息头部部分之后发送的数据的解释取决于所使用的 HTTP 版本。

对 CONNECT 方法的响应不可缓存。

9.3.7. OPTIONS

OPTIONS 方法请求有关目标资源在源服务器或中间代理处可用的通信选项的信息。该方法允许客户端确定与资源相关的选项和/或要求,或服务器的能力,而不暗含对资源执行操作。

以星号 ("*") 作为请求目标的 OPTIONS 请求(参见 第 7.1 节)适用于服务器整体而非特定资源。由于服务器的通信选项通常取决于资源,“*” 请求仅作为一种“ping”或“无操作”类型的方法有用;它仅允许客户端测试服务器的能力,例如测试代理对 HTTP/1.1 的兼容性(或不兼容性)。

如果请求目标不是星号,则 OPTIONS 请求适用于与目标资源通信时可用的选项。

生成 OPTIONS 成功响应的服务器 SHOULD 发送任何可能指示服务器实现的并适用于目标资源的可选功能的头字段(例如 Allow),包括本规范未定义的潜在扩展。响应内容(如果有)也可用机器或人可读的表示描述通信选项。本规范并未定义此类表示的标准格式,但未来的 HTTP 扩展可能会定义。

客户端 MAY 在 OPTIONS 请求中发送 Max-Forwards 头字段以定位请求链中的特定接收者(参见 第 7.6.2 节)。代理 MUST NOT 在转发请求时生成 Max-Forwards 头字段,除非该请求在接收时已包含 Max-Forwards 字段。

在 OPTIONS 请求中包含内容的客户端 MUST 发送有效的 Content-Type 头字段以描述所表示的媒体类型。注意,本规范并未为此类内容定义任何用途。

对 OPTIONS 方法的响应不可缓存。

9.3.8. TRACE

TRACE 方法请求对请求消息进行远程应用层回环。请求的最终接收者 SHOULD 将其接收到的消息(排除下面描述的某些字段)反射回客户端,作为 200 (OK) 响应的内容。使用 "message/http" 格式(见 第 10.1 节)是一种实现方式。请求的最终接收者要么是源服务器,要么是请求中 Max-Forwards 值为零 (0) 的第一个服务器(见 第 7.6.2 节)。

客户端 MUST NOT 在 TRACE 请求中生成可能被响应泄露的包含敏感数据的字段。例如,用户代理不应在 TRACE 请求中发送存储的用户凭证(见 第 11 节)或 Cookie(参见 COOKIE)。请求的最终接收者在生成响应内容时 SHOULD 排除任何可能包含敏感数据的请求字段。

TRACE 允许客户端查看在请求链另一端接收到的内容并将其用于测试或诊断信息。Via 头字段的值尤其值得关注,因为它作为请求链的跟踪。使用 Max-Forwards 头字段允许客户端限制请求链的长度,这对于测试可能在循环中转发消息的一系列代理非常有用。

客户端 MUST NOT 在 TRACE 请求中发送内容。

对 TRACE 方法的响应不可缓存。

10. 消息上下文

10.1. 请求上下文字段

下面的请求头字段提供有关请求上下文的附加信息,包括关于用户、用户代理和请求所指资源的信息。

10.1.1. Expect

请求中的 "Expect" 头字段指示一组行为(期望),服务器必须支持这些行为以便正确处理该请求。

Expect 字段值不区分大小写。

本规范定义的唯一期望是 "100-continue"(无定义参数)。

收到包含除 100-continue 之外成员的 Expect 字段值的服务器 MAY 使用 417 (Expectation Failed) 状态码响应,以表示无法满足该意外期望。

100-continue 期望告知接收方客户端即将发送(通常是较大)内容,并希望在方法、目标 URI 和头字段不足以导致立即的成功、重定向或错误响应时接收一个 100 (Continue) 中间响应。这允许客户端在真正发送大量数据之前先等待是否值得发送,有助于在数据巨大或客户端预期可能发生错误时提高效率(例如在首次发送状态改变的方法但尚未验证认证凭据时)。

例如,下述以...

PUT /somewhere/fun HTTP/1.1
Host: origin.example.com
Content-Type: video/h264
Content-Length: 1234567890987
Expect: 100-continue

允许源服务器在客户端开始耗费带宽传输大量数据之前立即返回错误消息,例如 401 (Unauthorized)405 (Method Not Allowed)

对客户端的要求:

  • 客户端在请求不包含内容时 MUST NOT 生成 100-continue 期望。
  • 将会在发送请求内容之前等待 100 (Continue) 响应的客户端 MUST 发送包含 100-continue 期望的 Expect 头字段。
  • 发送 100-continue 期望的客户端并不要求等待特定的时间;这样的客户端 MAY 即使尚未收到响应也继续发送内容。此外,由于 100 (Continue) 响应无法通过 HTTP/1.0 中间件发送,客户端 SHOULD NOT 在发送内容之前无限期等待。
  • 在收到对包含 100-continue 期望的请求的响应为 417 (Expectation Failed) 时,客户端 SHOULD 重复该请求但不带 100-continue 期望,因为 417 响应仅表示响应链不支持期望(例如它通过了一个 HTTP/1.0 服务器)。

对服务器的要求:

  • 在收到 HTTP/1.0 请求中的 100-continue 期望时,服务器 MUST 忽略该期望。
  • 服务器 MAY 在已接收部分或全部对应请求的内容时,或帧定界表明没有内容时,省略发送 100 (Continue) 响应。
  • 发送 100 (Continue) 响应的服务器 MUST 在接收并处理请求内容后最终发送一个最终状态码,除非连接被提前关闭。
  • 在在读取完整请求内容之前就返回最终状态码的服务器 SHOULD 指示它是否打算关闭连接(例如参见 HTTP/1.1 第 9.6 节),或继续读取请求内容。

在接收到一个 HTTP/1.1(或更高)请求,该请求的方法、目标 URI 和完整头部部分包含 100-continue 期望且表明后续将有请求内容时,源服务器 MUST 发送以下之一:

  • 如果仅通过检查方法、目标 URI 与头字段即可确定最终状态,则立即发送该最终状态码的响应,或
  • 发送一个立即的 100 (Continue) 响应以鼓励客户端发送请求内容。

源服务器 MUST NOT 在发送 100 (Continue) 响应之前等待内容。

在接收到一个 HTTP/1.1(或更高)请求,该请求的方法、目标 URI 和完整头部部分包含 100-continue 期望且表明后续将有请求内容时,代理 MUST 执行下列之一:

  • 如果仅通过检查方法、目标 URI 与头字段即可确定最终状态,则发送一个立即的最终状态码响应,或
  • 通过向下一个入站服务器发送相应的请求行和头部部分将请求转发给源服务器。

如果代理基于配置或过往交互认为下一个入站服务器仅支持 HTTP/1.0,则代理 MAY 生成一个立即的 100 (Continue) 响应以鼓励客户端开始发送内容。

10.1.2. From

“From” 头字段包含一个互联网电子邮件地址,表示控制发起请求的用户代理的人类用户。该地址应为机器可用格式,如 RFC5322 第 3.4 节 中定义的 "mailbox":

  From    = mailbox

  mailbox = <mailbox, see [RFC5322], Section 3.4>

示例:

From: spider-admin@example.org

非机器人用户代理很少发送 From 头字段。用户代理在未获得用户明确配置时 SHOULD NOT 发送 From 头字段,因为这可能与用户的隐私利益或其站点的安全策略相冲突。

机器人用户代理 SHOULD 发送有效的 From 头字段,以便在服务器出现问题(例如机器人发送过多、不受欢迎或无效请求时)时能联系到负责运行该机器人的人。

服务器 SHOULD NOT 将 From 头字段用于访问控制或认证,因为其值通常对接收或观察请求的任何人可见,并且常在日志文件和错误报告中记录,无法期望其具有隐私性。

10.1.3. Referer

“Referer”(拼写有误,原意为 "Referrer")头字段允许用户代理指定一个 URI 引用,表示从该资源获取了 目标 URI(即“引用者”)。用户代理 MUST NOT 在生成 Referer 字段值时包含 URI 引用的 fragment 和 userinfo 分量(如有)。

字段值要么为 absolute-URI,要么为 partial-URI。在后者情况下,所引用的 URI 相对于目标 URI(参见第 4 节)。

Referer 头字段允许服务器生成指向其他资源的回溯链接,用于简单分析、日志、优化缓存等。它也有助于发现过时或拼写错误的链接以便维护。一些服务器使用 Referer 头字段作为拒绝来自其他站点链接(所谓“深度链接”)或限制跨站请求伪造(CSRF)的一种手段,但并非所有请求都包含该字段。

示例:

Referer: http://www.example.org/hypertext/Overview.html

如果目标 URI 是从没有自己 URI 的来源(例如用户键盘输入或用户书签/收藏中的条目)获得的,用户代理 MUST 要么省略 Referer 头字段,要么以 "about:blank" 作为其值发送。

Referer 字段值不必传达完整的引用资源 URI;用户代理 MAY 截断除了引用源(origin)之外的部分。

Referer 头字段可能泄露有关请求上下文或用户浏览历史的信息,如果引用资源标识符暴露个人信息(如账户名)或属于应保密的资源(如防火墙后或受保护服务内部),则构成隐私顾虑。大多数通用用户代理在引用资源为本地 "file" 或 "data" URI 时不会发送 Referer。用户代理 SHOULD NOT 在引用资源使用安全协议且目标请求的 origin 与引用资源不同的情况下发送 Referer,除非引用资源明确允许发送 Referer。用户代理 MUST NOT 在不安全的 HTTP 请求中发送 Referer,如果引用资源是通过安全协议访问的。有关更多安全考虑,请参见第 17.9 节。

一些中间件已知会不加区分地从外发请求中移除 Referer 头字段。这会不幸地干扰对 CSRF 攻击的防护,而 CSRF 对用户可能造成更大的危害。希望限制 Referer 信息泄露的中间件和用户代理扩展应仅限于特定编辑,例如将内部域名替换为伪名或截断查询和/或路径分量。中间件 SHOULD NOT 在字段值与目标 URI 具有相同方案和主机时修改或删除 Referer 头字段。

10.1.4. TE

“TE” 头字段描述客户端关于传输编码和尾部部分的能力。

如在 第 6.5 节 所述,请求中包含有 "trailers" 成员的 TE 字段表示客户端不会丢弃尾字段。

TE 也用于在 HTTP/1.1 中告知服务器客户端能够接受响应中哪些传输编码。截至发布时,仅 HTTP/1.1 使用传输编码(参见 HTTP/1.1 第 7 节)。

TE 字段值为成员列表,除了 "trailers" 之外的每个成员由传输编码名称的 token 组成,可选地带有指示客户端对该传输编码相对偏好的权重(参见 第 12.4.2 节)以及该传输编码的可选参数。

TE 的发送方 MUST 还在 Connection 头字段中发送一个 "TE" 连接选项(见 第 7.6.1 节),以告知中间件不要转发该字段。

10.1.5. User-Agent

“User-Agent” 头字段包含关于发起请求的用户代理的信息,服务器常用它来帮助识别互操作性问题的范围、为规避特定用户代理的限制而调整响应、以及进行有关浏览器或操作系统使用情况的分析。除非明确配置为不发送,否则用户代理 SHOULD 在每个请求中发送 User-Agent 头字段。

User-Agent 字段值由一个或多个 product 标识符组成,每个后跟零个或多个注释(参见 第 5.6.5 节),共同标识用户代理软件及其重要的子产品。按惯例,product 标识符按其标识用户代理软件的重要性递减的顺序列出。每个 product 标识符由名称和可选版本组成。

发送方 SHOULD 将生成的 product 标识符限制为识别产品所必需的信息;发送方 MUST NOT 在 product 标识符中生成广告或其他非必要信息。发送方 SHOULD NOTproduct-version 中生成非版本标识的信息(即同一产品名的连续版本应仅在 product-version 部分有所不同)。

示例:

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

用户代理 SHOULD NOT 生成包含不必要细粒度细节的 User-Agent 头字段,并且 SHOULD 限制第三方添加子产品。过长且过于详细的 User-Agent 字段值会增加请求延迟并提高用户被识别的风险(“指纹识别”)。

同样,鼓励实现不要使用其他实现的 product token 来声明与其兼容,因为这会规避该字段的目的。如果用户代理伪装为另一个用户代理,接收方可以假定用户有意希望看到为该被标识用户代理定制的响应,即便这些响应对于实际使用的用户代理可能并不完全合适。

10.2. 响应上下文字段

下面的响应头字段提供关于响应的附加信息,超出状态码所暗示的内容,包括有关服务器、目标资源或相关资源的信息。

10.2.1. Allow

“Allow” 头字段列出目标资源宣称支持的方法集合。此字段的目的仅仅是告知接收方与该资源相关的有效请求方法。

使用示例:

Allow: GET, HEAD, PUT

允许的方法的实际集合由源服务器在每次请求时定义。源服务器 MUST405 (Method Not Allowed) 响应中生成 Allow 头字段,并且 MAY 在任何其他响应中生成。Allow 字段值为空表示该资源不允许任何方法,这可能在 405 响应中发生,例如资源被配置暂时禁用时。

代理 MUST NOT 修改 Allow 头字段——它不需要理解所有指示的方法即可根据通用消息处理规则来处理这些方法。

10.2.2. Location

“Location” 头字段在某些响应中用于引用与该响应相关的特定资源。该关系的类型由请求方法和状态码语义的组合确定。

字段值由单个 URI-reference 组成。当其为相对引用形式时,最终值通过将其解析相对于目标 URI 来计算(参见 URI 规范)。

对于 201 (Created) 响应,Location 值指向请求所创建的主资源。对于 3xx (Redirection) 响应,Location 值指向用于自动重定向请求的首选目标资源。

如果 3xx 响应中提供的 Location 值不包含 fragment 分量,用户代理 MUST 将重定向视为如果该值继承用于生成目标 URI 的 URI 引用的 fragment(即重定向继承原始引用的 fragment(若有))。

例如,对 URI 引用 "http://www.example.org/~tim" 发起的 GET 请求可能产生包含如下头字段的 303 (See Other) 响应:

Location: /People.html#tim

这表明用户代理应重定向到 "http://www.example.org/People.html#tim"

同样,对 URI 引用 "http://www.example.org/index.html#larry" 发起的 GET 请求可能产生包含如下头字段的 301 (Moved Permanently) 响应:

Location: http://www.example.net/index.html

这表明用户代理应重定向到 "http://www.example.net/index.html#larry",保留原始的 fragment 标识符。

在某些情况下,Location 值中包含 fragment 标识符并不合适。例如,201 (Created) 响应中的 Location 头字段应提供一个特定于所创建资源的 URI。

10.2.3. Retry-After

服务器发送 "Retry-After" 头字段以指示用户代理在发起后续请求之前应等待多长时间。当与 503 (Service Unavailable) 响应一起发送时,Retry-After 指示服务预计对客户端不可用的时长。当与任何 3xx (Redirection) 响应一起发送时,Retry-After 指示要求用户代理在发起被重定向请求之前至少等待的时间。

Retry-After 字段值可以是 HTTP-date 或表示在接收响应后应延迟的秒数。

delay-seconds 值为非负十进制整数,表示秒数。

两个使用示例:

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

在后一个示例中,延迟为 2 分钟。

10.2.4. Server

“Server” 头字段包含源服务器用于处理请求的软件信息,客户端通常用它来帮助识别互操作性问题的范围、在发送请求时规避或针对特定服务器限制进行调整,并用于关于服务器或操作系统使用情况的分析。源服务器 MAY 在其响应中生成 Server 头字段。

Server 字段值由一个或多个 product 标识符组成,每个后跟零个或多个注释(参见 第 5.6.5 节),共同标识源服务器软件及其重要子产品。按惯例,product 标识符按其在识别源服务器软件方面的重要性递减的顺序列出。每个 product 标识符由名称和可选版本组成,如第 10.1.5 节所定义。

示例:

Server: CERN/3.0 libwww/2.17

源服务器 SHOULD NOT 生成包含不必要细粒度细节的 Server 头字段值,并且 SHOULD 限制第三方添加子产品。过长且过于详细的 Server 字段值会增加响应延迟并可能泄露内部实现细节,从而(略微)使攻击者更容易发现并利用已知安全漏洞。

11. HTTP 认证

11.1. 认证方案

HTTP 提供了一个通用的访问控制和认证框架,采用可扩展的一组质询-响应认证方案,服务器可以使用这些方案对客户端请求发起质询,客户端可以用它们来提供认证信息。使用不区分大小写的 token 来标识认证方案:

  auth-scheme    = token

除了通用框架外,本文件不对任何认证方案作出具体规范。新的和现有的认证方案独立规范并应在 “Hypertext Transfer Protocol (HTTP) Authentication Scheme Registry” 中注册。例如,"basic" 和 "digest" 认证方案分别由 [RFC7617][RFC7616] 定义。

11.2. 认证参数

认证方案后面跟随为通过该方案实现认证所必需的附加信息,这些信息可以是以逗号分隔的参数列表,或是一段可承载 base64 编码信息的字符序列。

  token68        = 1*( ALPHA / DIGIT /
                       "-" / "." / "_" / "~" / "+" / "/" ) *"="

token68 语法允许 66 个未保留的 URI 字符(参见 [URI])加上一些其他字符,因此它可以承载 base64、base64url(URL 与文件名安全字母表)、base32 或 base16(十六进制)编码,带或不带填充,但不包括空白字符(参见 [RFC4648])。

认证参数为名称/值对,其中名称 token 匹配不区分大小写,并且每个参数名 MUST 在每个质询中仅出现一次。

  auth-param     = token BWS "=" BWS ( token / quoted-string )

参数值可以用 "token" 或 "quoted-string" 表示(见 第 5.6 节)。认证方案的定义需要接受这两种表示法,以便发送方和接收方都能使用通用的解析组件,而不依赖于具体方案。

为向后兼容,认证方案的定义可以限制发送方仅使用两种格式中的一种。这在已知已部署实现遇到其中一种格式会失败的情况下非常重要。

11.3. 质询与响应

401 (Unauthorized) 响应消息由源服务器用于对用户代理的授权发起质询,响应中包含至少一个适用于所请求资源的 WWW-Authenticate 头字段。

407 (Proxy Authentication Required) 响应消息由代理用于对客户端的授权发起质询,响应中包含至少一个适用于所请求资源的代理的 Proxy-Authenticate 头字段。

希望向源服务器进行认证的用户代理 —— 通常(但不必然)在接收 401 (Unauthorized) 之后 —— 可以在请求中包含 Authorization 头字段来进行认证。

希望向代理进行认证的客户端 —— 通常(但不必然)在接收 407 (Proxy Authentication Required) 之后 —— 可以在请求中包含 Proxy-Authorization 头字段来进行认证。

11.4. 凭证

Authorization 字段值和 Proxy-Authorization 字段值都包含客户端针对所请求资源的领域(realm)而基于以前收到的质询(可能是过去的某个时间点)生成的凭证。在构造这些字段值时,用户代理应选择其所理解的最安全的 auth-scheme,并在适当情况下向用户获取凭证。凭证在头字段中传输时涉及重要的安全考虑,尤其是关于底层连接的机密性,详见第 17.16.1 节

在收到缺少凭证、包含无效凭证(例如密码错误)或包含部分凭证(例如认证方案需要多次往返)的请求时,源服务器 SHOULD 返回一个包含至少一个适用于所请求资源的新(或已有)质询的 401 (Unauthorized) 响应,其中包含 WWW-Authenticate 头字段。

同样地,在收到缺少代理凭证或包含无效/部分代理凭证的请求时,要求认证的代理 SHOULD 生成一个包含至少一个适用于该代理的质询的 407 (Proxy Authentication Required) 响应,其中包含 Proxy-Authenticate 头字段。

接收到了有效但不足以获取访问权限的凭证的服务器应以 403 (Forbidden) 状态码响应(参见 第 15.5.4 节)。

HTTP 并不将应用程序限于此简单的质询-响应认证框架。可以使用额外的机制,例如在传输层进行认证或通过消息封装进行认证,并使用额外的头字段指定认证信息。但这些额外机制不在本规范中定义。

注意:各种自定义的用户认证机制使用 Set-Cookie 与 Cookie 头字段来传递与认证相关的令牌。

11.5. 建立保护空间(Realm)

realm 认证参数保留给希望指示保护范围的认证方案使用。

保护空间(protection space) 由被访问服务器的 origin(参见 第 4.3.1 节)与可选的 realm 值共同定义。这些 realm 允许服务器上的受保护资源被划分为若干保护空间,每个保护空间有其自己的认证方案和/或授权数据库。realm 值为一个字符串,通常由源服务器分配,并且可能具有与认证方案相关的额外语义。注意,一个响应可以包含相同 auth-scheme 但 realm 不同的多个质询。

保护空间决定了凭证可以自动应用的域范围。如果先前的请求已被授权,用户代理 MAY 在该保护空间内的其他请求中重用相同的凭证,重用期限由认证方案、参数和/或用户偏好(例如可配置的不活动超时)决定。

保护空间的范围,因此凭证可能被自动应用的请求范围,并不一定在客户端已知,除非有额外信息。认证方案可定义描述保护空间范围的参数。除非认证方案明确允许,单一保护空间不能超出其服务器的范围。

出于历史原因,发送方 MUST 仅生成 quoted-string 语法。接收方可能需要同时支持 token 与 quoted-string 语法以与长期接受两者表示的现有客户端最大限度互操作。

11.6. 向源服务器对用户进行认证

11.6.1. WWW-Authenticate

"WWW-Authenticate" 响应头字段指示适用于目标资源的认证方案及其参数。

生成 401 (Unauthorized) 响应的服务器 MUST 发送一个包含至少一个质询的 WWW-Authenticate 头字段。服务器 MAY 在其他响应消息中生成 WWW-Authenticate 头字段以指示提供凭证(或不同的凭证)可能会影响响应。

转发响应的代理 MUST NOT 修改响应中的任何 WWW-Authenticate 头字段。

建议用户代理在解析该字段值时格外小心,因为它可能包含多个质询,并且每个质询可以包含以逗号分隔的认证参数列表。此外,该头字段本身也可能出现多次。

例如:

WWW-Authenticate: Basic realm="simple", Newauth realm="apps",
                 type=1, title="Login to \"apps\""

该头字段包含两个质询:一个是 "Basic" 方案并带有 realm="simple",另一个是 "Newauth" 方案并带有 realm="apps"。它还包含两个额外参数 "type" 和 "title"。

不过,一些用户代理不识别这种形式。因此,在同一字段行上发送包含多个成员的 WWW-Authenticate 字段值可能不具互操作性。

11.6.2. Authorization

"Authorization" 头字段允许用户代理向源服务器进行自我认证 —— 通常(但不必然)在收到 401 (Unauthorized) 响应之后。其值由包含用户代理在请求所针对领域内的认证信息的凭证组成。

如果请求已通过认证并指定了 realm,则假定相同凭证对该 realm 内的所有其他请求有效(前提是认证方案本身没有另行要求,例如凭证随挑战值变化或需要同步时钟的情形)。

转发请求的代理 MUST NOT 修改该请求中的任何 Authorization 头字段。有关 HTTP 缓存处理 Authorization 头字段的详细要求,请参见 CACHING 第 3.5 节

11.6.3. Authentication-Info

HTTP 认证方案可以使用 "Authentication-Info" 响应字段在客户端凭证被接受之后传达信息。该信息可以包含来自服务器的最终消息(例如,它可以包含服务器的认证信息)。

字段值为参数列表(名称/值对),使用在 第 11.3 节 中定义的 "auth-param" 语法。本规范仅描述通用格式;使用 Authentication-Info 的认证方案将定义各自的参数。例如,Digest 认证方案在 RFC7616 第 3.5 节 中定义了多个参数。

Authentication-Info 字段可用于任何 HTTP 响应,与请求方法和状态码无关。其语义由与对应请求相关的 Authorization 头字段所指示的认证方案定义(参见 第 11.6.2 节)。

转发响应的代理不得以任何方式修改该字段值。

当认证方案明确允许时,Authentication-Info 可以作为尾部字段发送(参见 第 6.5 节)。

11.7. 向代理对客户端进行认证

11.7.1. Proxy-Authenticate

"Proxy-Authenticate" 头字段由至少一个质询组成,指示适用于本次请求的代理的认证方案和参数。代理在生成的每个 407 (Proxy Authentication Required) 响应中 MUST 发送至少一个 Proxy-Authenticate 头字段。

WWW-Authenticate 不同,Proxy-Authenticate 头字段仅适用于响应链中下一个出站客户端。这是因为只有选择了给定代理的客户端最可能拥有进行认证所需的凭证。然而,当多个代理位于同一管理域内(例如大型公司网络中的办公与区域缓存代理)时,通常会由用户代理生成凭证并在层级中传递直到被消费。因此,在这种配置下,看起来 Proxy-Authenticate 被转发,因为每个代理都会发送相同的质询集合。

注意:对 WWW-Authenticate 的解析注意事项同样适用于此头字段;详见 第 11.6.1 节

11.7.2. Proxy-Authorization

"Proxy-Authorization" 头字段允许客户端向要求认证的代理标识其自身(或其用户)。其值由包含针对代理和/或所请求资源的领域的客户端认证信息的凭证组成。

Authorization 不同,Proxy-Authorization 头字段仅适用于期待由 Proxy-Authenticate 头字段要求凭证的下一个入站代理。当链中使用多个代理时,Proxy-Authorization 头字段会被第一个期待接收凭证的入站代理消费。代理 MAY 将客户端请求中的凭证中继到下一个代理,如果这是代理之间协同认证给定请求的机制。

11.7.3. Proxy-Authentication-Info

"Proxy-Authentication-Info" 响应头字段等同于 Authentication-Info,但它应用于代理认证(参见 第 11.3 节),其语义由对应请求中的 Proxy-Authorization 头字段所指示的认证方案定义:

然而,与 Authentication-Info 不同,Proxy-Authentication-Info 头字段仅适用于响应链中的下一个出站客户端。这是因为只有选择了给定代理的客户端最可能拥有进行认证所需的凭证。然而,当多个代理位于同一管理域内(例如大型公司网络中的办公与区域缓存代理)时,通常会由用户代理生成凭证并在层级中传递直到被消费。因此,在这种配置下,看起来 Proxy-Authentication-Info 被转发,因为每个代理都会发送相同的字段值。

当认证方案明确允许时,Proxy-Authentication-Info 可以作为尾部字段发送(参见 第 6.5 节)。

12. 内容协商

当响应传递内容时,无论表示成功还是错误,源服务器通常有不同的方式来表示该信息;例如,不同的格式、语言或编码。同样,不同的用户或用户代理可能具有不同的能力、特性或偏好,这些都会影响在可用表示中哪一种最适合交付。因此,HTTP 提供了用于内容协商的机制。

本规范定义了三种可以在协议中可见的内容协商模式:“主动”协商,即服务器基于用户代理声明的偏好选择表示;“被动”协商,即服务器提供一个表示列表供用户代理选择;以及“请求内容”协商,即用户代理基于服务器在以往响应中声明的偏好为将来的请求选择表示。

其他的内容协商模式包括“条件内容”,其中表示由多个部分组成并根据用户代理参数选择性呈现;“主动内容”,其中表示包含一个脚本,该脚本会基于用户代理特性发出额外(更具体)的请求;以及“透明内容协商”([RFC2295]),其中内容选择由中间件执行。这些模式并非互斥,每种模式在适用性和可行性方面各有权衡。

请注意,在所有情况下,HTTP 并不了解资源的语义。源服务器随时间以及在内容协商的不同维度上对请求的响应一致性,因而观察到的资源表示随时间的“等同性”,完全由选择或生成这些响应的实体或算法决定。

12.1. 主动协商

当用户代理在请求中发送内容协商偏好以促使位于服务器端的算法选择首选表示时,这称为主动协商(又称 服务器驱动协商)。选择基于响应的可用表示(可能变化的维度,例如语言、内容编码等)与请求中提供的各种信息进行比较,包括下面定义的显式协商头字段和隐含特性,例如客户端的网络地址或 User-Agent 字段的部分内容。

当用于从可用表示中选择的算法难以向用户代理描述,或者当服务器希望在首个响应中就向用户代理发送其“最佳猜测”时,主动协商很有优势(当该“最佳猜测”对用户足够好时,可以避免随后请求的往返延迟)。为改进服务器的猜测,用户代理 MAY 发送描述其偏好的请求头字段。

主动协商有严重的缺点:

  • 服务器不可能准确判断对任一特定用户何者为“最佳”,因为那需要完全了解用户代理的能力以及响应的预期用途(例如用户是希望在屏幕上查看还是打印到纸上?);
  • 让用户代理在每次请求中描述其能力既可能非常低效(考虑到只有很小比例的响应具有多重表示),又可能对用户隐私构成风险;
  • 它使得源服务器的实现和生成响应的算法变得更复杂;以及
  • 它限制了针对共享缓存的响应重用。

用户代理不能依赖主动协商偏好会被一致地尊重,因为源服务器可能不为所请求资源实现主动协商,或者可能认为发送不符合用户代理偏好的响应比发送 406 (Not Acceptable) 响应更好。

在受主动协商影响的响应中,通常会发送一个 Vary 头字段(见 第 12.5.5 节)以指示在选择算法中使用了请求信息的哪些部分。

请求头字段 Accept、Accept-Charset、Accept-Encoding 和 Accept-Language 在下文为用户代理参与对响应内容的主动协商而定义。这些字段中发送的偏好适用于响应中的任何内容,包括目标资源的表示、错误或处理状态的表示,以及协议中可能出现的杂项文本字符串。

12.2. 被动协商

被动协商(又称 代理驱动协商)中,内容的选择(无论状态码如何)由用户代理在接收初始响应后执行。被动协商的机制可能像列出可替代表示的引用列表那样简单。

如果用户代理对初始响应内容不满意,可以对一个或多个备用资源执行 GET 请求以获取不同的表示。此类替代的选择可以由用户代理自动执行(由用户代理执行)或手动执行(例如由用户从超文本菜单中选择)。

服务器可能选择不发送初始表示,而仅发送替代列表,从而表明更偏好由用户代理进行被动协商。例如,带有 300 (Multiple Choices)406 (Not Acceptable) 状态码的响应中列出的替代项包含可用表示的信息,以便用户或用户代理可以通过选择来做出反应。

当响应将在常用维度(例如类型、语言或编码)上变化、当源服务器无法通过检查请求确定用户代理的能力、以及通常在使用公共缓存以分散服务器负载和减少网络使用时,被动协商是有利的。

被动协商的缺点是必须向用户代理传输替代列表,如果在头部部分传输会降低用户感知的延迟,并且需要第二次请求来获取替代表示。此外,本规范并未定义支持自动选择的机制,但并不阻止未来开发这类机制。

12.3. 请求内容协商

当内容协商偏好作为服务器的响应发送时,这些列出的偏好称为请求内容协商,因为它们旨在影响对该资源的后续请求时对适当内容的选择。例如,Accept(见 第 12.5.1 节)和 Accept-Encoding(见 第 12.5.3 节)头字段可以在响应中发送,以指示对该资源后续请求中首选的媒体类型和内容编码。

类似地,RFC5789 第 3.1 节 定义了 "Accept-Patch" 响应头字段,允许发现 PATCH 请求中被接受的内容类型。

12.4. 内容协商字段特性

12.4.1. 缺失

对于每个内容协商字段,不包含该字段的请求表示发送方在该协商维度上没有偏好。

如果请求中存在一个内容协商头字段,且没有可用的响应表示被认为可接受,源服务器可以通过发送 406 (Not Acceptable) 响应来尊重该头字段,或者无视该头字段,将响应视为不受该请求头字段的内容协商影响。但这并不意味着客户端就一定能够使用该表示。

12.4.2. 质量值

本规范定义的内容协商字段使用一个通用参数,名为 "q"(不区分大小写),用于为该种类内容的偏好分配相对“权重”。该权重称为“质量值”(或“qvalue”),因为相同的参数名常在服务器配置中用于为可为资源选择的各种表示分配相对质量的权重。

权重被归一化为 0 到 1 范围内的实数,其中 0.001 最不偏好,1 最偏好;值为 0 表示“不可接受”。如果未提供 "q" 参数,则默认权重为 1。

  weight = OWS ";" OWS "q=" qvalue
  qvalue = ( "0" [ "." 0*3DIGIT ] )
         / ( "1" [ "." 0*3("0") ] )

qvalue 的发送方 MUST NOT 生成小数点后超过三位的数字。用户配置这些值时也应受同样限制。

12.4.3. 通配值

大多数在本处指明的头字段定义了一个选择未指明值的通配值 ("*")。如果未出现通配值,则字段中未明确提到的值被视为不可接受。在 Vary 中,通配值表示变异是无限制的。

12.5. 内容协商字段

12.5.1. Accept

"Accept" 头字段可被用户代理用于指定它们关于响应媒体类型的偏好。例如,Accept 头字段可用于指示请求专门限制于一小组期望类型(如对内联图像的请求)。

当服务器在响应中发送时,Accept 提供了有关在对同一资源的后续请求的内容中首选哪些内容类型的信息。

  Accept = #( media-range [ weight ] )

  media-range    = ( "*/*"
                     / ( type "/" "*" )
                     / ( type "/" subtype )
                   ) parameters

星号 "*" 字符用于将媒体类型分组为范围,其中 "*/*" 表示所有媒体类型,"type/*" 表示该 type 的所有子类型。media-range 可以包含适用于该范围的媒体类型参数。

每个 media-range 之后可以跟随可选的适用媒体类型参数(例如 charset),随后可以附带表示相对权重(见 第 12.4.2 节)的可选 "q" 参数。

先前的规范允许在权重参数之后出现额外的扩展参数。accept 扩展语法(accept-params, accept-ext)已被移除,因为其定义复杂、实践中未被使用,并且更容易通过新头字段部署。使用权重的发送方 SHOULD 将 "q" 放在最后(在所有 media-range 参数之后)。接收方 SHOULD 无论参数顺序如何,都将名为 "q" 的参数作为权重处理。

示例

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

可解释为 “我偏好 audio/basic,但如果在折扣 80% 后它不是最佳可用项,也可发送任何 audio 类型”。

更复杂的例子:

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/plain 表示”。

媒体范围可以被更具体的媒体范围或具体的媒体类型覆盖。如果多个 media range 适用于给定类型,则最具体的引用具有优先权。例如,

Accept: text/*, text/plain, text/plain;format=flowed, */*

具有如下优先顺序:

  1. text/plain;format=flowed
  2. text/plain
  3. text/*
  4. */*

与给定类型关联的媒体类型质量因子通过找到与类型匹配且具有最高优先级的 media range 来确定。例如,

Accept: text/*;q=0.3, text/plain;q=0.7, text/plain;format=flowed,
       text/plain;format=fixed;q=0.4, */*;q=0.5

将导致以下值被关联:

Table 5
Media Type Quality Value
text/plain;format=flowed 1
text/plain 0.7
text/html 0.3
image/jpeg 0.5
text/plain;format=fixed 0.4
text/html;level=3 0.7

12.5.2. Accept-Charset

"Accept-Charset" 头字段可由用户代理发送以指示其对文本响应内容中字符集的偏好。例如,该字段允许能够理解更全面或特殊用途字符集的用户代理向能够以这些字符集表示信息的源服务器声明该能力。

  Accept-Charset = #( ( token / "*" ) [ weight ] )

字符集名称在 第 8.3.2 节 中定义。用户代理 MAY 为每个字符集关联一个质量值以指示用户对该字符集的相对偏好,如 第 12.4.2 节 所定义。例如:

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

特殊值 "*"(如果出现在 Accept-Charset 头字段中)匹配在字段的其他地方未提及的每个字符集。

12.5.3. Accept-Encoding

"Accept-Encoding" 头字段可用于指示关于使用内容编码(参见 第 8.4.1 节)的偏好。

当用户代理在请求中发送时,Accept-Encoding 表示响应中可接受的内容编码。

当服务器在响应中发送时,Accept-Encoding 提供了关于在对同一资源的后续请求的内容中哪些内容编码是首选的信息。

使用 "identity" 作为“无编码”的同义词,以便在偏好中传达何时首选不进行编码。

  Accept-Encoding  = #( codings [ weight ] )
  codings          = content-coding / "identity" / "*"

每个 codings 值 MAY 被赋予一个关联的质量值(权重),表示对该编码的偏好(见 第 12.4.2 节)。Accept-Encoding 字段中的星号 "*" 符号匹配字段中未明确列出的任何可用内容编码。

示例:

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

服务器使用以下规则测试给定表示的内容编码是否可接受:

  1. 如果请求中没有 Accept-Encoding 头字段,则任何内容编码都被视为用户代理可接受。
  2. 如果表示没有内容编码,则默认可接受,除非 Accept-Encoding 头字段明确将其排除,例如列出 "identity;q=0" 或 "*;q=0" 而没有为 "identity" 提供更具体的条目。
  3. 如果表示的内容编码列在 Accept-Encoding 字段值中,则可接受,除非其伴随的 qvalue 为 0。(如 第 12.4.2 节 所定义,qvalue 为 0 表示“不可接受”。)

一个表示可以被多个内容编码编码。然而,大多数内容编码是为实现相同目的的替代方式(例如数据压缩)。在选择多个具有相同目的的内容编码之间时,具有最高非零 qvalue 的可接受内容编码优先。

Accept-Encoding 头字段的字段值为空表示用户代理不希望响应中有任何内容编码。如果请求中存在非空的 Accept-Encoding 头字段且可用的响应表示没有列为可接受的内容编码,则源服务器 SHOULD 发送不带任何内容编码的响应,除非 identity 编码被指示为不可接受。

当响应中存在 Accept-Encoding 头字段时,它指示资源在关联请求中愿意接受哪些内容编码。字段值的评估方式与在请求中相同。

注意该信息特定于关联的请求;同一服务器上其他资源可能支持的编码集不同,并可能随时间变化或取决于请求的其他方面(例如请求方法)。

因不支持内容编码而失败请求的服务器应以 415 (Unsupported Media Type) 状态响应,并在该响应中包含 Accept-Encoding 头字段,以便客户端区分与内容编码相关的问题和媒体类型相关的问题。为避免与媒体类型相关问题混淆,对于与内容编码无关而返回 415 的情况,服务器 MUST NOT 包含 Accept-Encoding 头字段。

Accept-Encoding 最常见的用途是在响应因客户端乐观使用内容编码而导致的 415 (Unsupported Media Type) 状态码的响应中。然而,该头字段也可用于指示客户端支持哪些内容编码,以便优化未来的交互。例如,当请求内容足够大以证明使用压缩编码是值得的,但客户端未使用时,资源可能在 2xx (Successful) 响应中包含该字段以告知客户端。

12.5.4. Accept-Language

"Accept-Language" 头字段可被用户代理用于指示在响应中首选的一组自然语言。语言标签在 第 8.5.1 节 中定义。

每个 language-range 可以被赋予一个关联的质量值,表示用户对该范围指定的语言的偏好估计,如 第 12.4.2 节 所定义。例如:

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

表示 “我偏好丹麦语,但也接受英式英语和其他类型的英语”。

注意,有些接收方将语言标签列出的顺序视为优先级递减的指示,特别是对那些被赋予相等质量值的标签(未指定值等同于 q=1)。然而,这种行为不能被依赖。为一致性并最大化互操作性,许多用户代理会为每个语言标签分配唯一的质量值,同时也按降序列出它们。关于语言优先级列表的更多讨论见 RFC4647 第 2.3 节

对于匹配,RFC4647 第 3 节 定义了几种匹配方案。实现可以根据其需求提供最合适的匹配方案。“基本过滤”方案(见 RFC4647 第 3.3.1 节)与以前在 HTTP 中为此定义的匹配方案相同(见 RFC2616 第 14.4 节)。

在每个请求中发送完整的用户语言偏好可能违背用户的隐私期望(参见 第 17.13 节)。

由于可理解性高度依赖于个别用户,用户代理需要允许用户对语言偏好进行控制(通过用户代理自身的配置或默认采用可由用户控制的系统设置)。未向用户提供此类控制的用户代理 MUST NOT 发送 Accept-Language 头字段。

12.5.5. Vary

响应中的 "Vary" 头字段描述了请求消息的哪些部分(除了方法和目标 URI 之外)可能影响了源服务器选择此响应内容的过程。

  Vary = #( "*" / field-name )

Vary 字段值要么为通配成员 "*",要么为一列请求字段名,称为选择性头字段,这些字段可能在为该响应选择表示时起了作用。潜在的选择性头字段不局限于本规范定义的字段。

包含成员 "*" 的列表表示请求的其他方面可能在选择响应表示时起了作用,可能包括消息语法之外的方面(例如客户端的网络地址)。接收方将无法确定该响应是否适用于以后的请求,而不将请求转发给源服务器。代理 MUST NOT 在 Vary 字段值中生成 "*"。

例如,包含下列内容的响应

Vary: accept-encoding, accept-language

表示源服务器在选择该响应内容时可能使用了请求的 Accept-EncodingAccept-Language 头字段(或它们的缺失)作为决定性因素。

包含字段名列表的 Vary 字段有两个目的:

  1. 告知缓存接收方,除非后来的请求在所列头字段上与原始请求具有相同的值,或者该响应的重用已被源服务器验证,否则它们 MUST NOT 使用该响应来满足后续的请求(参见 CACHING 第 4.1 节)。换言之,Vary 扩展了匹配新请求与存储缓存条目的所需缓存键。

  2. 告知用户代理接收方该响应受内容协商(第 12 节)影响,并且如果在所列头字段中提供了其他值,则后续请求可能会发送不同的表示(即 主动协商)。

当源服务器希望该可缓存响应在后续请求中被有选择地重用时,源服务器 SHOULD 在可缓存响应上生成 Vary 头字段。通常,当响应内容已根据所选头字段表达的偏好进行定制时(例如源服务器根据请求的 Accept-Language 头字段选择响应的语言),就应如此。

当源服务器认为内容选择中的变化性对缓存的性能影响小于 Vary 所带来的影响时,特别是在重用已经受缓存响应指令限制的情况下,Vary 可以被省略(参见 CACHING 第 5.2 节)。

没有必要在 Vary 中发送 Authorization 字段名,因为根据该字段定义,不允许将该响应重用于不同的用户(参见 第 11.6.2 节)。同样,如果响应内容的选择或影响是由网络区域决定的,但源服务器希望即使接收方从一个区域移动到另一个区域也能重用缓存响应,则没有必要在 Vary 中指示此类变异。

13. 条件请求

条件请求是包含一个或多个请求头字段的 HTTP 请求,这些头字段指示在对目标资源应用请求方法之前需要测试的前置条件。第 13.2 节 定义了何时评估前置条件,以及当存在多个前置条件时的优先顺序。

条件 GET 请求是 HTTP 缓存更新的最有效机制(参见 [CACHING])。条件也可应用于会改变状态的方法,例如 PUT 和 DELETE,以防止“丢失更新”问题:即一个客户端不小心覆盖了另一个并行操作客户端的工作。

13.1. 前置条件

前置条件通常相对于目标资源的整体状态(其当前值集合)或在先前获得的某个表示中观察到的状态(该集合中的一个值)来定义。如果资源有多个当前表示,每个表示有其各自可观察的状态,则前置条件将假定每个请求到 selected representation 的映射(参见 第 3.2 节)随时间是一致的。无论如何,如果映射不一致或服务器无法选择适当的表示,那么当前置条件评估为 false 时也不会产生不良后果。

下文定义的每个前置条件均由来自目标资源的先前表示的一组验证器与所选表示当前验证器状态之间的比较组成(参见 第 8.8 节)。因此,这些前置条件用于评估自客户端已知某一状态以来目标资源的状态是否已改变。此类评估的影响取决于方法语义和所选条件,如 第 13.2 节 所定义。

其他由扩展规范定义的前置条件可能对所有接收者、目标资源的一般状态或一组资源施加条件。例如,WebDAV 中的 "If" 头字段可以使请求以多个资源的各种方面(例如锁)为条件,前提是接收者理解并实现该字段(参见 [WEBDAV],以及 RFC4918 第 10.4 节)。

只有当未知的前置条件可以安全地被忽略(例如像 If-Modified-Since 那样),或对于给定用例可以假定已部署,或通过目标资源的某些其他属性来指示实现时,前置条件的可扩展性才是可能的。这鼓励针对常见标准的共同部署达成一致。

13.1.1. If-Match

“If-Match” 头字段使请求方法以接收方源服务器:当字段值为 "*" 时拥有目标资源的至少一个当前表示,或者拥有一个其实体标签与字段值中提供的实体标签列表中某一成员匹配的当前表示为条件。

在比较 If-Match 的实体标签时,源服务器 MUST 使用强比较函数(参见 第 8.8.3.2 节),因为客户端的意图是当表示数据有任何变化时阻止方法的应用。

  If-Match = "*" / #entity-tag

示例:

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

If-Match 最常与会改变状态的方法(例如 POST、PUT、DELETE)一起使用,以在多个用户代理可能并行操作同一资源时防止意外覆盖(即防止“丢失更新”问题)。总体而言,任何涉及选择或修改表示的方法都可以使用它来在所选表示的当前实体标签不在 If-Match 字段值中时中止请求。

当源服务器接收到选择某表示且该请求包含 If-Match 头字段的请求时,源服务器 MUST 在执行该方法之前按照 第 13.2 节 评估 If-Match 条件。

评估收到的 If-Match 头字段:

  1. 如果字段值为 "*",当源服务器对目标资源有当前表示时条件为真。
  2. 如果字段值为实体标签列表,当所列任一标签与所选表示的实体标签匹配时条件为真。
  3. 否则,条件为假。

当 If-Match 条件评估为 false 时,源服务器 MUST NOT 执行请求的方法。相反,源服务器 MAY 通过响应 412 (Precondition Failed) 状态码来表明条件请求失败。或者,如果该请求是一个看起来已经对所选表示生效的状态改变操作,源服务器 MAY 响应一个 2xx (Successful) 状态码(即用户代理请求的变更实际上已经成功,但用户代理可能未意识到,例如因为先前的响应丢失或其他用户代理做了等效的更改)。

当允许源服务器在看似已应用变更的情况下发送成功响应时,这对许多创作类用例更高效,但如果多个用户代理进行非常相似但不协作的变更请求,则存在风险。例如,多个用户代理将共同资源用作信号量(例如 nonatomic increment)可能会冲突并导致重要状态转变丢失。对于此类资源,源服务器最好在对不安全方法的每个失败前置条件都严格返回 412。在其他情况下,从成功响应中排除 ETag 字段可能会促使用户代理随后执行 GET 以消除对资源当前状态的混淆。

客户端 MAY 在 GET 请求中发送 If-Match 头字段以表明若所选表示不匹配则更希望收到 412 (Precondition Failed) 响应。然而,这仅在范围请求(参见 第 14 节)中对完成先前接收的部分表示有用,而不希望得到新的表示。对于在客户端更愿意接收新表示的范围请求,If-Range(见 第 13.1.5 节)更为合适。

缓存或中间件 MAY 忽略 If-Match,因为其互操作性特性仅对源服务器是必要的。

注意,If-Match 头字段如果其列表值同时包含 "*" 与其他值(包括其他 "*" 实例)是语法上无效的(因此不允许生成),并且很可能不可互操作。

13.1.2. If-None-Match

“If-None-Match” 头字段使请求方法以接收方缓存或源服务器:当字段值为 "*" 时不拥有目标资源的任何当前表示,或者所选表示的实体标签不匹配字段值中列出的任一标签为条件。

接收方 MUST 在比较 If-None-Match 的实体标签时使用弱比较函数(参见 第 8.8.3.2 节),因为即使表示数据发生了变化,弱实体标签也可用于缓存验证。

示例:

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 主要用于条件 GET 请求,以尽量减少事务开销地实现缓存信息的高效更新。当客户端希望更新一个或多个具有实体标签的已存储响应时,客户端 SHOULD 在发出 GET 请求时生成包含那些实体标签列表的 If-None-Match 头字段;这允许接收方服务器在所列的某一已存储响应与所选表示匹配时返回 304 (Not Modified) 响应来指示匹配。

If-None-Match 也可与 "*" 值一起使用,以阻止不安全的请求方法(例如 PUT)在客户端认为资源没有当前表示时无意中修改目标资源的已有表示。这是“丢失更新”问题的一种变体,可能在多个客户端尝试为目标资源创建初始表示时出现。

当源服务器接收到选择某表示且该请求包含 If-None-Match 头字段的请求时,源服务器 MUST 在执行该方法之前按照 第 13.2 节 评估 If-None-Match 条件。

评估收到的 If-None-Match 头字段:

  1. 如果字段值为 "*",当源服务器对目标资源有当前表示时条件为假。
  2. 如果字段值为实体标签列表,当所列任一标签与所选表示的实体标签匹配时条件为假。
  3. 否则,条件为真。

当 If-None-Match 条件评估为 false 时,源服务器 MUST NOT 执行请求的方法;相反,源服务器 MUST 返回:a) 如果请求方法为 GET 或 HEAD,则返回 304 (Not Modified),或 b) 对于其他方法,则返回 412 (Precondition Failed)

关于接收到的 If-None-Match 头字段的缓存处理要求,定义见 CACHING 第 4.3.2 节(参见 [CACHING])。

注意,If-None-Match 头字段如果其列表值同时包含 "*" 与其他值(包括其他 "*" 实例)是语法上无效的(因此不允许生成),并且很可能不可互操作。

13.1.3. If-Modified-Since

“If-Modified-Since” 头字段使 GET 或 HEAD 请求方法以所选表示的修改日期晚于字段值所提供的日期为条件。如果数据未改变,则可避免传输所选表示的数据。

字段示例:

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

如果请求包含 If-None-Match 头字段,则接收方 MUST 忽略 If-Modified-Since;因为 If-None-Match 中的条件被视为对 If-Modified-Since 条件更精确的替代项,两者仅为与可能不实现 If-None-Match 的旧中间件互操作而组合使用。

如果接收到的 If-Modified-Since 字段值不是有效的 HTTP-date、字段值包含多个成员,或请求方法既不是 GET 也不是 HEAD,则接收方 MUST 忽略该字段。

如果资源没有可用的修改日期,接收方 MUST 忽略 If-Modified-Since 头字段。

接收方 MUST 根据源服务器的时钟来解释 If-Modified-Since 字段值的时间戳。

If-Modified-Since 通常有两个不同的用途:1) 允许高效更新没有实体标签的缓存表示,以及 2) 将 Web 遍历限制在最近更改过的资源范围内。

用于缓存更新时,缓存通常使用缓存消息的 Last-Modified 头字段的值来生成 If-Modified-Since 的字段值。在时钟不同步或服务器仅选择精确时间戳匹配的场景中,这种行为最具互操作性(例如当源服务器的时钟被校正或从归档备份恢复表示时,Last-Modified 似乎“回到过去”)。不过,当缓存的消息不包含 Last-Modified 时,缓存有时也会基于其他数据(例如缓存消息的 Date 头字段或接收消息时的本地时间)生成该字段值。

用于将检索范围限制在最近时间窗口时,用户代理将基于自身时钟或先前从服务器接收的 Date 头字段生成 If-Modified-Since 字段值。选择基于所选表示的 Last-Modified 头字段进行精确时间戳匹配的源服务器将无法帮助用户代理将数据传输限制为仅在指定窗口期间更改的那些内容。

当源服务器接收到选择某表示且该请求包含 If-Modified-Since 头字段但不包含 If-None-Match 头字段时,源服务器 SHOULD 在执行该方法之前按照 第 13.2 节 评估 If-Modified-Since 条件。

评估收到的 If-Modified-Since 字段:

  1. 如果所选表示的最后修改日期早于或等于字段值提供的日期,则条件为假。
  2. 否则,条件为真。

当 If-Modified-Since 条件评估为 false 时,源服务器 SHOULD NOT 执行请求的方法;相反,源服务器 SHOULD 生成一个 304 (Not Modified) 响应,只包含那些对识别或更新先前缓存响应有用的元数据。

关于接收到的 If-Modified-Since 头字段的缓存处理要求见 CACHING 第 4.3.2 节(参见 [CACHING])。

13.1.4. If-Unmodified-Since

“If-Unmodified-Since” 头字段使请求方法以所选表示的最后修改日期早于或等于字段值中提供的日期为条件。对于用户代理没有表示的实体标签的情况,该字段实现了与 If-Match 相同的目的。

字段示例:

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

如果请求同时包含 If-Match 头字段,则接收方 MUST 忽略 If-Unmodified-Since;If-Match 中的条件被视为对 If-Unmodified-Since 的更精确替代,二者仅为与可能不实现 If-Match 的旧中间件互操作而组合使用。

如果接收到的 If-Unmodified-Since 字段值不是有效的 HTTP-date(包括字段值看起来像一个日期列表的情况),接收方 MUST 忽略该字段。

如果资源没有可用的修改日期,接收方 MUST 忽略 If-Unmodified-Since 字段。

接收方 MUST 根据源服务器的时钟来解释 If-Unmodified-Since 字段值的时间戳。

If-Unmodified-Since 最常与会改变状态的方法(例如 POST、PUT、DELETE)一起使用,以在多个用户代理可能并行操作且表示没有提供实体标签时防止意外覆盖(即防止“丢失更新”问题)。总体而言,任何涉及选择或修改表示的方法都可以使用它来在所选表示的最后修改日期自字段值以来已改变时中止请求。

当源服务器接收到选择某表示且该请求包含 If-Unmodified-Since 头字段但不包含 If-Match 头字段时,源服务器 MUST 在执行该方法之前按照 第 13.2 节 评估 If-Unmodified-Since 条件。

评估收到的 If-Unmodified-Since 字段:

  1. 如果所选表示的最后修改日期早于或等于字段值提供的日期,则条件为真。
  2. 否则,条件为假。

当 If-Unmodified-Since 条件评估为 false 时,源服务器 MUST NOT 执行请求的方法。相反,源服务器 MAY 通过响应 412 (Precondition Failed) 状态码来表明条件请求失败。或者,如果请求是看起来已经对所选表示生效的状态改变操作,源服务器 MAY 响应一个 2xx (Successful) 状态码(即请求的变更已经成功,但用户代理可能不知道)。

在某些创作类用例中,当变更请求似乎已被应用时允许源服务器返回成功响应会更高效,但当多个用户代理发出非常相似但不协作的变更请求时,这会带来风险。在那些情况下,源服务器最好在对不安全方法的每个失败前置条件都严格返回 412。

客户端 MAY 在 GET 请求中发送 If-Unmodified-Since 头字段以表明若所选表示已被修改则更愿意收到 412 (Precondition Failed) 响应。然而,这仅在范围请求(参见 第 14 节)中对完成先前接收的部分表示有用。对于在客户端更愿意接收新表示的范围请求,If-Range(见 第 13.1.5 节)更为合适。

缓存或中间件 MAY 忽略 If-Unmodified-Since,因为其互操作性特性仅对源服务器必要。

13.1.5. If-Range

“If-Range” 头字段提供了一种特殊的条件请求机制,类似于 If-MatchIf-Unmodified-Since,但其指示接收方在验证器不匹配时忽略 Range 头字段,从而在验证器不匹配时传输新的 selected representation,而不是返回 412 (Precondition Failed)

如果客户端持有某个表示的部分副本并希望获得该表示的最新版完整副本,它可以在条件 GET 中使用 Range 头字段(并使用 If-Unmodified-Since 和/或 If-Match 作为前置条件)。但是,如果前置条件因为表示已被修改而失败,客户端则需要发起第二次请求以获取完整的当前表示。

“If-Range” 头字段允许客户端“短路”第二次请求。非正式地,其含义为:如果表示未改变,则发送我请求的 Range 部分;否则,发送整个表示。

通过检查前三个字符是否为 DQUOTE,可以将有效的 entity-tag 与有效的 HTTP-date 区分开来。

客户端 MUST NOT 在不包含 Range 头字段的请求中生成 If-Range。服务器在接收到不包含 Range 的请求中 If-Range 时 MUST 忽略该 If-Range。对于不支持 Range 请求的目标资源,源服务器在接收到 If-Range 时 MUST 忽略该字段。

客户端 MUST NOT 生成包含标记为弱的实体标签的 If-Range 头字段。除非客户端对相应表示没有实体标签,且该日期在 第 8.8.2.2 节 中定义意义上是一个强验证器,否则客户端 MUST NOT 生成包含 HTTP-date 的 If-Range。

在 Range 请求上接收到 If-Range 头字段的服务器 MUST 在执行方法之前按照 第 13.2 节 评估该条件。

评估包含 HTTP-date 的 If-Range:

  1. 如果所提供的 HTTP-date 验证器不是在 第 8.8.2.2 节 中定义意义上的强验证器,则该条件为假。
  2. 如果所提供的 HTTP-date 验证器与所选表示的 Last-Modified 字段值完全匹配,则该条件为真。
  3. 否则,该条件为假。

评估包含 entity-tag 的 If-Range:

  1. 如果提供的 entity-tag 验证器使用强比较函数(参见 第 8.8.3.2 节)与所选表示的 ETag 字段值完全匹配,则条件为真。
  2. 否则,条件为假。

If-Range 的接收者 MUST 在 If-Range 条件评估为假时忽略 Range 头字段。否则,接收者 SHOULD 按请求处理 Range 头字段。

注意 If-Range 比较是按精确匹配进行的,包括当验证器为 HTTP-date 时,因此它不同于在评估 If-Unmodified-Since 条件时使用的“早于或等于”比较。

13.2. 前置条件的评估

13.2.1. 何时评估

除下文排除的情况外,接收方缓存或源服务器 MUST 在成功完成其正常请求检查并且就在处理请求内容(如果有)或执行与请求方法相关联的动作之前评估收到的请求前置条件。如果在在处理请求内容之前对同一请求不带这些条件的响应将是除 2xx (Successful)412 (Precondition Failed) 之外的状态码,则服务器 MUST 忽略所有收到的前置条件。换言之,可以在大量处理发生之前检测到的重定向和失败优先于前置条件的评估。

如果一个服务器不是目标资源的源服务器并且不能作为目标资源请求的缓存,它 MUST NOT 评估本规范定义的条件请求头字段,并且在转发请求时 MUST 转发这些字段,因为生成这些字段的客户端希望它们由能够提供当前表示的服务器来评估。同样,当收到的请求方法不涉及所选表示的选择或修改(例如 CONNECT、OPTIONS 或 TRACE)时,服务器 MUST 忽略本规范定义的条件请求头字段。

注意,协议扩展可以修改评估前置条件的条件或评估它们的后果。例如,不可变缓存指令(由 [RFC8246] 定义)指示缓存在持有新鲜响应时放弃转发条件请求。

尽管条件请求头字段被定义为可与 HEAD 方法一起使用(以使 HEAD 的语义与 GET 保持一致),但发送条件 HEAD 毫无意义,因为成功响应的大小与 304 (Not Modified) 响应相近,而后者比 412 (Precondition Failed) 更有用。

13.2.2. 前置条件的优先顺序

当请求中存在多个条件请求头字段时,字段的评估顺序就变得重要。实际上,本文档中定义的字段在实现中通常按一个统一的逻辑顺序实现,因为“丢失更新”前置条件的要求比缓存验证更严格,经过验证的缓存比部分响应更高效,并且实体标签被认为比日期验证器更准确。

接收方缓存或源服务器 MUST 按下列顺序评估本规范定义的请求前置条件:

  1. 当接收方为源服务器且存在 If-Match 时,评估 If-Match 前置条件:

  2. 当接收方为源服务器,且不存在 If-Match,但存在 If-Unmodified-Since 时,评估 If-Unmodified-Since 前置条件:

  3. 当存在 If-None-Match 时,评估 If-None-Match 前置条件:

  4. 当方法为 GET 或 HEAD,且不存在 If-None-Match,但存在 If-Modified-Since 时,评估 If-Modified-Since 前置条件:

  5. 当方法为 GET 且同时存在 RangeIf-Range 时,评估 If-Range 前置条件:

  6. 否则,

    • 执行请求的方法并根据其成功或失败作出响应。

任何对 HTTP 的扩展如果定义了额外的条件请求头字段,应当定义这些字段相对于本文档中定义的字段以及实践中可能出现的其他条件的评估顺序。

14. 范围请求

客户端经常因为请求被取消或连接中断而遇到传输中断的情况。当客户端已存储了部分表示时,期望在后续请求中只请求该表示的剩余部分,而不是再次传输整个表示。同样,存储受限的设备可能只需请求更大表示的子集(例如非常大文档的一页或嵌入图像的尺寸)。

Range requests 是 HTTP 的一个 OPTIONAL 功能,设计上允许不实现此功能的接收者(或对目标资源不支持此功能的接收者)像处理普通 GET 请求那样响应而不影响互操作性。部分响应使用特殊的状态码来指示,以免被不支持该功能的缓存误认为是完整响应。

14.1. 范围单位

当表示数据的内容编码或媒体类型固有地具有可寻址的结构单元时,表示数据可以被划分为子范围。例如,八位字节(即 byte)边界是所有表示数据的常见结构单元,允许将数据的分区标识为从数据开始或结束起的某个偏移量的字节范围。

这种通用的 range unit 概念用于响应头字段 Accept-Ranges(见 第 14.3 节)来声明对范围请求的支持;用于请求头字段 Range(见 第 14.2 节)来划定所请求表示的部分;以及用于头字段 Content-Range(见 第 14.4 节)来描述正在传输的表示部分。

所有 range unit 名称不区分大小写,并应在 “HTTP Range Unit Registry” 中注册(参见 第 16.5.1 节)。

第 16.5 节 所述,范围单位旨在可扩展。

14.1.1. 范围说明符

范围通过范围单位与一组范围说明符配对来表示。范围单位名称决定哪些类型的 range-spec 适用于其自身的说明符。因此,下面的语法是通用的:每个范围单位应规定何时允许 int-rangesuffix-rangeother-range

一个范围请求可以在单个表示内指定单个范围或一组范围。

int-range 是以两个非负整数表示的范围,或以一个非负整数表示直到表示数据末尾的范围。范围单位指定这些整数的含义(例如,它们可能表示从开头的单元偏移量、包括端点的编号部分等)。

如果存在 last-pos 且其值小于 first-pos,则该 int-range 是无效的。

suffix-range 是以表示数据的后缀表示的范围,给出的是所提供的非负整数最大长度(以范围单位为单位)。换言之,即表示数据的最后 N 个单元。

为支持可扩展性,other-range 规则为几乎不受约束的语法,允许特定应用或未来的范围单位定义额外的范围说明符。

  other-range   = 1*( %x21-2B / %x2D-7E )
                ; 1*(VCHAR excluding comma)

如果 ranges-specifier 包含任何对所指示的 range-unit 来说无效或未定义的 range-spec,则该 ranges-specifier 是无效的。

如果一个有效的 ranges-specifier 包含至少一个相对于所指示的 range-unit 而言是可满足的 range-spec,则称其为 satisfiable;否则称为 unsatisfiable

14.1.2. 字节范围(Byte Ranges)

“bytes” 范围单位用于表示表示数据的 octet(字节)序列的子范围。每个字节范围表示为相对于表示数据的开头(int-range)或结尾(suffix-range)的整数范围。字节范围不使用 other-range 说明符。

在 bytes 的 int-range 中,first-pos 值表示范围中第一个字节的偏移。last-pos 值表示范围中最后一个字节的偏移;也就是说,所指定的字节位置是包含端点的。字节偏移从零开始。

如果对表示数据应用了内容编码,则每个字节范围相对于编码后的字节序列计算,而不是解码后得到的底层字节序列。

字节范围说明符的示例:

  • 前 500 个字节(字节偏移 0-499,包含端点):

    bytes=0-499
    
  • 第二个 500 个字节(字节偏移 500-999,包含端点):

    bytes=500-999
    

客户端可以在不知道所选表示大小的情况下限制请求的字节数。如果 last-pos 值缺失,或该值大于或等于表示数据的当前长度,则该字节范围被解释为表示的剩余部分(即服务器将 last-pos 的值替换为所选表示当前长度减一)。

客户端可以使用 suffix-range 引用所选表示的最后 N 个字节(N > 0)。如果所选表示短于指定的 suffix-length,则使用整个表示。

假定一个长度为 10000 的表示,以下为其他示例:

  • 最后 500 个字节(字节偏移 9500-9999,包含端点):

    bytes=-500
    

    或:

    bytes=9500-
    
  • 仅第一个和最后一个字节(字节 0 和 9999):

    bytes=0-0,-1
    
  • 第一个、中间和最后各 1000 个字节:

    bytes= 0-999, 4500-5499, -1000
    
  • 第二个 500 个字节(字节偏移 500-999,包含端点)的其他有效(但非规范)说明:

    bytes=500-600,601-999
    bytes=500-700,601-999
    

对于 GET 请求,如果一个 bytes range-spec 有效,则当且仅当以下任一情况成立时该 range-spec 被视为 satisfiable

当所选表示长度为零时,在 GET 请求中唯一可满足的 range-spec 形式是带有非零 suffix-lengthsuffix-range

在字节范围语法中,first-poslast-possuffix-length 以十进制表示为八位字节数。由于内容长度没有预定义上限,接收方 MUST 预期可能出现很大的十进制数,并防止因整数转换溢出导致的解析错误。

14.2. Range

"Range" 头字段在 GET 请求中修改方法语义,以请求仅传输所选表示数据的一个或多个子范围(参见 第 8.1 节),而不是整个 selected representation

服务器 MAY 忽略 Range 头字段。然而,源服务器和中间缓存应在可能时支持字节范围,因为它们有助于从部分失败的传输中高效恢复并对大型表示进行部分检索。

服务器 MUST 忽略与未知或未定义范围处理的方法一起收到的 Range 头字段。对于本规范,只有 GET 为定义了范围处理的方法。

源服务器 MUST 忽略包含其不理解的范围单位的 Range 头字段。代理 MAY 丢弃包含其不理解范围单位的 Range 头字段。

支持范围请求的服务器 MAY 忽略或拒绝包含无效 ranges-specifierRange 头字段(见 第 14.1.1 节)、包含超过两个重叠范围的 ranges-specifier,或列出的许多小范围且未按升序列出的集合,因为这些情况表明客户端损坏或存在恶意的拒绝服务攻击(参见 第 17.15 节)。客户端 SHOULD NOT 请求那些在处理和传输上固有地比包含相同数据的单一范围更低效的多个范围。

支持范围请求的服务器 MAY 在所选表示没有内容(即所选表示的数据长度为零)时忽略 Range 头字段。

请求多个范围的客户端 SHOULD 按升序列出这些范围(即通常在完整表示中接收它们的顺序),除非确有需要先请求较后部分。例如,处理具有内部部件目录的大型表示的用户代理可能需要先请求后面的部分,特别是当表示由以相反顺序存储的页面组成且用户代理希望逐页传输时。

Range 头字段在评估 第 13.1 节 定义的前置条件头字段之后评估,并且仅在不带 Range 头字段时的结果将是 200 (OK) 响应时才适用。换言之,当条件 GET 会导致 304 (Not Modified) 响应时,Range 会被忽略。

If-Range 头字段(见 第 13.1.5 节)可用作应用 Range 头字段的前置条件。

如果所有前置条件为真、服务器对目标资源支持 Range 头字段、接收到的 Range 字段值包含对该目标资源支持的带有效 ranges-specifierrange-unit,并且该 ranges-specifier 相对于所选表示是 satisfiable,则服务器 SHOULD 发送带有一个或多个对应所请求可满足 range-spec 的部分表示内容的 206 (Partial Content) 响应。

上述并不意味着服务器会发送所有请求的范围。在某些情况下,可能只适合(或更高效)先发送所请求范围的一部分,同时期望客户端在仍需要时以后重新请求剩余部分(见 第 15.3.7 节)。

如果所有前置条件为真,且服务器对目标资源支持 Range 头字段,且接收到的 Range 字段值包含有效的 ranges-specifier,但该 range-unit 对该目标资源不被支持或该 ranges-specifier 相对于所选表示是不可满足的,则服务器 SHOULD 发送 416 (Range Not Satisfiable) 响应。

14.3. Accept-Ranges

响应中的 "Accept-Ranges" 字段表示上游服务器是否支持对该目标资源的范围请求。

例如,支持字节范围请求的服务器(见 第 14.1.2 节)可以发送:

Accept-Ranges: bytes

以表明它对该目标资源支持字节范围请求,从而鼓励客户端在同一路径上将来对部分请求加以使用。范围单位定义见 第 14.1 节

客户端 MAY 无论是否收到 Accept-Ranges 字段都生成范围请求。该信息仅作为建议以改进性能并减少不必要的网络传输。

相反,客户端 MUST NOT 假定收到 Accept-Ranges 字段意味着未来的范围请求必然返回部分响应。内容可能会改变,服务器可能仅在某些时间或条件下支持范围请求,或者下次请求可能由不同的中间件处理。

对于目标资源不支持任何范围请求的服务器,MAY 发送:

Accept-Ranges: none

以建议客户端不要在相同请求路径上尝试范围请求。范围单位 "none" 为此目的保留。

Accept-Ranges 字段 MAY 在尾部节中发送,但建议作为头字段发送,因为该信息对于重启在内容中断(在收到尾部节之前)的大型传输特别有用。

14.4. Content-Range

"Content-Range" 头字段在单部分 206 (Partial Content) 响应中发送,以指示所封装的 selected representation 的部分范围;在 multipart 206 响应的每个部分中发送以指示各主体部分所封装的范围(见 第 14.6 节);并在 416 (Range Not Satisfiable) 响应中发送以提供关于所选表示的信息。

如果 206 (Partial Content) 响应包含带有接收者不理解的 range unitContent-Range 头字段,则接收者 MUST NOT 尝试将其与已存储的表示重组。接收到此类消息的代理 SHOULD 将其转发到下游。

Content-Range 也可以作为请求修饰符发送以请求部分 PUT(基于客户端与源服务器之间的私人约定),如 第 14.5 节 所述。服务器 MUST 忽略在不定义 Content-Range 支持的方法的请求中接收到的 Content-Range 头字段。

对于字节范围,发送方 SHOULD 指示从中提取范围的表示的完整长度,除非完整长度未知或难以确定。当用星号字符 ("*") 代替 complete-length 时,表示在生成该头字段时表示长度未知。

下例说明当发送方已知所选表示的完整长度为 1234 字节时的情况:

Content-Range: bytes 42-1233/1234

下例说明当完整长度未知时的情况:

Content-Range: bytes 42-1233/*

如果 Content-Range 的值包含一个 range-resp,其 last-pos 小于其 first-pos,或其 complete-length 小于或等于其 last-pos,则该 Content-Range 字段值为无效。接收无效 Content-Range 的接收者 MUST NOT 试图将接收到的内容与已存储表示重组。

对于对字节范围请求生成 416 (Range Not Satisfiable) 响应的服务器,SHOULD 发送一个带有 unsatisfied-range 值的 Content-Range 头字段,例如:

Content-Range: bytes */1234

416 响应中的 complete-length 指示所选表示的当前长度。

Content-Range 头字段对那些未明确定义其语义的状态码没有意义。对于本规范,仅 206 (Partial Content)416 (Range Not Satisfiable) 描述了 Content-Range 的语义。

下列为所选表示总共包含 1234 字节时的一些 Content-Range 值示例:

  • 前 500 个字节:

    Content-Range: bytes 0-499/1234
    
  • 第二个 500 个字节:

    Content-Range: bytes 500-999/1234
    
  • 除了前 500 个字节之外的全部:

    Content-Range: bytes 500-1233/1234
    
  • 最后 500 个字节:

    Content-Range: bytes 734-1233/1234
    

14.5. 部分 PUT

一些源服务器在请求中收到带有 Content-Range 头字段(见 第 14.4 节)的 PUT 时支持对部分表示的 PUT,尽管这种支持不一致且取决于与用户代理的私人约定。通常,这意味着请求用所封装内容在由 Content-Range 值指示的偏移和长度处部分替换目标资源的状态,其中偏移相对于当前所选表示。

如果源服务器在对不支持部分 PUT 的目标资源的 PUT 请求中收到 Content-Range,则源服务器 SHOULD400 (Bad Request) 状态码响应。

部分 PUT 与 PUT 的原始定义不向后兼容。它可能导致内容作为对当前表示的完整替换而被写入。

部分资源更新也可以通过定位到一个单独标识的、其状态与更大资源重叠或扩展的资源来实现,或者使用已专门定义用于部分更新的不同方法(例如在 [RFC5789] 中定义的 PATCH 方法)。

14.6. 媒体类型 multipart/byteranges

当一个 206 (Partial Content) 响应消息包含多个范围的内容时,它们作为 multipart 消息体中的主体部分传输(参见 [RFC2046]第 5.1 节),媒体类型为 "multipart/byteranges"。

"multipart/byteranges" 媒体类型包含一个或多个主体部分,每个部分都有其自己的 Content-TypeContent-Range 字段。必需的 boundary 参数指定用于分隔每个主体部分的边界字符串。

实现说明:

  1. 主体中可能在第一个边界字符串之前有额外的 CRLF。
  2. 尽管 [RFC2046] 允许边界字符串被引用,但一些现有实现对带引号的边界字符串处理不正确。
  3. 许多客户端和服务器是根据早期草案实现的,该草案使用媒体类型 "multipart/x-byteranges",这与本类型几乎(但不完全)兼容。

尽管名称如此,"multipart/byteranges" 媒体类型并不限于字节范围。下例使用 "exampleunit" 范围单位:

HTTP/1.1 206 Partial Content
Date: Tue, 14 Nov 1995 06:25:24 GMT
Last-Modified: Tue, 14 July 04:58:08 GMT
Content-Length: 2331785
Content-Type: multipart/byteranges; boundary=THIS_STRING_SEPARATES

--THIS_STRING_SEPARATES
Content-Type: video/example
Content-Range: exampleunit 1.2-4.3/25

...the first range...
--THIS_STRING_SEPARATES
Content-Type: video/example
Content-Range: exampleunit 11.2-14.3/25

...the second range
--THIS_STRING_SEPARATES--

以下信息作为 "multipart/byteranges" 媒体类型的注册表单:

类型名称:
multipart
子类型名称:
byteranges
必需参数:
boundary
可选参数:
N/A
编码考虑:
仅允许 "7bit"、"8bit" 或 "binary"
安全考虑:
第 17 节
互操作性考虑:
N/A
发布规范:
RFC 9110(见 第 14.6 节
使用此媒体类型的应用:
支持在单个请求中返回多个范围的 HTTP 组件
片段标识符考虑:
N/A
额外信息:
此类型的已弃用别名:
N/A
魔数:
N/A
文件扩展名:
N/A
Macintosh 文件类型代码:
N/A
用于进一步信息的联系人及电子邮箱:
见 Authors' Addresses 节。
预期用途:
COMMON
使用限制:
N/A
作者:
见 Authors' Addresses 节。
变更控制者:
IESG

15. 状态码

响应的状态码是一个三位整数代码,用来描述请求的结果和响应的语义,包括请求是否成功以及所封装的内容(如果有的话)是什么。所有有效的状态码范围为 100 到 599(含)。

状态码的第一位数字定义了响应的类别。后两位数字不承担任何分类作用。第一位数字有五种取值:

HTTP 状态码是可扩展的。客户端不必理解所有已注册状态码的含义,尽管理解当然是可取的。然而,客户端 MUST 理解任一状态码的类别(由第一位数字指示),并将无法识别的状态码视为该类的 x00 状态码等价。

例如,如果客户端收到一个无法识别的 471 状态码,它可以从首位数字看出请求存在问题,并将该响应当作收到 400 (Bad Request) 来处理。响应消息通常会包含一个解释该状态的表示。

超出 100..599 范围的值是无效的。实现经常在该范围之外(即 600..999)使用三位整数作为内部非 HTTP 状态(例如库错误)的通信手段。收到带有无效状态码的响应的客户端 SHOULD 将该响应当作 5xx (Server Error) 来处理。

一次请求可以有多个相关联的响应:零个或多个带有 “信息性”(1xx)范围内状态码的临时(非最终)响应,随后恰好一个属于其他范围之一的最终响应。

15.1. 状态码概览

下列列出的状态码在本规范中有定义。此处列出的原因短语只是建议——它们可以被本地等价替换或完全省略,而不会影响协议。

被定义为启发式可缓存的状态码响应(例如本规范中的 200、203、204、206、300、301、308、404、405、410、414 和 501)可以由缓存以启发式过期规则重用,除非方法定义或显式缓存控制另有指示;所有其他状态码则不是启发式可缓存的。

在本规范范围之外的其他状态码也已为 HTTP 使用进行规定。所有此类状态码应在 “Hypertext Transfer Protocol (HTTP) Status Code Registry” 中注册,如 第 16.2 节 所述。

15.2. 信息性 1xx

1xx(信息性)类状态码表示在完成所请求动作并发送最终响应之前,用于传达连接状态或请求进展的临时响应。由于 HTTP/1.0 未定义任何 1xx 状态码,服务器 MUST NOT 向 HTTP/1.0 客户端发送 1xx 响应。

1xx 响应以头部部分结束;它不能包含内容或尾部字段。

客户端 MUST 能够解析在最终响应之前收到的一个或多个 1xx 响应,即使客户端并未期望收到这样的响应。用户代理 MAY 忽略意外的 1xx 响应。

代理 MUST 转发 1xx 响应,除非该代理自身请求生成该 1xx 响应。例如,如果代理在转发请求时添加了 "Expect: 100-continue" 头字段,那么它无需转发相应的 100 (Continue) 响应。

15.2.1. 100 Continue

100 (Continue) 状态码表示请求的初始部分已被接收且尚未被服务器拒绝。服务器打算在完全接收并处理完请求后发送最终响应。

当请求包含一个带有 100-continue 期望的 Expect 头字段时,100 响应表示服务器希望接收请求内容,如 第 10.1.1 节 所述。客户端应继续发送请求并丢弃该 100 响应。

如果请求不包含带有 100-continue 期望的 Expect 头字段,客户端可以简单地丢弃此临时响应。

15.2.2. 101 Switching Protocols

101 (Switching Protocols) 状态码表示服务器理解并愿意遵从客户端通过 Upgrade 头字段(见 第 7.8 节)发起的用于更改此连接上使用的应用协议的请求。服务器 MUST 在响应中生成一个 Upgrade 头字段,指明在此响应之后将生效的协议。

通常假定服务器仅在切换协议有利时才同意这样做。例如,切换到更新的 HTTP 版本可能优于旧版本;在交付使用实时、同步特性的资源时,切换到实时同步协议可能更有利。

15.3. 成功 2xx

2xx(成功)类状态码表示客户端的请求已被成功接收、理解并接受。

15.3.1. 200 OK

200 (OK) 状态码表示请求已成功。200 响应中发送的内容取决于请求方法。对于本规范定义的方法,内容的预期意义可概括如下:

Table 6
Request Method 响应内容表示:
GET 目标资源(target resource
HEAD 目标资源,与 GET 类似,但不传输表示数据
POST 操作的状态或从该操作获得的结果
PUT, DELETE 该操作的状态
OPTIONS 目标资源的通信选项
TRACE 服务器收到的请求消息以供跟踪返回

除 CONNECT 的响应外,除非消息分块明确指示内容长度为零,否则 200 响应预计包含消息内容。如果请求的某些方面表示成功时希望没有内容,源服务器应发送 204 (No Content) 响应。对于 CONNECT,没有内容因为成功结果是一个隧道,它在 200 响应头部部分之后立即开始。

200 响应是启发式可缓存的;即除非方法定义或显式缓存控制另有指示(参见 CACHING 第 4.2.2 节)。

对于对 GET 或 HEAD 的 200 响应,源服务器 SHOULD 为所选表示发送任何可用的验证器字段(见 第 8.8 节),首选同时提供强实体标签和 Last-Modified 日期。

对于对会改变状态的方法的 200 响应,任何在响应中发送的验证器字段(见 第 8.8 节)传达的是成功应用请求语义后新生成表示的当前验证器。注意 PUT 方法(参见 第 9.3.4 节)具有可能阻止发送此类验证器的附加要求。

15.3.2. 201 Created

201 (Created) 状态码表示请求已被实现并导致一个或多个新资源被创建。由请求创建的主要资源通过响应中的 Location 头字段标识,或者如果没有收到 Location 头字段,则由目标 URI 标识。

201 响应的内容通常描述并链接到所创建的资源。任何在响应中发送的验证器字段(见 第 8.8 节)传达的是由请求创建的新表示的当前验证器。注意 PUT 方法(参见 第 9.3.4 节)具有可能阻止发送此类验证器的附加要求。

15.3.3. 202 Accepted

202 (Accepted) 状态码表示请求已被接受以供处理,但处理尚未完成。请求可能最终会被处理,也可能不会被处理,因为在实际处理时可能被拒绝。HTTP 中没有用于从异步操作重新发送状态码的机制。

202 响应故意保持非承诺性。其目的是允许服务器接受某个供其他进程(也许是每天仅运行一次的批处理)处理的请求,而不要求用户代理与服务器的连接持续到处理完成。随该响应发送的表示应描述请求的当前状态并指向(或嵌入)一个状态监视器,以便为用户提供关于请求何时完成的估计。

15.3.4. 203 Non-Authoritative Information

203 (Non-Authoritative Information) 状态码表示请求成功,但所封装的内容已被某个转换代理修改,且与源服务器的 200 (OK) 响应不同(参见 第 7.7 节)。当代理对内容应用了转换时,此状态码允许代理通知接收方,这一事实可能会影响针对该内容的后续决策,例如将来对该内容的缓存验证可能仅适用于通过相同请求链路(通过相同代理)。

203 响应是启发式可缓存的;即除非方法定义或显式缓存控制另有指示(参见 CACHING 第 4.2.2 节)。

15.3.5. 204 No Content

204 (No Content) 状态码表示服务器已成功完成请求且没有要在响应内容中再发送的额外内容。响应头字段中的元数据指的是在请求动作被应用后目标资源及其所选表示(如果有的话)。

例如,如果对 PUT 请求收到 204 状态码并且响应包含一个 ETag 字段,则说明 PUT 成功,且 ETag 字段值包含该目标资源新表示的实体标签。

204 响应允许服务器表示该动作已成功应用于目标资源,同时暗示用户代理无需离开其当前的“文档视图”(如果有的话)。服务器假定用户代理会根据其界面向用户提供某种成功提示,并将响应中新的或更新的元数据应用于其活动表示。

例如,在与“保存”操作对应的文档编辑界面中通常使用 204 状态码,以便被保存的文档仍然可供用户编辑。它也常用于期望自动化数据传输普遍存在的接口,例如分布式版本控制系统中。

204 响应以头部部分结束;它不能包含内容或尾部字段。

204 响应是启发式可缓存的;即除非方法定义或显式缓存控制另有指示(参见 CACHING 第 4.2.2 节)。

15.3.6. 205 Reset Content

205 (Reset Content) 状态码表示服务器已完成请求,并希望用户代理重置导致该请求被发送的“文档视图”,将其恢复为从源服务器接收时的原始状态。

此响应旨在支持常见的数据录入用例:用户收到支持数据录入的内容(表单、记事本、画布等),在该空间输入或操作数据,提交这些数据形成请求,然后数据录入机制被重置以供下一次输入,从而便于用户再次发起输入操作。

由于 205 状态码意味着不会提供额外内容,服务器 MUST NOT 在 205 响应中生成内容。

15.3.7. 206 Partial Content

206 (Partial Content) 状态码表示服务器成功地满足了对目标资源的范围请求,通过传输所选表示的一个或多个部分来完成。

支持范围请求(见 第 14 节)的服务器通常会尝试满足所有请求的范围,因为发送更少的数据可能会导致客户端为剩余内容发起另一个请求。然而,服务器可能出于自身原因(例如临时不可用、缓存效率、负载平衡等)只发送请求数据的一个子集。由于 206 响应是自描述的,客户端仍然可以理解仅部分满足范围请求的响应。

客户端 MUST 检查 206 响应的 Content-TypeContent-Range 字段以确定包含了哪些部分以及是否需要额外的请求。

生成 206 响应的服务器 MUST 生成下列头字段(如果在对相同请求的 200 (OK) 响应中会发送它们),除非在下文小节中另有规定:DateCache-ControlETagExpiresContent-LocationVary

在 206 响应中出现的 Content-Length 头字段表示该消息内容的八位字节数,通常不是所选表示的完整长度。每个 Content-Range 头字段包含有关所选表示完整长度的信息。

对带有 If-Range 头字段的请求生成 206 响应的发送方 SHOULD NOT 生成额外的表示头字段,因为客户端已经持有先前响应中包含的这些头字段。否则,发送方 MUST 生成在对相同请求的 200 (OK) 响应中本应发送的所有表示头字段。

206 响应是启发式可缓存的;即除非显式缓存控制另有指示(参见 CACHING 第 4.2.2 节)。

15.3.7.1. 单部分

如果仅传输单个部分,生成 206 响应的服务器 MUST 生成一个 Content-Range 头字段,描述所封装的所选表示的哪一范围,并生成包含该范围的内容。例如:

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

... 26012 bytes of partial image data ...
15.3.7.2. 多部分

如果传输多个部分,生成 206 响应的服务器 MUST 生成 "multipart/byteranges" 内容(按 第 14.6 节 定义),并在 Content-Type 头字段中包含 "multipart/byteranges" 媒体类型及其必需的 boundary 参数。为避免与单部分响应混淆,服务器 MUST NOT 在多部分响应的 HTTP 头部节中生成 Content-Range 头字段(该字段将在每个部分中单独发送)。

在 multipart 内容的每个主体部分的头部区域内,服务器 MUST 生成对应于该主体部分所包含范围的 Content-Range 头字段。如果所选表示在对同一请求的 200 (OK) 响应中本应有 Content-Type 头字段,则服务器 SHOULD 在每个主体部分的头部区域生成相同的 Content-Type 头字段。例如:

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-Length: 1741
Content-Type: multipart/byteranges; boundary=THIS_STRING_SEPARATES

--THIS_STRING_SEPARATES
Content-Type: application/pdf
Content-Range: bytes 500-999/8000

...the first range...
--THIS_STRING_SEPARATES
Content-Type: application/pdf
Content-Range: bytes 7000-7999/8000

...the second range
--THIS_STRING_SEPARATES--

当请求多个范围时,服务器 MAY 合并任何重叠的范围,或将被分隔成多个部分但间隙小于发送多个部分的开销的范围合并,无论接收的 Range 头字段中对应的 range-spec 出现顺序如何。由于 "multipart/byteranges" 每个部分之间的典型开销大约为 80 字节(取决于所选表示的媒体类型和所选 boundary 参数长度),传输许多小的分离部分可能比传输整个所选表示更低效。

服务器 MUST NOT 向仅请求单一范围的请求生成多部分响应,因为不请求多部分的客户端可能不支持多部分响应。然而,如果客户端请求了多个范围且仅发现一个范围可满足或合并后只剩一个范围,则服务器 MAY 生成仅含单个主体部分的 "multipart/byteranges" 响应。无法处理 "multipart/byteranges" 响应的客户端 MUST NOT 生成请求以请求多个范围。

生成多部分响应的服务器 SHOULD 按接收的 Range 头字段中各对应 range-spec 出现的相同顺序发送部分,排除那些被判定为不可满足或被合并到其他范围的部分。接收多部分响应的客户端 MUST 检查每个主体部分中存在的 Content-Range 头字段,以确定该主体部分包含的是哪个范围;客户端不能依赖收到的范围与其请求的范围相同,也不能依赖其收到的顺序与请求顺序相同。

15.3.7.3. 合并部分

如果连接过早关闭或请求使用了一个或多个 Range 说明符,响应可能仅传输表示的一个子范围。在多次此类传输之后,客户端可能已接收到同一表示的多个范围。只有当这些范围具有相同的强验证器时(见 第 8.8.1 节),这些范围才能安全地合并。

收到针对目标资源的多个部分响应的客户端 MAY 在它们共享相同强验证器的前提下,将这些响应合并为更大的连续范围。

如果最近的响应是不完整的 200 (OK) 响应,则该响应的头字段用于任何合并后的响应,并替换匹配已存响应的头字段。

如果最近的响应是 206 (Partial Content) 且至少一个匹配的已存响应是 200 (OK),则合并后响应的头字段由最近的 200 响应的头字段组成。如果所有匹配的已存响应均为 206 响应,则具有最近头字段的已存响应被用作合并后响应头字段的来源,除非新的响应提供了其他头字段(不包括 Content-Range),这些新的头字段必须替换已存响应中对应的所有头字段实例。

合并后响应的内容由新响应与所有匹配已存响应中部分内容范围的并集组成。如果并集构成了表示的完整范围,则客户端 MUST 将合并后的响应作为完整的 200 (OK) 响应来处理,包括一个反映完整长度的 Content-Length 头字段。否则,客户端 MUST 将连续范围集合处理为下列之一:如果合并响应是表示的前缀,则作为不完整的 200 (OK) 响应;作为包含 "multipart/byteranges" 内容的单个 206 (Partial Content) 响应;或多个 206 (Partial Content) 响应,每个响应包含一个由 Content-Range 头字段标示的连续范围。

15.4. 重定向 3xx

3xx(重定向)类状态码表示用户代理为完成请求需要采取进一步操作。存在若干类型的重定向:

  1. 指示该资源可能在不同 URI 可用的重定向,由 Location 头字段提供,如 301 (Moved Permanently)302 (Found)307 (Temporary Redirect)308 (Permanent Redirect)
  2. 提供在多个可匹配资源之间选择的重定向,供代表该资源的匹配项选择,如 300 (Multiple Choices)
  3. 重定向到不同资源(由 Location 头字段标识),该资源可以对请求提供间接响应,如 303 (See Other)
  4. 重定向到以前存储的结果,如 304 (Not Modified)

如果提供了 Location 头字段(见 第 10.2.2 节),用户代理 MAY 自动将其请求重定向到 Location 字段值所引用的 URI,即使该特定状态码不被理解。自动重定向时对于不被认为是 safe 的方法(见 第 9.2.1 节)需要谨慎,因为用户可能不希望将不安全的请求重定向。

在自动跟随重定向请求时,用户代理 SHOULD 重新发送原始请求消息并进行如下修改:

  1. 将目标 URI 替换为重定向响应的 Location 头字段值所引用的 URI,在解析时以原始请求的目标 URI 为基准。

  2. 移除由实现自动生成的头字段,并在适当情况下用更新的值替换它们。包括:

    1. 连接特定的头字段(见 第 7.6.1 节),
    2. 与客户端代理配置相关的头字段(包括但不限于 Proxy-Authorization),
    3. 来源特定的头字段(如有),包括(但不限于)Host
    4. 由实现的缓存添加的验证头字段(例如 If-None-MatchIf-Modified-Since),以及
    5. 资源特定的头字段,包括(但不限于)Referer、Origin、Authorization 和 Cookie。
  3. 考虑移除那些不是由实现自动生成的头字段(即由调用上下文添加的头字段),如果它们存在安全含义;这包括但不限于 Authorization 和 Cookie。

  4. 根据重定向状态码的语义(如适用)更改请求方法。

  5. 如果请求方法已更改为 GET 或 HEAD,移除内容相关的头字段,包括(但不限于)Content-EncodingContent-LanguageContent-LocationContent-TypeContent-Length、Digest、以及 Last-Modified

客户端 SHOULD 检测并干预循环重定向(即“无限”重定向环)。

15.4.1. 300 Multiple Choices

300 (Multiple Choices) 状态码表示目标资源具有多个表示,每个表示都有各自更具体的标识符,并且服务器提供了有关这些替代项的信息,以便用户(或用户代理)可以通过将其请求重定向到这些标识符之一或多个来选择首选表示。换言之,服务器希望用户代理进行被动协商以选择最适合其需求的表示(参见 第 12 节)。

如果服务器有首选的选择,服务器 SHOULD 在响应中生成一个包含首选选择 URI 引用的 Location 头字段。用户代理 MAY 使用 Location 字段值进行自动重定向。

对于除 HEAD 之外的方法,服务器 SHOULD 在 300 响应中生成包含一系列表示元数据和 URI 引用的内容,以便用户或用户代理可以从中选择最合适的。用户代理 MAY 如果能理解所提供的媒体类型而自动从该列表中选择。由于 HTTP 力求与内容定义保持正交,本规范并未定义用于自动选择的具体格式。实际上,表示通常以某种认为对用户代理可接受的易解析格式提供,或以某些常用的超文本格式提供,具体由共享设计或内容协商决定。

300 响应是启发式可缓存的;即除非方法定义或显式缓存控制另有指示(参见 CACHING 第 4.2.2 节)。

15.4.2. 301 Moved Permanently

301 (Moved Permanently) 状态码表示目标资源已被分配了一个新的永久 URI,今后对该资源的任何引用都应使用响应中包含的某个 URI。服务器建议具有链接编辑能力的用户代理可以永久替换对目标 URI 的引用为服务器发送的新引用之一。然而,除非用户代理正在主动编辑引用(例如进行内容创作)、连接已加密并且源服务器是所编辑内容的可信权威,否则通常会忽略此建议。

服务器 SHOULD 在响应中生成包含首选新永久 URI 的 Location 头字段。用户代理 MAY 使用 Location 字段值进行自动重定向。服务器的响应内容通常包含一个指向新 URI 的简短超文本说明。

301 响应是启发式可缓存的;即除非方法定义或显式缓存控制另有指示(参见 CACHING 第 4.2.2 节)。

15.4.3. 302 Found

302 (Found) 状态码表示目标资源临时位于不同的 URI 下。由于重定向可能会偶尔改变,客户端应继续在未来请求中使用目标 URI。

服务器 SHOULD 在响应中生成包含不同 URI 的 Location 头字段。用户代理 MAY 使用 Location 字段值进行自动重定向。服务器的响应内容通常包含一个指向不同 URI 的简短超文本说明。

15.4.4. 303 See Other

303 (See Other) 状态码表示服务器将用户代理重定向到另一个资源(由 Location 头字段指示),该资源意在作为对原始请求的间接响应。用户代理可以对该 URI 发起检索请求(如果使用 HTTP,则为 GET 或 HEAD),该请求可能也会被重定向,并最终将结果作为对原始请求的响应呈现。注意 Location 头字段中的新 URI 并不被视为与目标 URI 等价。

该状态码适用于任何 HTTP 方法。它主要用于允许 POST 操作的输出重定向用户代理到不同资源,因为这可以将 POST 响应对应的信息作为一个可单独识别、可书签和可缓存的资源提供。

对 GET 请求的 303 响应表示源服务器没有可以通过 HTTP 传输的目标资源的表示。然而,Location 字段值引用的资源是描述目标资源的资源,因此对该其他资源发起检索请求可能会得到对接收方有用的表示,而不意味着它代表原始目标资源。关于什么可以被表示、哪些表示是充分的以及什么可能是有用的描述不在 HTTP 的范畴内。

除对 HEAD 请求的响应外,303 响应的表示应包含一个指向 Location 头字段中同一 URI 的简短超文本说明。

15.4.5. 304 Not Modified

304 (Not Modified) 状态码表示已收到带条件的 GET 或 HEAD 请求,且如果不是因为条件为假,该请求本来会产生 200 (OK) 响应。换言之,服务器无需传输目标资源的表示,因为发起请求的客户端已经持有有效的表示;因此服务器指示客户端使用其已存的表示,仿佛它是 200 (OK) 响应的内容。

生成 304 响应的服务器 MUST 在响应中生成下列在对同一请求的 200 (OK) 响应中本应发送的任一头字段:

由于 304 响应的目标是当接收方已有一个或多个缓存表示时最小化信息传输,发送方 SHOULD NOT 生成除上述字段之外的表示元数据,除非该元数据用于指导缓存更新(例如,如果响应没有 ETag 字段,则 Last-Modified 可能有用)。

接收 304 响应的缓存的要求定义见 CACHING 第 4.3.4 节。如果条件请求源自一个外向客户端(例如具有自己缓存并向共享代理发送条件 GET 的用户代理),则代理 SHOULD 将 304 响应转发给该客户端。

304 响应以头部部分结束;它不能包含内容或尾部字段。

15.4.6. 305 Use Proxy

305 (Use Proxy) 状态码在先前版本的规范中定义,现在已不推荐使用(见旧 RFC 附录)。

15.4.7. 306 (Unused)

306 状态码在先前版本的规范中定义,现在不再使用,该代码被保留。

15.4.8. 307 Temporary Redirect

307 (Temporary Redirect) 状态码表示目标资源临时位于不同的 URI 下,并且用户代理在自动重定向到该 URI 时 MUST NOT 更改请求方法。由于重定向可能随时间改变,客户端应继续在未来请求中使用原始目标 URI。

服务器 SHOULD 在响应中生成包含不同 URI 的 Location 头字段。用户代理 MAY 使用 Location 字段值进行自动重定向。服务器的响应内容通常包含一个指向不同 URI 的简短超文本说明。

15.4.9. 308 Permanent Redirect

308 (Permanent Redirect) 状态码表示目标资源已被分配了一个新的永久 URI,今后对该资源的任何引用都应使用响应中包含的某个 URI。服务器建议具有链接编辑能力的用户代理可以永久替换对目标 URI 的引用为服务器发送的新引用之一。然而,除非用户代理正在主动编辑引用、连接已加密且源服务器为可信权威,否则通常会忽略此建议。

服务器 SHOULD 在响应中生成包含首选新永久 URI 的 Location 头字段。用户代理 MAY 使用 Location 字段值进行自动重定向。服务器的响应内容通常包含一个指向新 URI 的简短超文本说明。

308 响应是启发式可缓存的;即除非方法定义或显式缓存控制另有指示(参见 CACHING 第 4.2.2 节)。

15.5. 客户端错误 4xx

4xx(客户端错误)类状态码表示客户端似乎发生了错误。除非响应 HEAD 请求,服务器 SHOULD 发送一个包含错误情况解释并指示该情况是临时还是永久的表示(如果有的话)。这些状态码适用于任何请求方法。用户代理 SHOULD 向用户显示任何包含的表示。

15.5.1. 400 Bad Request

400 (Bad Request) 状态码表示服务器因为被认为是客户端错误的原因而无法或拒绝处理请求(例如:请求语法错误、无效的消息分界或欺骗性的请求路由)。

15.5.2. 401 Unauthorized

401 (Unauthorized) 状态码表示请求未被应用,因为缺少针对目标资源的有效认证凭证。生成 401 响应的服务器 MUST 在响应中发送至少包含一个适用于目标资源的质询的 WWW-Authenticate 头字段(见 第 11.6.1 节)。

如果请求包含了认证凭证,那么 401 响应表示这些凭证被拒绝。用户代理 MAY 使用新的或替换的 Authorization 头字段重复请求(见 第 11.6.2 节)。如果 401 响应包含与先前响应相同的质询,且用户代理已至少尝试过一次认证,则用户代理 SHOULD 向用户呈现所封装的表示,因为该表示通常包含相关的诊断信息。

15.5.3. 402 Payment Required

402 (Payment Required) 状态码被保留以备将来使用。

15.5.4. 403 Forbidden

403 (Forbidden) 状态码表示服务器理解该请求但拒绝执行它。希望公开说明请求为何被禁止的服务器可以在响应内容(如果有的话)中描述该原因。

如果请求中提供了认证凭证,服务器认为它们不足以授予访问权限。客户端 SHOULD NOT 自动使用相同凭证重复请求。客户端 MAY 使用新的或不同的凭证重复请求。然而,请求被禁止可能与凭证无关。

希望“隐藏”被禁止的目标资源当前存在性的源服务器 MAY 改为以 404 (Not Found) 状态码响应。

15.5.5. 404 Not Found

404 (Not Found) 状态码表示源服务器未找到目标资源的当前表示或不愿意透露其存在。404 状态码并未指示该缺失是临时还是永久;如果源服务器知道该情况可能是永久的(通过某种可配置手段),则应优先使用 410 (Gone) 状态码。

404 响应是启发式可缓存的;即除非方法定义或显式缓存控制另有指示(参见 CACHING 第 4.2.2 节)。

15.5.6. 405 Method Not Allowed

405 (Method Not Allowed) 状态码表示请求行中收到的方法为源服务器所知道,但目标资源不支持该方法。源服务器 MUST 在 405 响应中生成一个 Allow 头字段,包含目标资源当前支持的方法列表。

405 响应是启发式可缓存的;即除非方法定义或显式缓存控制另有指示(参见 CACHING 第 4.2.2 节)。

15.5.7. 406 Not Acceptable

406 (Not Acceptable) 状态码表示目标资源没有一个当前表示能被用户代理根据请求中收到的主动协商头字段(见 第 12.1 节)接受,且服务器不愿意提供默认表示。

服务器 SHOULD 生成包含可用表示特性和对应资源标识符列表的内容,供用户或用户代理从中选择最适合的项。用户代理 MAY 从该列表中自动选择最合适的选项。然而,本规范并未定义用于此类自动选择的标准,参见 第 15.4.1 节

15.5.8. 407 Proxy Authentication Required

407 (Proxy Authentication Required) 状态码类似于 401 (Unauthorized),但表示客户端需要进行认证以便使用代理来发起该请求。代理 MUST 发送一个包含适用于该代理的质询的 Proxy-Authenticate 头字段(见 第 11.7.1 节)。客户端 MAY 使用新的或替换的 Proxy-Authorization 头字段重复请求(见 第 11.7.2 节)。

15.5.9. 408 Request Timeout

408 (Request Timeout) 状态码表示服务器在其准备等待的时间内未收到完整的请求消息。

如果客户端有未完成的请求在传输中,它 MAY 重复该请求。如果当前连接不可用(例如在 HTTP/1.1 中请求定界丢失),则将使用新的连接。

15.5.10. 409 Conflict

409 (Conflict) 状态码表示请求由于与目标资源的当前状态存在冲突而无法完成。此代码用于用户可能能够解决冲突并重新提交请求的情形。服务器 SHOULD 生成包含足够信息以便用户识别冲突来源的内容。

冲突最有可能在对 PUT 请求的响应中发生。例如,如果使用了版本控制并且被 PUT 的表示包含与较早(第三方)请求所做更改冲突的更改,则源服务器可能使用 409 来指示无法完成请求。在这种情况下,响应表示可能包含用于根据修订历史合并差异的信息。

15.5.11. 410 Gone

410 (Gone) 状态码表示源服务器上已无法再访问目标资源,且此情况可能为永久性。如果源服务器不知道或无法确定该情况是否永久,应使用 404 (Not Found)

410 响应主要用于通过通知接收方该资源明确不可用并希望移除远程链接来帮助网页维护。这类事件在限时促销服务和不再与源服务器站点关联的个人资源中常见。不必对所有永久不可用的资源都标记为 “gone”,是否以及保持该标记多长时间由服务器所有者自行决定。

410 响应是启发式可缓存的;即除非方法定义或显式缓存控制另有指示(参见 CACHING 第 4.2.2 节)。

15.5.12. 411 Length Required

411 (Length Required) 状态码表示服务器拒绝接受没有定义 Content-Length 的请求(见 第 8.6 节)。客户端 MAY 在添加了包含请求内容长度的有效 Content-Length 头字段后重复该请求。

15.5.13. 412 Precondition Failed

412 (Precondition Failed) 状态码表示请求头字段中给定的一个或多个条件在服务器上检测时为假(见 第 13 节)。此响应允许客户端将前置条件施加到当前资源状态(其当前表示和元数据)上,从而在目标资源处于意外状态时阻止请求方法的应用。

15.5.14. 413 Content Too Large

413 (Content Too Large) 状态码表示服务器拒绝处理请求,因为请求内容比服务器愿意或能够处理的要大。服务器 MAY 中止请求(如果所用协议版本允许);否则,服务器 MAY 关闭连接。

如果此情况为临时性的,服务器 SHOULD 生成一个 Retry-After 头字段以指示该情况是临时的以及客户端可以在什么时间后尝试重试。

15.5.15. 414 URI Too Long

414 (URI Too Long) 状态码表示服务器拒绝为该请求提供服务,因为目标 URI 比服务器愿意解释的要长。此罕见情形通常发生于客户端将 POST 请求错误地转换为带长查询信息的 GET 请求、客户端陷入重定向的无限循环(例如重定向的 URI 前缀指向其自身的后缀)或服务器遭受企图利用潜在安全漏洞的攻击时。

414 响应是启发式可缓存的;即除非方法定义或显式缓存控制另有指示(参见 CACHING 第 4.2.2 节)。

15.5.16. 415 Unsupported Media Type

415 (Unsupported Media Type) 状态码表示源服务器拒绝为该请求提供服务,因为该内容对于目标资源上的此方法而言是未被支持的格式。

格式问题可能由请求指示的 Content-TypeContent-Encoding 引起,或由直接检查数据所发现的问题导致。

如果问题由不支持的内容编码引起,则应使用 Accept-Encoding 响应头字段(见 第 12.5.3 节)来指示哪些(如果有)内容编码在请求中会被接受。

另一方面,如果原因是媒体类型不被支持,则可以使用 Accept 响应头字段(见 第 12.5.1 节)来指示哪些媒体类型在请求中会被接受。

15.5.17. 416 Range Not Satisfiable

416 (Range Not Satisfiable) 状态码表示请求的 Range 头字段中所列范围集已被拒绝,原因要么是没有任何请求的范围是可满足的,要么是客户端请求了过多的小范围或重叠范围(可能构成拒绝服务攻击)。

每个范围单位定义其自身的范围集合要满足的要求。例如,第 14.1.2 节 定义了什么是可满足的字节范围集合。

对字节范围请求生成 416 响应的服务器 SHOULD 生成一个 Content-Range 头字段,指明所选表示的当前长度(见 第 14.4 节)。

例如:

HTTP/1.1 416 Range Not Satisfiable
Date: Fri, 20 Jan 2012 15:41:54 GMT
Content-Range: bytes */47022

15.5.18. 417 Expectation Failed

417 (Expectation Failed) 状态码表示请求的 Expect 头字段中给定的期望(见 第 10.1.1 节)至少不能被某一入站服务器满足。

15.5.19. 418 (Unused)

[RFC2324] 是一个愚人节 RFC,讽刺了 HTTP 的各种滥用;其中一次滥用定义了一个应用特定的 418 状态码,作为玩笑被部署得颇多,以致该代码无法用于未来的任何用途。

因此,418 状态码在 IANA HTTP 状态码注册表中被保留。这表示该状态码当前不能分配给其他应用。如果将来需要使用(例如,4NN 状态码耗尽),可以将其重新分配。

15.5.20. 421 Misdirected Request

421 (Misdirected Request) 状态码表示请求被发送到了一个无法或不愿为目标 URI 产生权威响应的服务器。源服务器(或代表源服务器的网关)发送 421 以拒绝一个与服务器配置的 origin(见 第 4.3.1 节)不匹配或与接收该请求的连接上下文不匹配的目标 URI(见 第 7.4 节)。

收到 421 (Misdirected Request) 响应的客户端 MAY 在不同的连接上重试该请求(无论请求方法是否幂等),例如针对目标资源的 origin 建立一个新的专用连接,或通过替代服务(见 [ALTSVC])。

代理 MUST NOT 生成 421 响应。

15.5.21. 422 Unprocessable Content

422 (Unprocessable Content) 状态码表示服务器理解请求内容的媒体类型(因此使用 415 (Unsupported Media Type) 不合适),且请求内容的语法是正确的,但无法处理所包含的指令。例如,如果 XML 请求内容是格式正确(即语法无误)但在语义上有错误的 XML 指令,则可以发送此状态码。

15.5.22. 426 Upgrade Required

426 (Upgrade Required) 状态码表示服务器拒绝使用当前协议执行请求,但在客户端升级到不同协议后可能愿意执行请求。服务器 MUST 在 426 响应中发送一个 Upgrade 头字段以指示所需协议(见 第 7.8 节)。

示例:

HTTP/1.1 426 Upgrade Required
Upgrade: HTTP/3.0
Connection: Upgrade
Content-Length: 53
Content-Type: text/plain

This service requires use of the HTTP/3.0 protocol.

15.6. 服务器错误 5xx

5xx(服务器错误)类状态码表示服务器意识到其发生了错误或无法执行请求方法。除非响应 HEAD 请求,服务器 SHOULD 发送包含错误情况解释以及该情况是临时还是永久的表示。用户代理 SHOULD 向用户展示任何包含的表示。这些状态码适用于任何请求方法。

15.6.1. 500 Internal Server Error

500 (Internal Server Error) 状态码表示服务器遇到了意外情况,导致无法完成请求。

15.6.2. 501 Not Implemented

501 (Not Implemented) 状态码表示服务器不支持完成请求所需的功能。当服务器无法识别请求方法且无法为任何资源支持该方法时,应返回此响应。

501 响应是启发式可缓存的;即除非方法定义或显式缓存控制另有指示(参见 CACHING 第 4.2.2 节)。

15.6.3. 502 Bad Gateway

502 (Bad Gateway) 状态码表示服务器在作为网关或代理时,从其访问的上游服务器收到无效响应,从而无法完成请求。

15.6.4. 503 Service Unavailable

503 (Service Unavailable) 状态码表示服务器当前由于临时过载或计划内维护而无法处理请求,这种情况可能在一段延迟后得到缓解。服务器 MAY 发送一个 Retry-After 头字段(见 第 10.2.3 节)以建议客户端在重试之前等待的适当时间。

15.6.5. 504 Gateway Timeout

504 (Gateway Timeout) 状态码表示服务器在作为网关或代理时,未能及时从其需要访问以完成请求的上游服务器接收到响应。

15.6.6. 505 HTTP Version Not Supported

505 (HTTP Version Not Supported) 状态码表示服务器不支持或拒绝支持请求消息中使用的 HTTP 主版本。服务器表明它无法或不愿使用与客户端相同的主版本来完成请求,除非以该错误消息形式。服务器 SHOULD 为 505 响应生成一个描述为何该版本不被支持以及该服务器支持哪些其他协议的表示。

16. 扩展 HTTP

HTTP 定义了若干通用的扩展点,可用于在不引入新版本的情况下向协议引入功能,包括方法、状态码、字段名,以及已定义字段内的进一步可扩展点,例如认证方案和缓存指令(参见 第 5.2.3 节 中的 Cache-Control 扩展,见 [CACHING])。由于 HTTP 的语义不随版本编号,这些扩展点是持久的;所用协议版本不会影响它们的语义。

与版本无关的扩展不应依赖于或与所使用的特定协议版本交互。当不可避免时,需要仔细考虑该扩展如何跨版本互操作。

此外,HTTP 的特定版本可能具有其自己的可扩展点,例如 HTTP/1.1 中的传输编码(见 第 6.1 节,参见 [HTTP/1.1])以及 HTTP/2 的 SETTINGS 或帧类型(参见 [HTTP/2])。这些扩展点是特定于它们所在的协议版本的。

版本特定的扩展不得在未被该协议元素显式允许的情况下,覆盖或修改与版本无关的机制或扩展点(例如方法或头字段)的语义。例如,CONNECT 方法(见 第 9.3.6 节)允许这样做。

这些指南确保即使路径的部分实现了不同版本的 HTTP,协议也能正确且可预测地运行。

16.1. 方法可扩展性

16.1.1. 方法注册表

“超文本传输协议 (HTTP) 方法注册表”由 IANA 在 https://www.iana.org/assignments/http-methods 维护,用于注册 方法 名称。

HTTP 方法注册 MUST 包含下列字段:

  • 方法名称(参见 第 9 节
  • 是否为 Safe(“yes” 或 “no”,参见 第 9.2.1 节
  • 是否为 Idempotent(“yes” 或 “no”,参见 第 9.2.2 节
  • 指向规范文本的指针

要添加到此命名空间的值需要进行 IETF 评审(参见 [RFC8126]第 4.8 节)。

16.1.2. 关于新方法的考虑

标准化的方法是通用的;也就是说,它们可能适用于任何资源,而不仅限于某一媒体类型、资源种类或应用。因此,建议在非针对单一应用或数据格式的文档中注册新方法,因为正交的技术应由正交的规范来描述。

由于消息解析(见 第 6 节)需要独立于方法语义(HEAD 响应除外),新方法的定义不能更改解析算法或禁止请求或响应消息中存在内容。新方法的定义可以通过要求 Content-Length 头字段值为 "0" 明确限定只允许零长度内容。

同样,新方法不能使用 CONNECT 和 OPTIONS 所允许的特殊 host:port 或星号形式的请求目标(分别参见 CONNECTOPTIONS;见 第 7.1 节)。需要完整形式的 URI 作为目标 URI,这意味着要么以绝对形式发送请求目标,要么像对其他方法那样从请求上下文重构目标 URI。

新方法定义需要指明它是否为 Safe(参见 第 9.2.1 节)、是否为 Idempotent(参见 第 9.2.2 节)、是否可缓存(参见 第 9.2.3 节)、与请求内容(若有)相关的语义,以及方法对头字段或状态码语义所做的任何细化。如果新方法可缓存,其定义应描述缓存如何以及在何种条件下存储响应并用以满足后续请求。新方法应说明是否可被置为有条件请求(参见 第 13.1 节),如果可以,服务器在条件为假时如何响应。同样,如果新方法可能与部分响应语义(见 第 14.2 节)有某种用途,也应在定义中加以说明。

16.2. 状态码可扩展性

16.2.1. 状态码注册表

“超文本传输协议 (HTTP) 状态码注册表”由 IANA 在 https://www.iana.org/assignments/http-status-codes 维护,用于注册 状态码 编号。

注册条目 MUST 包含下列字段:

  • 状态码(3 位数字)
  • 简短描述
  • 指向规范文本的指针

要添加到 HTTP 状态码命名空间的值需要 IETF 评审(参见 [RFC8126]第 4.8 节)。

16.2.2. 关于新状态码的考虑

当需要表达当前状态码未定义的响应语义时,可以注册新的状态码。状态码是通用的;它们可能适用于任何资源,而不仅限于某一媒体类型、资源种类或 HTTP 的某一应用。因此,建议在非针对单一应用的文档中注册新状态码。

新状态码必须属于第 第 15 节 定义的类别之一。为使现有解析器能够处理响应消息,新状态码不能禁止内容,尽管它们可以强制要求零长度内容。

尚未广泛部署的新状态码提案应避免为代码分配具体数字,直到明确达成注册共识;早期草案可以使用诸如 "4NN" 或 "3N0" .. "3N9" 之类的表示来指示拟议状态码的类别,而不会过早占用编号。

新状态码的定义应解释导致包含该状态码的响应的请求条件(例如,请求头字段组合和/或方法),以及对响应头字段的任何依赖(例如,哪些字段是必需的、哪些字段可以修改语义、以及在与新状态码共同使用时哪些字段语义会被进一步细化)。

默认情况下,状态码仅适用于其所处响应对应的请求。如果某个状态码适用于更大的适用范围——例如,适用于该资源的所有请求或服务器的所有请求——必须明确说明。在这样做时,应注意并非所有客户端都能一致地应用更大范围,因为它们可能并不了解该新状态码。

新最终状态码的定义应指定它是否为启发式可缓存的。注意任何具有最终状态码的响应在具有显式新鲜度信息时都可以被缓存。被定义为启发式可缓存的状态码允许在没有显式新鲜度信息的情况下被缓存。类似地,状态码的定义可以在使用 must-understand 缓存指令时对缓存行为施加约束。有关更多信息,请参阅 [CACHING]

最后,新状态码的定义应指明该内容是否与某个已识别的资源存在隐含关联(参见 第 6.4.2 节)。

16.3. 字段可扩展性

HTTP 最广泛使用的可扩展点是新头字段和尾部字段(trailer fields)的定义。

可以定义新字段,使得当接收者理解它们时,它们会覆盖或增强对先前已定义字段的解释、对请求评估定义前置条件,或细化响应的含义。

然而,定义一个字段并不能保证其被部署或被接收者识别。大多数字段被设计为接收者可以安全地忽略(但向下游转发)任何未识别的字段。在其他情况下,发送方对某字段的理解能力可能由其先前通信指示,例如其所发送的协议版本或先前消息中发送的字段,或其使用的特定媒体类型。同样,通过 OPTIONS 请求或与已定义的 well-known URI 交互也可能能够直接检查支持情况(如果与所引入字段一并定义了这样的检查)。参见 [RFC8615]

16.3.1. 字段名称注册表

“超文本传输协议 (HTTP) 字段名注册表”定义了 HTTP 字段名的命名空间。

任何一方都可以请求注册 HTTP 字段。创建新 HTTP 字段时应考虑的事项见 第 16.3.2 节

“超文本传输协议 (HTTP) 字段名注册表”位于 https://www.iana.org/assignments/http-fields/。可以按照该处说明提交注册请求,或发送电子邮件到 "ietf-http-wg@w3.org" 邮件列表。

字段名由指定专家(由 IESG 或其代表任命)在建议下注册。状态为 'permanent' 的字段为 Specification Required(参见 [RFC8126]第 4.6 节)。

注册请求包括下列信息:

字段名:
请求的字段名。它 MUST 符合在 第 5.1 节 中定义的 field-name 语法,并且 SHOULD 限制为仅使用字母、数字和连字符 ('-') 字符,并以字母为首字符。
状态:
“permanent”、“provisional”、“deprecated” 或 “obsoleted”。
规范文档:
指定该字段的文档引用,最好包含可用于检索文档副本的 URI。对于临时注册可选但鼓励提供。也可包括相关部分的指示,但不是必需的。

可选项:

注释:
附加信息,例如有关保留条目的说明。

专家可以与社区协商后定义在注册表中需要收集的额外字段。

标准定义的名称具有 "permanent" 状态。如果专家(与社区协商)发现某些其他名称已在使用,也可以将其注册为 permanent。其他名称应注册为 "provisional"。

如果专家(与社区协商)发现临时条目未被使用,专家可以移除该条目。专家可以随时将临时条目的状态更改为 permanent。

注意,如果专家认定某个未注册名称已被广泛部署且否则不太可能及时注册,则该名称可以由第三方(包括专家)注册。

16.3.2. 关于新字段的考虑

HTTP 头和尾部字段是协议中广泛使用的扩展点。尽管它们可以以临时方式使用,但打算广泛使用的字段需要被仔细记录以确保互操作性。

特别是,定义新字段的规范作者被建议考虑并在适当时记录下列方面:

  • 该字段可以在何种条件下使用;例如仅在响应或请求中、在所有消息中、仅在对特定请求方法的响应中等。
  • 字段语义是否由其上下文进一步细化,例如与某些请求方法或状态码共同使用时。
  • 所传达信息的适用范围。默认情况下,字段仅适用于其所关联的消息,但一些响应字段被设计为适用于资源的所有表示、资源本身或更广泛的范围。扩展响应字段适用范围的规范需要仔细考虑内容协商、适用时间范围以及(在某些情况下)多租户服务器部署等问题。
  • 在何种条件下中间件被允许插入、删除或修改该字段的值。
  • 该字段是否允许出现在 trailers 中;默认情况下不允许(见 第 6.5.1 节)。
  • 该字段名是否应列在 Connection 头字段中(即该字段是否为 hop-by-hop;见 第 7.6.1 节)。
  • 该字段是否引入任何额外的安全考虑,例如披露与隐私相关的数据。

请求头字段还有附加的考虑,需要在默认行为不适用时记录:

  • 是否适合在响应的 Vary 头字段中列出该字段名(例如,当该请求头字段被源服务器的内容选择算法使用时;见 第 12.5.5 节)。
  • 当在 PUT 请求中接收到该字段时是否应将其存储(见 第 9.3.4 节)。
  • 在因安全考虑而自动重定向请求时是否应移除该字段(见 第 15.4 节)。
16.3.2.1. 关于新字段名的考虑

定义新字段的规范作者被建议选择简短但描述性的字段名。简短的名称可避免不必要的数据传输;描述性的名称可避免混淆和对可能有更广泛用途的名称的“抢占”。

为此,鼓励将限定用途的字段(例如仅限于单一应用或用例的头)使用包含该用途(或缩写)作为前缀的名称;例如,如果 Foo 应用需要一个 Description 字段,它可能使用 "Foo-Desc";"Description" 过于通用,而 "Foo-Description" 则过长。

虽然 field-name 语法允许任何 token 字符,但在实践中一些实现对字段名接受的字符有限制。为实现互操作性,新字段名 SHOULD 将字符限制为字母数字、"-" 和 ".",并且 SHOULD 以字母开头。例如,当通过非 HTTP 网关接口传递时,下划线 ("_") 字符可能会带来问题(参见 第 17.10 节)。

字段名不应以 "X-" 为前缀;详情见 [BCP178]

其他前缀有时在 HTTP 字段名中使用;例如 "Accept-" 在许多内容协商头中使用,而 "Content-" 的用途如 第 6.4 节 所述。这些前缀只是帮助识别字段的用途,并不触发自动处理。

16.3.2.2. 关于新字段值的考虑

定义新 HTTP 字段的一项主要任务是指定字段值语法:发送方应生成何种值,接收方应如何从接收到的数据中推断语义。

建议(但不强制)作者使用本规范中的 ABNF 规则或 [RFC8941] 中的规则来定义新字段值的语法。

作者应仔细考虑多行字段值的组合如何影响它们(参见 第 5.3 节)。由于发送方可能错误地发送多个值,并且中间件和 HTTP 库可能会自动合并,这适用于所有字段值——即便只预期单个值也应考虑。

因此,建议作者对包含逗号的值进行分隔或编码(例如使用 quoted-string 规则或 [RFC8941] 的 String 数据类型,或字段特定的编码)。这可以确保字段数据中的逗号不会与用于分隔列表值的逗号混淆。

例如,Content-Type 字段值只允许在引用字符串中出现逗号,这能在存在多个值时可靠地解析。Location 字段值则是反例:由于 URI 可能包含逗号,无法可靠地区分包含逗号的单个值与两个值。

对于只允许单值的字段(参见 第 5.5 节),作者还应记录在出现多个成员时如何处理消息(一个合理的默认是忽略该字段,但这并不总是正确的选择)。

16.4. 认证方案可扩展性

16.4.1. 认证方案注册表

“超文本传输协议 (HTTP) 认证方案注册表”定义了用于质询和凭证中的认证方案命名空间。该注册表维护在 https://www.iana.org/assignments/http-authschemes

注册条目 MUST 包含下列字段:

  • 认证方案名称
  • 指向规范文本的指针
  • 注释(可选)

要添加到此命名空间的值需要 IETF 评审(参见 [RFC8126]第 4.8 节)。

16.4.2. 关于新认证方案的考虑

HTTP 认证框架的若干方面对新认证方案的工作方式施加了约束:

  • HTTP 认证假定为无状态:用于认证请求的所有信息 MUST 在请求中提供,而不应依赖服务器记住先前请求。基于或绑定到底层连接的认证超出本规范范围,除非采取措施确保该连接不能被除已认证用户外的任何一方使用,否则固有上是有缺陷的(参见 第 3.3 节)。

  • 认证参数 "realm" 保留用于按照 第 11.5 节 所述定义保护空间。新方案 MUST NOT 以与该定义不兼容的方式使用该参数。

  • "token68" 表示法为了与现有认证方案兼容而引入,并且在每个质询或凭证中只能使用一次。因此,新方案应使用 auth-param 语法,否则将来扩展变得不可能。

  • 质询和凭证的解析由本规范定义,新认证方案不能修改解析方式。当使用 auth-param 语法时,所有参数应支持 token 和 quoted-string 语法,并且应在解析后(即经过 quoted-string 处理)对字段值定义语法约束。这是必要的,以便接收方可以使用对所有认证方案通用的通用解析器。

    注意:将 "realm" 参数的值语法限制为 quoted-string 是一个不好的设计决策,新参数不应重复此错误。

  • 新方案的定义应规定对未知扩展参数的处理。通常,“必须忽略”规则优于“必须理解”规则,因为否则在存在遗留接收方时很难引入新参数。此外,最好描述定义新参数的策略(例如“更新规范”或“使用此注册表”)。

  • 认证方案需要说明它们是否可用于源服务器认证(即使用 WWW-Authenticate)和/或代理认证(即使用 Proxy-Authenticate)。

  • Authorization 头字段中携带的凭证特定于用户代理,因此在出现该请求的范围内,它们对 HTTP 缓存具有与 "private" 缓存响应指令相同的效果(参见 第 5.2.2.7 节)。

    因此,选择不在 Authorization 头字段中携带凭证(例如使用新定义的头字段)的新认证方案,需要通过强制使用缓存响应指令(例如 "private")来明确禁止缓存。

  • 使用 Authentication-InfoProxy-Authentication-Info 或任何其他与认证相关的响应头字段的方案,需要考虑并记录相关的安全注意事项(参见 第 17.16.4 节)。

16.5. 范围单位可扩展性

16.5.1. 范围单位注册表

“HTTP 范围单位注册表”定义了范围单位名称的命名空间并指向其相应规范。该注册表维护在 https://www.iana.org/assignments/http-parameters

注册 HTTP 范围单位 MUST 包含下列字段:

  • 名称
  • 描述
  • 指向规范文本的指针

要添加到此命名空间的值需要 IETF 评审(参见 [RFC8126]第 4.8 节)。

16.5.2. 关于新范围单位的考虑

其他范围单位,例如基于格式的边界如页、节、记录、行或时间,可能在 HTTP 中用于特定应用,但在实践中并不常用。替代范围单位的实现者应考虑它们如何与内容编码和通用中间件协同工作。

16.6. 内容编码可扩展性

16.6.1. 内容编码注册表

“HTTP 内容编码注册表”由 IANA 在 https://www.iana.org/assignments/http-parameters/ 维护,用于注册 content-coding 名称。

内容编码注册 MUST 包含下列字段:

  • 名称
  • 描述
  • 指向规范文本的指针

内容编码的名称 MUST NOT 与传输编码的名称重叠(参见 “HTTP Transfer Coding Registry” 位于 https://www.iana.org/assignments/http-parameters/),除非编码转换是相同的(例如第 第 8.4.1 节 定义的压缩编码的情形)。

要添加到此命名空间的值需要 IETF 评审(参见 RFC8126 第 4.8 节)并且 MUST 符合在 第 8.4.1 节 中定义的内容编码目的。

16.6.2. 关于新内容编码的考虑

新的内容编码应尽可能具有自描述性,其可选参数应可在编码格式内部发现,而不是依赖可能在传输中丢失的外部元数据。

16.7. Upgrade Token 注册表

“超文本传输协议 (HTTP) Upgrade Token 注册表”定义了在 Upgrade 头字段中用于标识协议的 protocol-name 令牌的命名空间。该注册表维护在 https://www.iana.org/assignments/http-upgrade-tokens

每个已注册的协议名称都关联联系人信息和一组可选的规范,详述在升级后如何处理连接。

注册按“先到先得”方式进行(参见 RFC8126 第 4.4 节),并受下列规则约束:

  1. 协议名称令牌一旦注册,永久保留。
  2. 协议名称令牌不区分大小写,并以建议的大小写注册以供发送方生成。
  3. 注册 MUST 指明注册责任方。
  4. 注册 MUST 指明联系点。
  5. 注册 MAY 指明与该令牌关联的一组规范。这些规范不必公开可用。
  6. 注册 SHOULD 在注册时指明与该令牌相关的预期 "protocol-version" 令牌集。
  7. 责任方 MAY 随时更改注册。IANA 会保留所有此类更改的记录,并在请求时提供。
  8. IESG MAY 重新分配协议令牌的责任方。通常仅在无法联系责任方时使用。

17. 安全考虑

本节旨在告知开发者、信息提供者和用户与 HTTP 语义及其在互联网上传输信息时相关的已知安全问题。与缓存有关的考虑在 第7节[CACHING] 中讨论,而与 HTTP/1.1 消息语法和解析相关的考虑在 第11节[HTTP/1.1] 中讨论。

下面列出的考虑事项并不详尽。大多数与 HTTP 语义相关的安全问题都与保护服务器端应用(位于 HTTP 接口之后的代码)、保护用户代理处理通过 HTTP 接收的内容或一般安全使用互联网有关,而不是协议本身的安全性。URI 的安全考虑(它们对 HTTP 操作至关重要)在 第7节[URI] 中讨论。多个组织维护关于 Web 应用安全的专题信息和当前研究的链接(例如 [OWASP])。

17.1. 建立权威性

HTTP 依赖于“权威响应”的概念:即由(或在原点服务器标识的目标 URI 的指示下)确定为针对该请求在响应消息产生时目标资源状态下最合适的响应。

当在 authority 组件中使用注册名称时,"http" URI 方案(第4.2.1节)依赖于用户的本地名称解析服务来确定在哪里可以找到权威响应。这意味着对用户网络主机表、已缓存名称或名称解析库的任何攻击都会成为对“http” URI 建立权威性的攻击途径。同样,用户为域名服务(DNS)选择的服务器以及它从中获得解析结果的服务器层级,可能会影响地址映射的真实性;DNS 安全扩展(DNSSEC,[RFC4033])是提高真实性的一种方式,使用更安全传输协议进行 DNS 请求的各种机制也是如此。

此外,在获得 IP 地址之后,为 "http" URI 建立权威性仍然容易受到对互联网协议路由的攻击。

"https" 方案(第4.2.2节)旨在防止(或至少揭示)许多此类针对建立权威性的潜在攻击,前提是协商的连接已被保护且客户端正确验证通信服务器的身份与目标 URI 的 authority 组件相匹配(第4.3.4节)。正确实现此类验证可能很困难(参见 [Georgiev])。

给定原点服务器的权威性可以通过协议扩展进行委派;例如,[ALTSVC]。同样,通过像 [RFC8336] 这样的协议扩展,可以更改被视为具权威性的服务器集合。

从非权威来源(例如共享代理缓存)提供响应通常对提高性能和可用性很有用,但只有在该来源可被信任或不被信任的响应可以被安全使用的情况下才适用。

不幸的是,将权威性传达给用户可能很困难。例如,网络钓鱼(phishing) 是对用户对权威感知的攻击,通过在超文本中呈现类似的品牌可能会误导这种感知,userinfo 可能会使 authority 组件混淆(参见 第4.2.1节)。用户代理可以通过在执行操作前让用户容易地检查目标 URI、在存在 userinfo 时显著区分(或拒绝)它,并且在引用文档来自未知或不受信任来源时不发送已存储的凭证和 cookies,从而减少网络钓鱼攻击的影响。

17.2. 中间人风险

HTTP 中的中间人天生处于路径上,易于实施中间路径攻击。中间人所在系统的妥协可能导致严重的安全和隐私问题。中间人可能访问与安全相关的信息、关于单个用户和组织的个人信息以及属于用户和内容提供者的专有信息。被攻陷的中间人,或以不考虑安全和隐私的方式实现或配置的中间人,可能被用于实施各种潜在的攻击。

包含共享缓存的中间人尤其容易受到缓存中毒攻击,如 第7节[CACHING] 中所述。

实现者需要考虑其设计和编码决策的隐私与安全影响,以及他们向运营者提供的配置选项(尤其是默认配置)。

中间人的可信程度取决于其运营下的人和政策;HTTP 无法解决这个问题。

17.3. 基于文件名和路径名的攻击

原点服务器经常使用其本地文件系统来管理从目标 URI 到资源表征的映射。大多数文件系统并非为防范恶意文件或路径名而设计。因此,原点服务器在将目标资源映射到文件、文件夹或目录时,需要避免访问对系统具有特殊意义的名称。

例如,UNIX、Microsoft Windows 和其他操作系统使用“..”作为路径组件来表示当前目录之上的目录级别,并使用具有特殊名称的路径或文件名将数据发送到系统设备。类似的命名约定可能存在于其他类型的存储系统中。同样,本地存储系统在处理无效或意外字符、分解字符的重组以及对不区分大小写名称的大小写规范化时,往往在安全性方面不如用户友好性。

基于这些特殊名称的攻击往往集中于拒绝服务(例如,指示服务器从 COM 端口读取)或泄露不应被提供的配置和源文件。

17.4. 基于命令、代码或查询注入的攻击

原点服务器经常使用 URI 中的参数来标识系统服务、选择数据库条目或选择数据源。但是,请求中接收的数据不能被信任。攻击者可以构造任意请求数据元素(方法、目标 URI、首部字段或内容),使其包含在通过命令调用、语言解释器或数据库接口传递时可能被误解为命令、代码或查询的数据。

例如,SQL 注入是一种常见攻击,其中在目标 URI 或首部字段的某些部分(例如 HostReferer 等)中插入额外的查询语言。如果接收到的数据直接用于 SELECT 语句,则查询语言可能被解释为数据库命令而不是简单的字符串值。这类实现漏洞非常常见,尽管其实易于防止。

一般来说,资源实现应该避免在会被处理或解释为指令的上下文中使用请求数据。参数应该与固定字符串进行比较,并根据该比较采取行动,而不是通过不适合处理不受信数据的接口传递。对于非基于固定参数的接收数据,应仔细过滤或编码以避免被误解。

类似的考虑也适用于请求数据在被存储后再处理的情况,例如在日志文件、监控工具中,或当包含在允许嵌入脚本的数据格式中时。

17.5. 基于协议元素长度的攻击

由于 HTTP 主要使用文本、字符分隔的字段,解析器通常容易受到基于发送非常长(或非常慢)数据流的攻击,尤其是在实现期望一个无预定义长度的协议元素时(第2.3节)。

为促进互操作性,对字段的最小大小限制提出了具体建议(第5.4节)。这些是最小建议,选取为即便资源有限的实现也能支持;预计大多数实现会选择显著更高的限制。

服务器可以拒绝目标 URI 过长的消息(第15.5.15节)或请求内容过大的消息(第15.5.14节)。与容量限制相关的其他状态码已由对 HTTP 的扩展定义 [RFC6585]

接收方应谨慎限制其处理其他协议元素的程度,包括(但不限于)请求方法、响应状态短语、字段名、数值和分块长度。未能限制此类处理可能导致由于缓冲区或算术溢出而被任意代码执行,并增加对拒绝服务攻击的脆弱性。

17.6. 使用共享字典压缩的攻击

对加密协议的一些攻击利用动态压缩产生的长度差异来揭示机密信息;例如 [BREACH]。这些攻击依赖于在攻击者可控内容与机密信息之间创建冗余,以便当动态压缩算法对两者使用相同字典时,当攻击者可控内容匹配机密内容的部分时,压缩效率会更高。

HTTP 消息可以通过多种方式进行压缩,包括使用 TLS 压缩、内容编码、传输编码以及其他扩展或版本特定的机制。

对此风险最有效的缓解措施是禁用敏感数据的压缩,或严格将敏感数据与攻击者可控数据分离,以使它们不能共享相同的压缩字典。通过谨慎设计,压缩方案可以被设计为在有限用例中不被认为可被利用,例如 HPACK([HPACK])。

17.7. 个人信息的泄露

客户端通常可以接触到大量个人信息,包括用户为与资源交互提供的信息(例如用户的姓名、位置、邮件地址、密码、加密密钥等)以及关于用户浏览活动随时间的相关信息(例如历史记录、书签等)。实现需要防止无意泄露个人信息。

17.8. 服务器日志信息的隐私

服务器处于能够随时间保存关于用户请求的个人数据的位置,这些数据可能识别其阅读模式或感兴趣的主题。特别是,在中间人处收集的日志信息通常包含跨多个站点的用户代理交互历史,可以追溯到单个用户。

HTTP 日志信息在本质上是机密的;其处理通常受法律和法规的约束。日志信息需要安全存储并在分析时遵循适当的指南。对单个条目中个人信息的匿名化有所帮助,但通常不足以防止基于与其他访问特征的关联而重新识别真实日志痕迹。因此,即使键是伪匿名的,键定向到特定客户端的访问痕迹也不安全以发布。

为将被盗或意外公开的风险降到最低,应在不再需要这些信息以支持安全、审计或欺诈控制的运营需要时,从日志信息中清除个人可识别信息,包括用户标识符、IP 地址和用户提供的查询参数。

17.9. URI 中敏感信息的泄露

URI 是用于共享而非保密的,即便它们标识受保护资源。URI 常在显示屏上显示、在页面打印时加入到模板中并存储在各种不受保护的书签列表中。许多服务器、代理和用户代理在可能被第三方看到的地方记录或显示目标 URI。因此,在 URI 中包含敏感的、可识别个人的信息或可能带来泄露风险的信息是不明智的。

当应用使用客户端机制根据用户提供的信息(例如使用 GET 的表单的查询字段)构造目标 URI 时,可能会提供不适合在 URI 中披露的敏感数据。在这种情况下通常更倾向于使用 POST,因为它通常不构造 URI;相反,表单的 POST 将潜在敏感数据传输在请求内容中。然而,这会阻碍缓存并为本来是安全的请求使用不安全的方法。可选的解决方法包括在构造 URI 之前变换用户提供的数据或过滤数据仅包含不敏感的常见值。同样,将查询结果重定向到不同(服务器生成的)URI 可以从后续链接中移除潜在敏感数据,并为以后重用提供可缓存的响应。

由于 Referer 首部字段会告知目标站点导致请求的上下文,它可能会暴露关于用户即时浏览历史的信息以及可能出现在引用资源 URI 中的任何个人信息。关于 Referer 首部字段的限制在 第10.1.3节 中有所描述,以解决其部分安全考虑。

17.10. 应用对字段名的处理

服务器经常使用非 HTTP 网关接口和框架来处理接收的请求并生成响应内容。出于历史原因,这类接口经常将接收到的字段名作为外部变量名传递,使用适合环境变量的名称映射。

例如,公共网关接口(CGI)对协议特定元变量的映射,由 第4.1.18节[RFC3875] 定义,应用于不对应于 CGI 标准变量之一的接收首部字段;该映射由在每个名称前添加 "HTTP_" 并将所有连字符 ("-") 改为下划线 ("_") 组成。为了简化将应用从一个平台迁移到另一个平台,许多其他应用框架也继承了相同的映射。

例如,在 CGI 中,接收到的 Content-Length 字段将作为元变量 "CONTENT_LENGTH" 传递,其字符串值与接收到的字段值匹配。相比之下,接收到的 "Content_Length" 首部字段将作为协议特定元变量 "HTTP_CONTENT_LENGTH" 传递,这可能导致一些混淆,如果应用错误地读取了协议特定元变量而不是默认的那个。(这种历史做法就是为什么 第16.3.2.1节 劝阻创建包含下划线的新字段名。)

不幸的是,将字段名映射到不同接口名如果映射不完整或含糊,可能导致安全漏洞。例如,如果攻击者发送名为 "Transfer_Encoding" 的字段,天真的接口可能将其映射到与 "Transfer-Encoding" 字段相同的变量名,导致潜在的请求走私漏洞(第11.2节[HTTP/1.1])。

为缓解相关风险,执行此类映射的实现建议使映射对接收到的名称的所有可能八位组范围(包括那些被 HTTP 语法不鼓励或禁止的八位组)明确且完整。例如,具有不寻常名称字符的字段可能导致请求被阻止、特定字段被移除,或该名称以不同前缀传递以将其与其他字段区分开来。

17.11. 重定向后片段标识符的泄露

尽管在 URI 引用中使用的片段标识符不会在请求中发送,实施者应注意它们会对用户代理以及任何作为响应结果运行的扩展或脚本可见。特别地,当发生重定向且原始请求的片段标识符被 Location 中的新引用继承(第10.2.2节)时,这可能会导致一个站点的片段泄露给另一个站点。如果第一个站点在片段中使用个人信息,应确保对其他站点的重定向包含(可能为空的)片段组件以阻止该继承。

17.12. 产品信息的泄露

User-Agent第10.1.5节)、Via第7.6.3节)和 Server第10.2.4节)首部字段经常揭示关于各自发送方的软件系统的信息。理论上,这可以使攻击者更容易利用已知的安全漏洞;实际上,攻击者往往会尝试所有潜在的漏洞,而不管所使用的软件版本如何。

作为网络防火墙门户的代理在传输可能识别防火墙后主机的首部信息时应采取特殊预防措施。Via 首部字段允许中间人用化名替换敏感的机器名。

17.13. 浏览器指纹识别

浏览器指纹识别是一组用于通过用户代理随时间的唯一特征识别特定用户代理的技术。这些特征可能包括与其使用底层传输协议、功能能力和脚本环境相关的信息,但在此特别关注的是可能通过 HTTP 传达的一组唯一特征。指纹识别被视为隐私问题,因为它使得在没有用户对其他形式数据收集(例如 cookies)相应控制的情况下跟踪用户代理的行为成为可能(参见 [Bujlow])。许多通用用户代理(即 Web 浏览器)已采取措施以减少其指纹。

有若干请求首部字段可能会向服务器泄露足以使指纹识别成为可能的独特信息。最明显的是 From 首部字段,尽管预计只有在用户希望自我标识时才会发送 From。同样,Cookie 首部字段被有意设计为启用重新识别,因此当用户代理配置禁用或限制 cookies 时,指纹识别问题才适用。

User-Agent 首部字段可能包含足够的信息来唯一标识特定设备,通常是与其他特征结合时,尤其是当用户代理发送过多关于用户系统或扩展的详细信息时。然而,用户最不期望的唯一信息来源是 proactive negotiation第12.1节),包括 AcceptAccept-CharsetAccept-EncodingAccept-Language 首部字段。

除了指纹识别问题外,对 Accept-Language 首部字段的详细使用可能会泄露用户可能认为是私密的内容。例如,了解给定的语言集合可能与某一特定族群的成员身份高度相关。为限制此类隐私泄露,用户代理可采取的做法是仅对经过显式许可的网站发送 Accept-Language,可能通过在检测到 Vary 首部字段表明语言协商可能有用时进行交互来实现。

在使用代理以增强隐私的环境中,用户代理应在发送主动协商首部字段时保持保守。提供高度首部字段可配置性的通用用户代理应告知用户如果提供过多细节可能导致的隐私丧失。作为极端隐私措施,代理可以在转发请求时过滤主动协商首部字段。

17.14. 验证器保留

本规范定义的验证器并不旨在确保表征的有效性、防范恶意更改或检测中间路径攻击。充其量,它们在所有参与者行为良好的情况下,能够实现更高效的缓存更新和乐观并发写入。最坏情况下,这些条件会失败,客户端将收到一个与没有条件请求的 HTTP 交换一样不更有害的响应。

实体标签(entity tag)可能被滥用而产生隐私风险。例如,站点可能故意构造一个语义上无效且对用户或用户代理唯一的实体标签,将其发送在具有长新鲜度时间的可缓存响应中,然后在后续的条件请求中读取该实体标签以重新识别该用户或用户代理。只要用户代理保留原始缓存条目,这样的识别标签就会成为持久标识符。缓存表征的用户代理应确保在用户执行维护隐私的操作(例如清除已存储的 cookies 或切换到私密浏览模式)时清除或替换缓存。

17.15. 使用 Range 的拒绝服务攻击

不受约束的多范围请求容易受到拒绝服务攻击,因为请求许多重叠范围所需的努力相对很小,而尝试以许多部分提供所请求数据所消耗的时间、内存和带宽却很大。服务器应忽略、合并或拒绝明显滥用的范围请求,例如请求超过两个重叠范围或在单个范围集内请求许多小范围,特别是当这些范围无明显理由地无序请求时。多部分范围请求并非为支持随机访问而设计。

17.16. 认证相关的考虑

关于 HTTP 认证主题的一切都是安全考虑,因此下面的注意事项并不详尽。此外,这里限于关于认证框架的安全考虑,而不是讨论具体认证方案的所有潜在考虑(应在定义那些方案的规范中记录)。多个组织维护关于 Web 应用安全的专题信息和当前研究的链接(例如 [OWASP]),包括在实践中实现和使用认证方案的常见陷阱。

17.16.1. 凭证的机密性

HTTP 认证框架并未定义单一机制来维护凭证的机密性;相反,每个认证方案定义凭证在传输前如何编码。尽管这为未来认证方案的发展提供了灵活性,但对于那些本身不提供机密性或未能充分防护重放攻击的现有方案来说,这不足以保护它们。此外,如果服务器期望凭证是针对每个用户特定的,则传输这些凭证将会导致识别该用户,即使凭证内容保持机密亦然。

HTTP 依赖于底层传输或会话级连接的安全属性来提供字段的机密传输。依赖于个别用户认证的服务在交换凭证之前需要一个 受保护的 连接(第4.2.2节)。

17.16.2. 凭证与空闲客户端

现有的 HTTP 客户端和用户代理通常无限期保留认证信息。HTTP 不提供一种机制让原点服务器指示客户端丢弃这些缓存的凭证,因为协议并不了解凭证是如何被用户代理获取或管理的。凭证过期或撤销的机制可以作为认证方案定义的一部分来指定。

凭证缓存可能干扰应用安全模型的情况包括但不限于:

  • 已长时间空闲的客户端,之后服务器可能希望使客户端重新提示用户输入凭证。
  • 应用包含会话终止指示(例如页面上的“注销”或“提交”按钮),服务器端应用在此之后“知道”客户端没有保留凭证的进一步理由。

缓存凭证的用户代理应提供一种便捷可访问的机制,允许用户在控制下丢弃缓存的凭证。

17.16.3. 保护空间

仅依赖 "realm" 机制来建立保护空间的认证方案会将凭证暴露给原点服务器上的所有资源。成功对某资源进行认证请求的客户端可以对同一原点服务器上的其他资源使用相同的认证凭证。这使得不同资源有可能收集其他资源的认证凭证。

当原点服务器在同一原点下为多个方提供资源时(参见 第11.5节),这尤其令人担忧。可能的缓解策略包括限制直接访问认证凭证(即不使 Authorization 请求首部字段的内容可用)和通过为每一方使用不同的主机名(或端口号)来分隔保护空间。

17.16.4. 附加响应字段

在未加密通道上向响应中添加信息可能影响安全和隐私。仅存在 Authentication-InfoProxy-Authentication-Info 首部字段就表明 HTTP 认证正在使用。认证方案特定参数的内容可能会暴露额外信息;这需要在这些方案的定义中加以考虑。

18. IANA 注意事项

下列注册的变更控制者为:“IETF (iesg@ietf.org) - Internet Engineering Task Force”。

18.1. URI 方案注册

IANA 已在 [BCP35] 的 “Uniform Resource Identifier (URI) Schemes” 注册表(位于 https://www.iana.org/assignments/uri-schemes/)中更新了在 表 2(见 第4.2节)列出的永久方案。

18.2. 方法注册

IANA 已在 “Hypertext Transfer Protocol (HTTP) Method Registry”(位于 https://www.iana.org/assignments/http-methods)中更新了 第16.1.1节 所述的注册程序,并在下表中汇总了方法名称。

表 7
Method Safe Idempotent Section
CONNECT 9.3.6
DELETE 9.3.5
GET 9.3.1
HEAD 9.3.2
OPTIONS 9.3.7
POST 9.3.3
PUT 9.3.4
TRACE 9.3.8
* 18.2

方法名 "*" 被保留,因为在某些字段(例如 "Access-Control-Request-Method")中将 "*" 用作通配符会产生冲突。

18.3. 状态码注册

IANA 已在 “Hypertext Transfer Protocol (HTTP) Status Code Registry”(位于 https://www.iana.org/assignments/http-status-codes)中更新了 第16.2.1节 所述的注册程序,并在下表中汇总了状态码值。

表 8
Value Description Section
100 Continue 15.2.1
101 Switching Protocols 15.2.2
200 OK 15.3.1
201 Created 15.3.2
202 Accepted 15.3.3
203 Non-Authoritative Information 15.3.4
204 No Content 15.3.5
205 Reset Content 15.3.6
206 Partial Content 15.3.7
300 Multiple Choices 15.4.1
301 Moved Permanently 15.4.2
302 Found 15.4.3
303 See Other 15.4.4
304 Not Modified 15.4.5
305 Use Proxy 15.4.6
306 (Unused) 15.4.7
307 Temporary Redirect 15.4.8
308 Permanent Redirect 15.4.9
400 Bad Request 15.5.1
401 Unauthorized 15.5.2
402 Payment Required 15.5.3
403 Forbidden 15.5.4
404 Not Found 15.5.5
405 Method Not Allowed 15.5.6
406 Not Acceptable 15.5.7
407 Proxy Authentication Required 15.5.8
408 Request Timeout 15.5.9
409 Conflict 15.5.10
410 Gone 15.5.11
411 Length Required 15.5.12
412 Precondition Failed 15.5.13
413 Content Too Large 15.5.14
414 URI Too Long 15.5.15
415 Unsupported Media Type 15.5.16
416 Range Not Satisfiable 15.5.17
417 Expectation Failed 15.5.18
418 (Unused) 15.5.19
421 Misdirected Request 15.5.20
422 Unprocessable Content 15.5.21
426 Upgrade Required 15.5.22
500 Internal Server Error 15.6.1
501 Not Implemented 15.6.2
502 Bad Gateway 15.6.3
503 Service Unavailable 15.6.4
504 Gateway Timeout 15.6.5
505 HTTP Version Not Supported 15.6.6

18.4. 字段名注册

本规范更新了 [RFC3864] 中定义的消息首部字段现有注册程序中与 HTTP 相关的部分。通过定义新的注册程序并将 HTTP 字段定义移入单独的注册表,本规范替换了旧程序中与 HTTP 相关的部分。

IANA 已创建了一个名为 “Hypertext Transfer Protocol (HTTP) Field Name Registry” 的新注册表,详见 第16.3.1节

IANA 已将 “Permanent Message Header Field Names” 和 “Provisional Message Header Field Names” 注册表(见 https://www.iana.org/assignments/message-headers/)中协议为 'http' 的所有条目移至此注册表并应用了以下更改:

  1. 已省略 'Applicable Protocol' 字段。
  2. 原先状态为 'standard'、'experimental'、'reserved' 或 'informational' 的条目现改为 'permanent'。
  3. 无状态的临时条目现被设为 'provisional'。
  4. 在确认注册文档未定义状态后,未标注状态的永久条目现被设为 'provisional'。专家可在有证据表明更合适时选择更新这些条目的状态。

IANA 已在 “Permanent Message Header Field Names” 和 “Provisional Message Header Field Names” 注册表上添加如下注释,以指示 HTTP 字段名注册已迁移:

IANA 已在 “Hypertext Transfer Protocol (HTTP) Field Name Registry” 中更新了下表所列的字段名。

表 9
Field Name Status Section Comments
Accept permanent 12.5.1
Accept-Charset deprecated 12.5.2
Accept-Encoding permanent 12.5.3
Accept-Language permanent 12.5.4
Accept-Ranges permanent 14.3
Allow permanent 10.2.1
Authentication-Info permanent 11.6.3
Authorization permanent 11.6.2
Connection permanent 7.6.1
Content-Encoding permanent 8.4
Content-Language permanent 8.5
Content-Length permanent 8.6
Content-Location permanent 8.7
Content-Range permanent 14.4
Content-Type permanent 8.3
Date permanent 6.6.1
ETag permanent 8.8.3
Expect permanent 10.1.1
From permanent 10.1.2
Host permanent 7.2
If-Match permanent 13.1.1
If-Modified-Since permanent 13.1.3
If-None-Match permanent 13.1.2
If-Range permanent 13.1.5
If-Unmodified-Since permanent 13.1.4
Last-Modified permanent 8.8.2
Location permanent 10.2.2
Max-Forwards permanent 7.6.2
Proxy-Authenticate permanent 11.7.1
Proxy-Authentication-Info permanent 11.7.3
Proxy-Authorization permanent 11.7.2
Range permanent 14.2
Referer permanent 10.1.3
Retry-After permanent 10.2.3
Server permanent 10.2.4
TE permanent 10.1.4
Trailer permanent 6.6.2
Upgrade permanent 7.8
User-Agent permanent 10.1.5
Vary permanent 12.5.5
Via permanent 7.6.3
WWW-Authenticate permanent 11.6.1
* permanent 12.5.5 (保留)

字段名 "*" 被保留,因为将该名称用作 HTTP 首部字段可能与 Vary 首部字段中的特殊语义冲突(第12.5.5节)。

IANA 已将新注册表中 “Content-MD5” 条目的状态更新为 'obsoleted',并引用了 RFC2616 第14.15节(定义该首部字段)以及 RFC7231 附录 B(该附录从更新的规范中移除了该字段的定义)。

18.5. 认证方案注册

IANA 已在 “Hypertext Transfer Protocol (HTTP) Authentication Scheme Registry”(位于 https://www.iana.org/assignments/http-authschemes)中更新了 第16.4.1节 所述的注册程序。本文档未定义任何认证方案。

18.6. 内容编码注册

IANA 已在 “HTTP Content Coding Registry”(位于 https://www.iana.org/assignments/http-parameters/)中更新了 第16.6.1节 所述的注册程序,并在下表中汇总了内容编码名称。

表 10
Name Description Section
compress UNIX "compress" 数据格式 [Welch] 8.4.1.1
deflate “deflate” 压缩数据([RFC1951])在 "zlib" 数据格式中([RFC1950] 8.4.1.2
gzip GZIP 文件格式 [RFC1952] 8.4.1.3
identity 保留 12.5.3
x-compress 已弃用(为 compress 的别名) 8.4.1.1
x-gzip 已弃用(为 gzip 的别名) 8.4.1.3

18.7. 范围单位注册

IANA 已在 “HTTP Range Unit Registry”(位于 https://www.iana.org/assignments/http-parameters/)中更新了 第16.5.1节 所述的注册程序,并在下表中汇总了范围单位名称。

表 11
Range Unit Name Description Section
bytes 八位组范围 14.1.2
none 保留为关键字以指示不支持范围请求 14.3

18.8. 媒体类型注册

IANA 已在 “Media Types” 注册表(位于 https://www.iana.org/assignments/media-types)中使用 第14.6节 的注册信息更新了媒体类型 "multipart/byteranges" 的条目。

IANA 已更新关于 "q" 参数的注册表注释,添加了指向本文件 第12.5.1节 的链接。

18.9. 端口注册

IANA 已为使用 UDP 或 TCP 的端口 80 和 443 上的服务在 “Service Name and Transport Protocol Port Number Registry”(位于 https://www.iana.org/assignments/service-names-port-numbers/)中进行了以下更新:

  1. 将本文件用作 “Reference”;以及
  2. 在当前未指定时,将 “Assignee” 设置为 "IESG",将 “Contact” 设置为 "IETF_Chair"。

18.10. Upgrade 令牌注册

IANA 已在 “Hypertext Transfer Protocol (HTTP) Upgrade Token Registry”(位于 https://www.iana.org/assignments/http-upgrade-tokens)中更新了 第16.7节 所述的注册程序,并在下表中汇总了升级令牌名称。

表 12
Name Description Expected Version Tokens Section
HTTP Hypertext Transfer Protocol 任何 DIGIT.DIGIT(例如 "2.0") 2.5

19. 参考文献

19.1. 规范性参考文献

[CACHING]
Fielding, R., 编辑,Nottingham, M., 编辑,和 J. Reschke, 编辑,“HTTP Caching”,STD 98,RFC 9111,DOI 10.17487/RFC9111,2022 年 6 月。
[RFC1950]
Deutsch, P. 和 J-L. Gailly,“ZLIB Compressed Data Format Specification version 3.3”,RFC 1950,DOI 10.17487/RFC1950,1996 年 5 月, <https://www.rfc-editor.org/info/rfc1950>。
[RFC1951]
Deutsch, P.,“DEFLATE Compressed Data Format Specification version 1.3”,RFC 1951,DOI 10.17487/RFC1951,1996 年 5 月, <https://www.rfc-editor.org/info/rfc1951>。
[RFC1952]
Deutsch, P.,“GZIP file format specification version 4.3”,RFC 1952,DOI 10.17487/RFC1952,1996 年 5 月, <https://www.rfc-editor.org/info/rfc1952>。
[RFC2046]
Freed, N. 和 N. Borenstein,“Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types”,RFC 2046,DOI 10.17487/RFC2046,1996 年 11 月, <https://www.rfc-editor.org/info/rfc2046>。
[RFC2119]
Bradner, S.,“Key words for use in RFCs to Indicate Requirement Levels”,BCP 14,RFC 2119,DOI 10.17487/RFC2119,1997 年 3 月, <https://www.rfc-editor.org/info/rfc2119>。
[RFC4647]
Phillips, A., 编辑 和 M. Davis, 编辑,“Matching of Language Tags”,BCP 47,RFC 4647, DOI 10.17487/RFC4647,2006 年 9 月, <https://www.rfc-editor.org/info/rfc4647>。
[RFC4648]
Josefsson, S.,“The Base16, Base32, and Base64 Data Encodings”,RFC 4648,DOI 10.17487/RFC4648,2006 年 10 月, <https://www.rfc-editor.org/info/rfc4648>。
[RFC5234]
Crocker, D., 编辑 和 P. Overell,“Augmented BNF for Syntax Specifications: ABNF”,STD 68,RFC 5234,DOI 10.17487/RFC5234,2008 年 1 月,<https://www.rfc-editor.org/info/rfc5234>。
[RFC5280]
Cooper, D., Santesson, S., Farrell, S., Boeyen, S., Housley, R., 和 W. Polk,“Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile”, RFC 5280,DOI 10.17487/RFC5280, 2008 年 5 月,<https://www.rfc-editor.org/info/rfc5280>。
[RFC5322]
Resnick, P., 编辑,“Internet Message Format”,RFC 5322,DOI 10.17487/RFC5322,2008 年 10 月,<https://www.rfc-editor.org/info/rfc5322>。
[RFC5646]
Phillips, A., 编辑 和 M. Davis, 编辑,“Tags for Identifying Languages”,BCP 47,RFC 5646,DOI 10.17487/RFC5646, 2009 年 9 月,<https://www.rfc-editor.org/info/rfc5646>。
[RFC6125]
Saint-Andre, P. 和 J. Hodges,“Representation and Verification of Domain-Based Application Service Identity within Internet Public Key Infrastructure Using X.509 (PKIX) Certificates in the Context of Transport Layer Security (TLS)”,RFC 6125,DOI 10.17487/RFC6125,2011 年 3 月, <https://www.rfc-editor.org/info/rfc6125>。
[RFC6365]
Hoffman, P. 和 J. Klensin,“Terminology Used in Internationalization in the IETF”,BCP 166,RFC 6365,DOI 10.17487/RFC6365,2011 年 9 月, <https://www.rfc-editor.org/info/rfc6365>。
[RFC7405]
Kyzivat, P.,“Case-Sensitive String Support in ABNF”,RFC 7405,DOI 10.17487/RFC7405,2014 年 12 月, <https://www.rfc-editor.org/info/rfc7405>。
[RFC8174]
Leiba, B.,“Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words”,BCP 14,RFC 8174,DOI 10.17487/RFC8174,2017 年 5 月, <https://www.rfc-editor.org/info/rfc8174>。
[TCP]
Postel, J.,“Transmission Control Protocol”,STD 7, RFC 793,DOI 10.17487/RFC0793, 1981 年 9 月,<https://www.rfc-editor.org/info/rfc793>。
[TLS13]
Rescorla, E.,“The Transport Layer Security (TLS) Protocol Version 1.3”,RFC 8446,DOI 10.17487/RFC8446,2018 年 8 月, <https://www.rfc-editor.org/info/rfc8446>。
[URI]
Berners-Lee, T., Fielding, R., 和 L. Masinter,“Uniform Resource Identifier (URI): Generic Syntax”,STD 66,RFC 3986,DOI 10.17487/RFC3986,2005 年 1 月,<https://www.rfc-editor.org/info/rfc3986>。
[USASCII]
美国国家标准学会, “Coded Character Set -- 7-bit American Standard Code for Information Interchange”,ANSI X3.4,1986 年。
[Welch]
Welch, T.,“A Technique for High-Performance Data Compression”,IEEE Computer 17(6),DOI 10.1109/MC.1984.1659158,1984 年 6 月,<https://ieeexplore.ieee.org/document/1659158/>。

19.2. 信息性参考文献

[ALTSVC]
Nottingham, M., McManus, P., 和 J. Reschke,“HTTP Alternative Services”,RFC 7838,DOI 10.17487/RFC7838,2016 年 4 月,<https://www.rfc-editor.org/info/rfc7838>。
[BCP13]
Freed, N. 和 J. Klensin,“Multipurpose Internet Mail Extensions (MIME) Part Four: Registration Procedures”,BCP 13,RFC 4289,DOI 10.17487/RFC4289,2005 年 12 月。
Freed, N., Klensin, J., 和 T. Hansen,“Media Type Specifications and Registration Procedures”,BCP 13,RFC 6838,DOI 10.17487/RFC6838,2013 年 1 月。
<https://www.rfc-editor.org/info/bcp13>
[BCP178]
Saint-Andre, P., Crocker, D., 和 M. Nottingham,“Deprecating the "X-" Prefix and Similar Constructs in Application Protocols”,BCP 178,RFC 6648,DOI 10.17487/RFC6648,2012 年 6 月。
<https://www.rfc-editor.org/info/bcp178>
[BCP35]
Thaler, D., 编辑,Hansen, T., 和 T. Hardie,“Guidelines and Registration Procedures for URI Schemes”,BCP 35,RFC 7595,DOI 10.17487/RFC7595,2015 年 6 月。
<https://www.rfc-editor.org/info/bcp35>
[BREACH]
Gluck, Y., Harris, N., 和 A. Prado,“BREACH: Reviving the CRIME Attack”,2013 年 7 月,<http://breachattack.com/resources/BREACH%20-%20SSL,%20gone%20in%2030%20seconds.pdf>。
[Bujlow]
Bujlow, T., Carela-Español, V., Solé-Pareta, J., 和 P. Barlet-Ros,“A Survey on Web Tracking: Mechanisms, Implications, and Defenses”,DOI 10.1109/JPROC.2016.2637878,发表于 IEEE 期刊 105(8),2017 年 8 月。
Barth, A.,“HTTP State Management Mechanism”,RFC 6265,DOI 10.17487/RFC6265,2011 年 4 月, <https://www.rfc-editor.org/info/rfc6265>。
[Err1912]
RFC Errata,Erratum ID 1912, RFC 2978,<https://www.rfc-editor.org/errata/eid1912>。
[Err5433]
RFC Errata,Erratum ID 5433, RFC 2978,<https://www.rfc-editor.org/errata/eid5433>。
[Georgiev]
Georgiev, M., Iyengar, S., Jana, S., Anubhai, R., Boneh, D., 和 V. Shmatikov,“The Most Dangerous Code in the World: Validating SSL Certificates in Non-Browser Software”,DOI 10.1145/2382196.2382204,发表于 2012 年 ACM 计算机与通信安全会议 (CCS '12),2012 年 10 月,第 38–49 页。
[HPACK]
Peon, R. 和 H. Ruellan,“HPACK: Header Compression for HTTP/2”,RFC 7541,DOI 10.17487/RFC7541,2015 年 5 月,<https://www.rfc-editor.org/info/rfc7541>。
[HTTP/1.0]
Berners-Lee, T., Fielding, R., 和 H. Frystyk,“Hypertext Transfer Protocol -- HTTP/1.0”,RFC 1945,DOI 10.17487/RFC1945,1996 年 5 月,<https://www.rfc-editor.org/info/rfc1945>。
[HTTP/1.1]
Fielding, R., 编辑,Nottingham, M., 编辑,和 J. Reschke, 编辑,“HTTP/1.1”,STD 99,RFC 9112,DOI 10.17487/RFC9112,2022 年 6 月。
[HTTP/2]
Thomson, M., 编辑 和 C. Benfield, 编辑,“HTTP/2”,RFC 9113,DOI 10.17487/RFC9113,2022 年 6 月, <https://www.rfc-editor.org/info/rfc9113>。
[HTTP/3]
Bishop, M., 编辑,“HTTP/3”, RFC 9114,DOI 10.17487/RFC9114, 2022 年 6 月,<https://www.rfc-editor.org/info/rfc9114>。
[ISO-8859-1]
国际标准化组织, “Information technology -- 8-bit single-byte coded graphic character sets -- Part 1: Latin alphabet No. 1”,ISO/IEC 8859-1:1998,1998 年。
[Kri2001]
Kristol, D.,“HTTP Cookies: Standards, Privacy, and Politics”,ACM Transactions on Internet Technology 1(2),2001 年 11 月,<http://arxiv.org/abs/cs.SE/0105018>。
[OWASP]
The Open Web Application Security Project,<https://www.owasp.org/>。
[REST]
Fielding, R.,“Architectural Styles and the Design of Network-based Software Architectures”,博士论文,加州大学欧文分校,2000 年 9 月,<https://roy.gbiv.com/pubs/dissertation/top.htm>。
[RFC1919]
Chatel, M.,“Classical versus Transparent IP Proxies”,RFC 1919,DOI 10.17487/RFC1919,1996 年 3 月,<https://www.rfc-editor.org/info/rfc1919>。
[RFC2047]
Moore, K.,“MIME (Multipurpose Internet Mail Extensions) Part Three: Message Header Extensions for Non-ASCII Text”,RFC 2047,DOI 10.17487/RFC2047,1996 年 11 月,<https://www.rfc-editor.org/info/rfc2047>。
[RFC2068]
Fielding, R., Gettys, J., Mogul, J., Frystyk, H., 和 T. Berners-Lee,“Hypertext Transfer Protocol -- HTTP/1.1”,RFC 2068,DOI 10.17487/RFC2068,1997 年 1 月,<https://www.rfc-editor.org/info/rfc2068>。
[RFC2145]
Mogul, J., Fielding, R., Gettys, J., 和 H. Frystyk,“Use and Interpretation of HTTP Version Numbers”,RFC 2145,DOI 10.17487/RFC2145,1997 年 5 月,<https://www.rfc-editor.org/info/rfc2145>。
[RFC2295]
Holtman, K. 和 A. Mutz,“Transparent Content Negotiation in HTTP”,RFC 2295,DOI 10.17487/RFC2295,1998 年 3 月,<https://www.rfc-editor.org/info/rfc2295>。
[RFC2324]
Masinter, L.,“Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0)”,RFC 2324,DOI 10.17487/RFC2324,1998 年 4 月 1 日,<https://www.rfc-editor.org/info/rfc2324>。
[RFC2557]
Palme, J., Hopmann, A., 和 N. Shelness,“MIME Encapsulation of Aggregate Documents, such as HTML (MHTML)”,RFC 2557,DOI 10.17487/RFC2557,1999 年 3 月,<https://www.rfc-editor.org/info/rfc2557>。
[RFC2616]
Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter, L., Leach, P., 和 T. Berners-Lee,“Hypertext Transfer Protocol -- HTTP/1.1”,RFC 2616,DOI 10.17487/RFC2616,1999 年 6 月,<https://www.rfc-editor.org/info/rfc2616>。
[RFC2617]
Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., Leach, P., Luotonen, A., 和 L. Stewart,“HTTP Authentication: Basic and Digest Access Authentication”,RFC 2617,DOI 10.17487/RFC2617,1999 年 6 月,<https://www.rfc-editor.org/info/rfc2617>。
[RFC2774]
Nielsen, H., Leach, P., 和 S. Lawrence,“An HTTP Extension Framework”,RFC 2774,DOI 10.17487/RFC2774,2000 年 2 月,<https://www.rfc-editor.org/info/rfc2774>。
[RFC2818]
Rescorla, E.,“HTTP Over TLS”,RFC 2818,DOI 10.17487/RFC2818,2000 年 5 月,<https://www.rfc-editor.org/info/rfc2818>。
[RFC2978]
Freed, N. 和 J. Postel,“IANA Charset Registration Procedures”,BCP 19,RFC 2978,DOI 10.17487/RFC2978,2000 年 10 月,<https://www.rfc-editor.org/info/rfc2978>。
[RFC3040]
Cooper, I., Melve, I., 和 G. Tomlinson,“Internet Web Replication and Caching Taxonomy”,RFC 3040,DOI 10.17487/RFC3040,2001 年 1 月,<https://www.rfc-editor.org/info/rfc3040>。
[RFC3864]
Klyne, G., Nottingham, M., 和 J. Mogul,“Registration Procedures for Message Header Fields”,BCP 90,RFC 3864,DOI 10.17487/RFC3864,2004 年 9 月,<https://www.rfc-editor.org/info/rfc3864>。
[RFC3875]
Robinson, D. 和 K. Coar,“The Common Gateway Interface (CGI) Version 1.1”,RFC 3875,DOI 10.17487/RFC3875,2004 年 10 月,<https://www.rfc-editor.org/info/rfc3875>。
[RFC4033]
Arends, R., Austein, R., Larson, M., Massey, D., 和 S. Rose,“DNS Security Introduction and Requirements”,RFC 4033,DOI 10.17487/RFC4033,2005 年 3 月,<https://www.rfc-editor.org/info/rfc4033>。
[RFC4559]
Jaganathan, K., Zhu, L., 和 J. Brezak,“SPNEGO-based Kerberos and NTLM HTTP Authentication in Microsoft Windows”,RFC 4559,DOI 10.17487/RFC4559,2006 年 6 月,<https://www.rfc-editor.org/info/rfc4559>。
[RFC5789]
Dusseault, L. 和 J. Snell,“PATCH Method for HTTP”, RFC 5789,DOI 10.17487/RFC5789,2010 年 3 月,<https://www.rfc-editor.org/info/rfc5789>。
[RFC5905]
Mills, D., Martin, J., 编辑,Burbank, J., 和 W. Kasch,“Network Time Protocol Version 4: Protocol and Algorithms Specification”,RFC 5905,DOI 10.17487/RFC5905,2010 年 6 月,<https://www.rfc-editor.org/info/rfc5905>。
[RFC6454]
Barth, A.,“The Web Origin Concept”,RFC 6454,DOI 10.17487/RFC6454,2011 年 12 月,<https://www.rfc-editor.org/info/rfc6454>。
[RFC6585]
Nottingham, M. 和 R. Fielding,“Additional HTTP Status Codes”,RFC 6585,DOI 10.17487/RFC6585,2012 年 4 月,<https://www.rfc-editor.org/info/rfc6585>。
[RFC7230]
Fielding, R., 编辑 和 J. Reschke, 编辑,“Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing”,RFC 7230,DOI 10.17487/RFC7230,2014 年 6 月,<https://www.rfc-editor.org/info/rfc7230>。
[RFC7231]
Fielding, R., 编辑 和 J. Reschke, 编辑,“Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content”,RFC 7231,DOI 10.17487/RFC7231,2014 年 6 月,<https://www.rfc-editor.org/info/rfc7231>。
[RFC7232]
Fielding, R., 编辑 和 J. Reschke, 编辑,“Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests”,RFC 7232,DOI 10.17487/RFC7232,2014 年 6 月,<https://www.rfc-editor.org/info/rfc7232>。
[RFC7233]
Fielding, R., 编辑,Lafon, Y., 编辑,和 J. Reschke, 编辑,“Hypertext Transfer Protocol (HTTP/1.1): Range Requests”,RFC 7233,DOI 10.17487/RFC7233,2014 年 6 月,<https://www.rfc-editor.org/info/rfc7233>。
[RFC7234]
Fielding, R., 编辑,Nottingham, M., 编辑,和 J. Reschke, 编辑,“Hypertext Transfer Protocol (HTTP/1.1): Caching”,RFC 7234,DOI 10.17487/RFC7234,2014 年 6 月,<https://www.rfc-editor.org/info/rfc7234>。
[RFC7235]
Fielding, R., 编辑 和 J. Reschke, 编辑,“Hypertext Transfer Protocol (HTTP/1.1): Authentication”,RFC 7235,DOI 10.17487/RFC7235,2014 年 6 月,<https://www.rfc-editor.org/info/rfc7235>。
[RFC7538]
Reschke, J.,“The Hypertext Transfer Protocol Status Code 308 (Permanent Redirect)”,RFC 7538,DOI 10.17487/RFC7538,2015 年 4 月,<https://www.rfc-editor.org/info/rfc7538>。
[RFC7540]
Belshe, M., Peon, R., 和 M. Thomson, 编辑,“Hypertext Transfer Protocol Version 2 (HTTP/2)”,RFC 7540,DOI 10.17487/RFC7540,2015 年 5 月,<https://www.rfc-editor.org/info/rfc7540>。
[RFC7578]
Masinter, L.,“Returning Values from Forms: multipart/form-data”,RFC 7578,DOI 10.17487/RFC7578,2015 年 7 月,<https://www.rfc-editor.org/info/rfc7578>。
[RFC7615]
Reschke, J.,“HTTP Authentication-Info and Proxy-Authentication-Info Response Header Fields”, RFC 7615,DOI 10.17487/RFC7615,2015 年 9 月,<https://www.rfc-editor.org/info/rfc7615>。
[RFC7616]
Shekh-Yusef, R., 编辑,Ahrens, D., 和 S. Bremer,“HTTP Digest Access Authentication”,RFC 7616,DOI 10.17487/RFC7616,2015 年 9 月,<https://www.rfc-editor.org/info/rfc7616>。
[RFC7617]
Reschke, J.,“The 'Basic' HTTP Authentication Scheme”,RFC 7617,DOI 10.17487/RFC7617,2015 年 9 月,<https://www.rfc-editor.org/info/rfc7617>。
[RFC7694]
Reschke, J.,“Hypertext Transfer Protocol (HTTP) Client-Initiated Content-Encoding”,RFC 7694,DOI 10.17487/RFC7694,2015 年 11 月,<https://www.rfc-editor.org/info/rfc7694>。
[RFC8126]
Cotton, M., Leiba, B., 和 T. Narten,“Guidelines for Writing an IANA Considerations Section in RFCs”,BCP 26,RFC 8126,DOI 10.17487/RFC8126,2017 年 6 月,<https://www.rfc-editor.org/info/rfc8126>。
[RFC8187]
Reschke, J.,“Indicating Character Encoding and Language for HTTP Header Field Parameters”,RFC 8187, DOI 10.17487/RFC8187,2017 年 9 月,<https://www.rfc-editor.org/info/rfc8187>。
[RFC8246]
McManus, P.,“HTTP Immutable Responses”,RFC 8246,DOI 10.17487/RFC8246,2017 年 9 月,<https://www.rfc-editor.org/info/rfc8246>。
[RFC8288]
Nottingham, M.,“Web Linking”,RFC 8288,DOI 10.17487/RFC8288,2017 年 10 月,<https://www.rfc-editor.org/info/rfc8288>。
[RFC8336]
Nottingham, M. 和 E. Nygren,“The ORIGIN HTTP/2 Frame”, RFC 8336,DOI 10.17487/RFC8336,2018 年 3 月,<https://www.rfc-editor.org/info/rfc8336>。
[RFC8615]
Nottingham, M.,“Well-Known Uniform Resource Identifiers (URIs)”,RFC 8615,DOI 10.17487/RFC8615,2019 年 5 月,<https://www.rfc-editor.org/info/rfc8615>。
[RFC8941]
Nottingham, M. 和 P-H. Kamp,“Structured Field Values for HTTP”,RFC 8941,DOI 10.17487/RFC8941,2021 年 2 月,<https://www.rfc-editor.org/info/rfc8941>。
[Sniffing]
WHATWG,“MIME Sniffing”,<https://mimesniff.spec.whatwg.org>。
[WEBDAV]
Dusseault, L., 编辑,“HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)”,RFC 4918, DOI 10.17487/RFC4918,2007 年 6 月,<https://www.rfc-editor.org/info/rfc4918>。

附录 A. 汇总的 ABNF

在下面的汇总 ABNF 中,列表规则按照 第 5.6.1 节 展开。

Accept = [ ( media-range [ weight ] ) *( OWS "," OWS ( media-range [
 weight ] ) ) ]
Accept-Charset = [ ( ( token / "*" ) [ weight ] ) *( OWS "," OWS ( (
 token / "*" ) [ weight ] ) ) ]
Accept-Encoding = [ ( codings [ weight ] ) *( OWS "," OWS ( codings [
 weight ] ) ) ]
Accept-Language = [ ( language-range [ weight ] ) *( OWS "," OWS (
 language-range [ weight ] ) ) ]
Accept-Ranges = acceptable-ranges
Allow = [ method *( OWS "," OWS method ) ]
Authentication-Info = [ auth-param *( OWS "," OWS auth-param ) ]
Authorization = credentials

BWS = OWS

Connection = [ connection-option *( OWS "," OWS connection-option )
 ]
Content-Encoding = [ content-coding *( OWS "," OWS content-coding )
 ]
Content-Language = [ language-tag *( OWS "," OWS language-tag ) ]
Content-Length = 1*DIGIT
Content-Location = absolute-URI / partial-URI
Content-Range = range-unit SP ( range-resp / unsatisfied-range )
Content-Type = media-type

Date = HTTP-date

ETag = entity-tag
Expect = [ expectation *( OWS "," OWS expectation ) ]

From = mailbox

GMT = %x47.4D.54 ; GMT

HTTP-date = IMF-fixdate / obs-date
Host = uri-host [ ":" port ]

IMF-fixdate = day-name "," SP date1 SP time-of-day SP GMT
If-Match = "*" / [ entity-tag *( OWS "," OWS entity-tag ) ]
If-Modified-Since = HTTP-date
If-None-Match = "*" / [ entity-tag *( OWS "," OWS entity-tag ) ]
If-Range = entity-tag / HTTP-date
If-Unmodified-Since = HTTP-date

Last-Modified = HTTP-date
Location = URI-reference

Max-Forwards = 1*DIGIT

OWS = *( SP / HTAB )

Proxy-Authenticate = [ challenge *( OWS "," OWS challenge ) ]
Proxy-Authentication-Info = [ auth-param *( OWS "," OWS auth-param )
 ]
Proxy-Authorization = credentials

RWS = 1*( SP / HTAB )
Range = ranges-specifier
Referer = absolute-URI / partial-URI
Retry-After = HTTP-date / delay-seconds

Server = product *( RWS ( product / comment ) )

TE = [ t-codings *( OWS "," OWS t-codings ) ]
Trailer = [ field-name *( OWS "," OWS field-name ) ]

URI-reference = <URI-reference, 参见 [URI], 第 4.1 节>
Upgrade = [ protocol *( OWS "," OWS protocol ) ]
User-Agent = product *( RWS ( product / comment ) )

Vary = [ ( "*" / field-name ) *( OWS "," OWS ( "*" / field-name ) )
 ]
Via = [ ( received-protocol RWS received-by [ RWS comment ] ) *( OWS
 "," OWS ( received-protocol RWS received-by [ RWS comment ] ) ) ]

WWW-Authenticate = [ challenge *( OWS "," OWS challenge ) ]

absolute-URI = <absolute-URI, 参见 [URI], 第 4.3 节>
absolute-path = 1*( "/" segment )
acceptable-ranges = range-unit *( OWS "," OWS range-unit )
asctime-date = day-name SP date3 SP time-of-day SP year
auth-param = token BWS "=" BWS ( token / quoted-string )
auth-scheme = token
authority = <authority, 参见 [URI], 第 3.2 节>

challenge = auth-scheme [ 1*SP ( token68 / [ auth-param *( OWS ","
 OWS auth-param ) ] ) ]
codings = content-coding / "identity" / "*"
comment = "(" *( ctext / quoted-pair / comment ) ")"
complete-length = 1*DIGIT
connection-option = token
content-coding = token
credentials = auth-scheme [ 1*SP ( token68 / [ auth-param *( OWS ","
 OWS auth-param ) ] ) ]
ctext = HTAB / SP / %x21-27 ; '!'-'''
 / %x2A-5B ; '*'-'['
 / %x5D-7E ; ']'-'~'
 / obs-text

date1 = day SP month SP year
date2 = day "-" month "-" 2DIGIT
date3 = month SP ( 2DIGIT / ( SP DIGIT ) )
day = 2DIGIT
day-name = %x4D.6F.6E ; Mon
 / %x54.75.65 ; Tue
 / %x57.65.64 ; Wed
 / %x54.68.75 ; Thu
 / %x46.72.69 ; Fri
 / %x53.61.74 ; Sat
 / %x53.75.6E ; Sun
day-name-l = %x4D.6F.6E.64.61.79 ; Monday
 / %x54.75.65.73.64.61.79 ; Tuesday
 / %x57.65.64.6E.65.73.64.61.79 ; Wednesday
 / %x54.68.75.72.73.64.61.79 ; Thursday
 / %x46.72.69.64.61.79 ; Friday
 / %x53.61.74.75.72.64.61.79 ; Saturday
 / %x53.75.6E.64.61.79 ; Sunday
delay-seconds = 1*DIGIT

entity-tag = [ weak ] opaque-tag
etagc = "!" / %x23-7E ; '#'-'~'
 / obs-text
expectation = token [ "=" ( token / quoted-string ) parameters ]

field-content = field-vchar [ 1*( SP / HTAB / field-vchar )
 field-vchar ]
field-name = token
field-value = *field-content
field-vchar = VCHAR / obs-text
first-pos = 1*DIGIT

hour = 2DIGIT
http-URI = "http://" authority path-abempty [ "?" query ]
https-URI = "https://" authority path-abempty [ "?" query ]

incl-range = first-pos "-" last-pos
int-range = first-pos "-" [ last-pos ]

language-range = <language-range, 参见 [RFC4647], 第 2.1 节>
language-tag = <Language-Tag, 参见 [RFC5646], 第 2.1 节>
last-pos = 1*DIGIT

mailbox = <mailbox, 参见 [RFC5322], 第 3.4 节>
media-range = ( "*/*" / ( type "/*" ) / ( type "/" subtype ) )
 parameters
media-type = type "/" subtype parameters
method = token
minute = 2DIGIT
month = %x4A.61.6E ; Jan
 / %x46.65.62 ; Feb
 / %x4D.61.72 ; Mar
 / %x41.70.72 ; Apr
 / %x4D.61.79 ; May
 / %x4A.75.6E ; Jun
 / %x4A.75.6C ; Jul
 / %x41.75.67 ; Aug
 / %x53.65.70 ; Sep
 / %x4F.63.74 ; Oct
 / %x4E.6F.76 ; Nov
 / %x44.65.63 ; Dec

obs-date = rfc850-date / asctime-date
obs-text = %x80-FF
opaque-tag = DQUOTE *etagc DQUOTE
other-range = 1*( %x21-2B ; '!'-'+'
 / %x2D-7E ; '-'-'~'
 )

parameter = parameter-name "=" parameter-value
parameter-name = token
parameter-value = ( token / quoted-string )
parameters = *( OWS ";" OWS [ parameter ] )
partial-URI = relative-part [ "?" query ]
path-abempty = <path-abempty, 参见 [URI], 第 3.3 节>
port = <port, 参见 [URI], 第 3.2.3 节>
product = token [ "/" product-version ]
product-version = token
protocol = protocol-name [ "/" protocol-version ]
protocol-name = token
protocol-version = token
pseudonym = token

qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'['
 / %x5D-7E ; ']'-'~'
 / obs-text
query = <query, 参见 [URI], 第 3.4 节>
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
qvalue = ( "0" [ "." *3DIGIT ] ) / ( "1" [ "." *3"0" ] )

range-resp = incl-range "/" ( complete-length / "*" )
range-set = range-spec *( OWS "," OWS range-spec )
range-spec = int-range / suffix-range / other-range
range-unit = token
ranges-specifier = range-unit "=" range-set
received-by = pseudonym [ ":" port ]
received-protocol = [ protocol-name "/" ] protocol-version
relative-part = <relative-part, 参见 [URI], 第 4.2 节>
rfc850-date = day-name-l "," SP date2 SP time-of-day SP GMT

second = 2DIGIT
segment = <segment, 参见 [URI], 第 3.3 节>
subtype = token
suffix-length = 1*DIGIT
suffix-range = "-" suffix-length

t-codings = "trailers" / ( transfer-coding [ weight ] )
tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
 "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
time-of-day = hour ":" minute ":" second
token = 1*tchar
token68 = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" )
 *"="
transfer-coding = token *( OWS ";" OWS transfer-parameter )
transfer-parameter = token BWS "=" BWS ( token / quoted-string )
type = token

unsatisfied-range = "*/" complete-length
uri-host = <host, 参见 [URI], 第 3.2.2 节>

weak = %x57.2F ; W/
weight = OWS ";" OWS "q=" qvalue

year = 4DIGIT

附录 B. 与先前 RFC 的差异

B.1. 与 RFC 2818 的差异

无。

B.2. 与 RFC 7230 的差异

介绍 HTTP 设计目标、历史、架构、符合性准则、协议版本控制、URI、消息路由和首部字段的章节已移至本处。

关于语义符合性的要求已被替换为允许忽略或采用变通方法来处理实现特定的失败。(第 2.2 节

关于 origin 及对原点服务器的权威访问的描述已扩展,涵盖 "http" 和 "https" URI,以考虑替代服务和不一定基于 TCP 的安全连接。(第 4.2.1 节4.2.2 节4.3.1 节7.3.3 节

已添加显式要求以检查目标 URI 方案的语义,并拒绝不满足任何相关要求的请求。(第 7.4 节

media type、media range 和 expectation 中的参数可以通过一个或多个尾随分号为空。(第 5.6.6 节

“Field value” 现在指在多行首部合并(以逗号分隔)之后的值 — 这是最常见的用法。若要指单个首部行的值,请使用 “field line value”。(第 6.3 节

Trailer 字段语义现在超越了分块传输编码的具体细节。对 trailer 字段的使用进一步受限:只有当发送方知道该字段定义支持作为 trailer 生成时,才允许作为 trailer;并且只有在接收方知道相应字段定义允许并定义如何合并时,才允许合并到头部段。在所有其他情况下,建议实现要么将 trailer 字段单独存储,要么在代替合并的情况下丢弃它们。(第 6.5.1 节

已明确原点服务器对请求 URI 绝对形式优先于 Host 首部字段,以与代理处理保持一致。(第 7.2 节

由于 URI 语法中对 host 的更改在 Via 中并不理想,RFC 7230 扩展了 Via 字段中 “received-by” 的语法。为简化起见,我们已从 received-by 产生式中移除了 uri-host,因为现有的 pseudonym 语法已能涵盖该项。特别是,此更改从 received-by 的允许主机名字符集中移除了逗号。(第 7.6.3 节

B.3. 与 RFC 7231 的差异

现在建议实现应支持的最小 URI 长度。(第 4.1 节

已澄清:字段值中的 CR 和 NUL 应被拒绝或映射为 SP,并且在消费字段值之前需要去除前导和尾随空白。(第 5.5 节

media type、media range 和 expectation 中的参数可以通过一个或多个尾随分号为空。(第 5.6.6 节

引入了一个用于 HTTP 消息的抽象数据类型,以定义消息组件及其语义,作为跨多个 HTTP 版本的抽象,而不是以 HTTP/1.1 的具体语法形式表示。这反映了解析后消息的内容,使得更容易区分对内容(所传达的内容)的要求与对消息语法(如何传达)的要求,并避免将早期协议版本的限制固化到 HTTP 的未来中。(第 6 节

术语 “payload” 和 “payload body” 已被替换为 “content”,以更好地与其他地方的用法(例如字段名)对齐,并避免与 HTTP/2 和 HTTP/3 中的帧负载混淆。(第 6.4 节

术语 “effective request URI” 已被替换为 “target URI”。(第 7.1 节

对客户端重试的限制已放宽,以反映实现行为。(第 9.2.2 节

已澄清 GET、HEAD 和 DELETE 上的请求主体不可互操作的事实。(第 9.3.1 节9.3.2 节、和 9.3.5 节

允许在 PUT 请求上将 Content-Range 首部字段(第 14.4 节)用作请求修饰符。(第 9.3.4 节

已从 OPTIONS 方法的描述中删除了一个关于设置 Content-Length 的多余要求。(第 9.3.7 节

已删除在 TRACE 响应中使用 "message/http" 媒体类型的规范性要求。(第 9.3.8 节

为与 RFC 2616 兼容,已恢复基于列表的 Expect 语法。(第 10.1.1 节

AcceptAccept-Encoding 被允许出现在响应消息中;后者由 [RFC7694] 引入。(第 12.3 节

已从 Accept 字段的定义中移除 “Accept Parameters”(accept-params 和 accept-ext ABNF 产生式)。(第 12.5.1 节

Accept-Charset 字段现在已弃用。(第 12.5.2 节

澄清了当 Vary 首部字段中存在其他值时 "*" 的语义。(第 12.5.5 节

范围单位比较时不区分大小写。(第 14.1 节

Accept-Ranges 字段的使用不再仅限于原点服务器。(第 14.3 节

重定向请求的创建过程已被澄清。(第 15.4 节

添加了状态码 308(此前在 [RFC7538] 中定义),以使其与 301、302 和 307 等状态码定义更接近。(第 15.4.9 节

状态码 421(此前在 RFC 7540 第 9.1.2 节 中定义)已被纳入,因为其具有通用适用性。由于该响应特定于连接(而非目标资源),421 不再被定义为启发式可缓存。(第 15.5.20 节

状态码 422(此前在 WEBDAV 第 11.2 节 中定义)已因其通用适用性而被纳入。(第 15.5.21 节

B.4. 与 RFC 7232 的差异

先前的 HTTP 修订对确定 Last-Modified 是否为强验证器施加了任意的 60 秒限制,以防 Date 和 Last-Modified 值由不同的时钟生成或在准备响应期间产生时间略有不同。本规范已放宽该限制,允许合理裁量。(第 8.8.2.2 节

已删除 If-Match 和 If-Unmodified-Since 的一个边缘情况要求,该要求规定如果因更改请求已被应用而导致验证失败,则不应在 2xx 响应中发送验证器。(第 13.1.1 节13.1.4 节

已澄清 If-Unmodified-Since 不适用于没有修改时间概念的资源。(第 13.1.4 节

先决条件现在可以在处理请求内容之前评估,而无需等到响应本应成功时才评估。(第 13.2 节

B.5. 与 RFC 7233 的差异

已重构 range-unit 和 ranges-specifier 的语法,以简化并减少字节与其他(扩展)范围单位之间的人为区分;通过将范围单位通用地定义为 token 并将扩展放在 range-spec(other-range)的范围内,移除了 other-range-unit 的重叠语法。这为所有范围集合(包括扩展范围单位)中用逗号指示多于一个范围的 range-set 的列表语法角色消除了歧义。将扩展语法移入 range-spec 也允许单独指定与字节范围相关的协议细节。

现在可以在扩展方法上定义 Range 处理。(第 14.2 节

描述了将 Content-Range 首部字段(第 14.4 节)作为请求修饰符以执行部分 PUT 的用法。(第 14.5 节

B.6. 与 RFC 7235 的差异

无。

B.7. 与 RFC 7538 的差异

无。

B.8. 与 RFC 7615 的差异

无。

B.9. 与 RFC 7694 的差异

本规范包含了 [RFC7694] 中定义的扩展,但省略了示例和部署注意事项。

致谢

除了当前的编辑之外,下列人士因对 HTTP 的早期方面及其核心规范所做的贡献而值得特别致谢:Marc Andreessen, Tim Berners-Lee, Robert Cailliau, Daniel W. Connolly, Bob Denny, John Franks, Jim Gettys, Jean-François Groff, Phillip M. Hallam-Baker, Koen Holtman, Jeffery L. Hostetler, Shel Kaphan, Dave Kristol, Yves Lafon, Scott D. Lawrence, Paul J. Leach, Håkon W. Lie, Ari Luotonen, Larry Masinter, Rob McCool, Jeffrey C. Mogul, Lou Montulli, David Morris, Henrik Frystyk Nielsen, Dave Raggett, Eric Rescorla, Tony Sanders, Lawrence C. Stewart, Marc VanHeyningen, 和 Steve Zilles。

本文件建立在以往多份 HTTP 规范所包含的诸多贡献之上,包括 [HTTP/1.0], [RFC2068], [RFC2145], [RFC2616], [RFC2617], [RFC2818], [RFC7230], [RFC7231], [RFC7232], [RFC7233], [RFC7234], 和 [RFC7235]。那些文档中的致谢仍然适用。

自 2014 年以来,以下贡献者通过报告错误、提出有见地的问题、起草或审阅文本以及评估议题,帮助改进了本规范:

Alan Egerton, Alex Rousskov, Amichai Rothman, Amos Jeffries, Anders Kaseorg, Andreas Gebhardt, Anne van Kesteren, Armin Abfalterer, Aron Duby, Asanka Herath, Asbjørn Ulsberg, Asta Olofsson, Attila Gulyas, Austin Wright, Barry Pollard, Ben Burkert, Benjamin Kaduk, Björn Höhrmann, Brad Fitzpatrick, Chris Pacejo, Colin Bendell, Cory Benfield, Cory Nelson, Daisuke Miyakawa, Dale Worley, Daniel Stenberg, Danil Suits, David Benjamin, David Matson, David Schinazi, Дилян Палаузов (Dilyan Palauzov), Eric Anderson, Eric Rescorla, Éric Vyncke, Erik Kline, Erwin Pe, Etan Kissling, Evert Pot, Evgeny Vrublevsky, Florian Best, Francesca Palombini, Igor Lubashev, James Callahan, James Peach, Jeffrey Yasskin, Kalin Gyokov, Kannan Goundan, 奥 一穂 (Kazuho Oku), Ken Murchison, Krzysztof Maczyński, Lars Eggert, Lucas Pardue, Martin Duke, Martin Dürst, Martin Thomson, Martynas Jusevičius, Matt Menke, Matthias Pigulla, Mattias Grenfeldt, Michael Osipov, Mike Bishop, Mike Pennisi, Mike Taylor, Mike West, Mohit Sethi, Murray Kucherawy, Nathaniel J. Smith, Nicholas Hurley, Nikita Prokhorov, Patrick McManus, Piotr Sikora, Poul-Henning Kamp, Rick van Rein, Robert Wilton, Roberto Polli, Roman Danyliw, Samuel Williams, Semyon Kholodnov, Simon Pieters, Simon Schüppel, Stefan Eissing, Taylor Hunt, Todd Greer, Tommy Pauly, Vasiliy Faronov, Vladimir Lashchev, Wenbo Zhu, William A. Rowe Jr., Willy Tarreau, Xingwei Liu, Yishuai Li, 和 Zaheduzzaman Sarker。

索引

1 2 3 4 5 A B C D E F G H I K L M N O P R S T U V W X

作者联系信息

Roy T. Fielding (editor)
Adobe
345 Park Ave
San Jose, CA 95110
United States of America
Email: fielding@gbiv.com
URI: https://roy.gbiv.com/
Mark Nottingham (editor)
Fastly
Prahran
Australia
Email: mnot@mnot.net
URI: https://www.mnot.net/
Julian Reschke (editor)
greenbytes GmbH
Hafenweg 16
48155 Münster
Germany
Email: julian.reschke@greenbytes.de
URI: https://greenbytes.de/tech/webdav/