HTTP 消息 - HTTP

HTTP 消息
=======

HTTP 消息是服务器和客户端之间交换数据的方式。有两种类型的消息：*请求*（request）——由客户端发送用来触发一个服务器上的动作；*响应*（response）——来自服务器的应答。

HTTP 消息由采用 ASCII 编码的多行文本构成。在 HTTP/1.1 及早期版本中，这些消息通过连接公开地发送。在 HTTP/2 中，为了优化和性能方面的改进，曾经可人工阅读的消息被分到多个 HTTP 帧中。

Web 开发人员或网站管理员，很少自己手工创建这些原始的 HTTP 消息：由软件、浏览器、代理或服务器完成。他们通过配置文件（用于代理服务器或服务器），API（用于浏览器）或其他接口提供 HTTP 消息。

![From a user-, script-, or server- generated event, an HTTP/1.x msg is generated, and if HTTP/2 is in use, it is binary framed into an HTTP/2 stream, then sent.](/zh-CN/docs/Web/HTTP/Messages/httpmsg2.png)

HTTP/2 二进制框架机制被设计为不需要改动任何 API 或配置文件即可应用：它大体上对用户是透明的。

HTTP 请求和响应具有相似的结构，由以下部分组成：

1. 一行起始行用于描述要执行的请求，或者是对应的状态，成功或失败。这个起始行总是单行的。
2. 一个可选的 HTTP 标头集合指明请求或描述消息主体（body）。
3. 一个空行指示所有关于请求的元数据已经发送完毕。
4. 一个可选的包含请求相关数据的*主体*（比如 HTML 表单内容），或者响应相关的文档。主体的大小有起始行的 HTTP 头来指定。

起始行和 HTTP 消息中的 HTTP 头统称为请求头，而其有效负载被称为消息主体。

![Requests and responses share a common structure in HTTP](/zh-CN/docs/Web/HTTP/Messages/httpmsgstructure2.png)

HTTP 请求
-------

### 起始行

HTTP 请求是由客户端发出的消息，用来使服务器执行动作。*起始行*（start-line）包含三个元素：

1. 一个 *HTTP 方法*，一个动词（像 `GET`、`PUT` 或者 `POST`）或者一个名词（像 `HEAD` 或者 `OPTIONS`），描述要执行的动作。例如，`GET` 表示要获取资源，`POST` 表示向服务器推送数据（创建或修改资源，或者产生要返回的临时文件）。
2. *请求目标*（request target），通常是一个 URL，或者是协议、端口和域名的绝对路径，通常以请求的环境为特征。请求的格式因不同的 HTTP 方法而异。它可以是：
    * 一个绝对路径，末尾跟上一个 `'?'` 和查询字符串。这是最常见的形式，称为*原始形式*（origin form），被 `GET`、`POST`、`HEAD` 和 `OPTIONS` 方法所使用。
        + `POST / HTTP/1.1`
        + `GET /background.png HTTP/1.0`
        + `HEAD /test.html?query=alibaba HTTP/1.1`
        + `OPTIONS /anypage.html HTTP/1.0`
    * 一个完整的 URL，被称为*绝对形式*（absolute form），主要在使用 `GET` 方法连接到代理时使用。`GET http://developer.mozilla.org/zh-CN/docs/Web/HTTP/Messages HTTP/1.1`
    * 由域名和可选端口（以 `':'` 为前缀）组成的 URL 的 authority 部分，称为 *authority form*。仅在使用 `CONNECT` 建立 HTTP 隧道时才使用。`CONNECT developer.mozilla.org:80 HTTP/1.1`
    * *星号形式*（asterisk form），一个简单的星号（`'*'`），配合 `OPTIONS` 方法使用，代表整个服务器。`OPTIONS * HTTP/1.1`
3. *HTTP 版本*（HTTP version），定义了剩余消息的结构，作为对期望的响应版本的指示符。
### 标头（Header）

来自请求的 HTTP 标头遵循和 HTTP 标头相同的基本结构：不区分大小写的字符串，紧跟着的冒号（`':'`）和一个结构取决于标头的值。整个标头（包括值）由一行组成，这一行可以相当长。

有许多请求标头可用，它们可以分为几组：

* 通用标头（General header），例如 `Via`，适用于整个消息。
* 请求标头（Request header），例如 `User-Agent`、`Accept-Type`，通过进一步的定义（例如 `Accept-Language`）、给定上下文（例如 `Referer`）或者进行有条件的限制（例如 `If-None`）来修改请求。
* 表示标头（Representation header），例如 `Content-Type` 描述了消息数据的原始格式和应用的任意编码（仅在消息有主体时才存在）。

![Example of headers in an HTTP request](/zh-CN/docs/Web/HTTP/Messages/http_request_headers3.png)

### 主体（Body）

请求的最后一部分是它的主体。不是所有的请求都有一个主体：例如获取资源的请求，像 `GET`、`HEAD`、`DELETE` 和 `OPTIONS`，通常它们不需要主体。有些请求将数据发送到服务器以便更新数据：常见的的情况是 POST 请求（包含 HTML 表单数据）。

主体大致可分为两类：

* 单一资源（Single-resource）主体，由一个单文件组成。该类型的主体由两个标头定义：`Content-Type` 和 `Content-Length`。
* 多资源（Multiple-resource）主体，由多部分主体组成，每一部分包含不同的信息位。通常是和 HTML 表单连系在一起。
HTTP 响应
-------

### 状态行

HTTP 响应的起始行被称作*状态行*（status line），包含以下信息：

1. *协议版本*，通常为 `HTTP/1.1`。
2. *状态码*（status code），表明请求是成功或失败。常见的状态码是 `200`、`404` 或 `302`。
3. *状态文本*（status text）。一个简短的，纯粹的信息，通过状态码的文本描述，帮助人们理解该 HTTP 消息。

一个典型的状态行看起来像这样：`HTTP/1.1 404 Not Found`。

### 标头（Header）

响应的 HTTP 标头遵循和任何其他标头相同的结构：不区分大小写的字符串，紧跟着的冒号（`':'`）和一个结构取决于标头类型的值。整个标头（包括其值）表现为单行形式。

许多不同的标头可能会出现在响应中。这些可以分为几组：

* 通用标头（General header），例如 `Via`，适用于整个消息。
* 响应标头（Response header），例如 `Vary` 和 `Accept-Ranges`，提供有关服务器的其他信息，这些信息不适合状态行。
* 表示标头（Representation header），例如 `Content-Type` 描述了消息数据的原始格式和应用的任意编码（仅在消息有主体时才存在）。

![Example of headers in an HTTP response](/zh-CN/docs/Web/HTTP/Messages/http_response_headers3.png)

### 主体（Body）

响应的最后一部分是主体。不是所有的响应都有主体：具有状态码（如 `201` 或 `204`）的响应，通常不会有主体。

主体大致可分为三类：

* 单资源（Single-resource）主体，由**已知**长度的单个文件组成。该类型主体由两个标头定义：`Content-Type` 和 `Content-Length`。
* 单资源（Single-resource）主体，由**未知**长度的单个文件组成。通过将 `Transfer-Encoding` 设置为 `chunked` 来使用分块编码。
* 多资源（Multiple-resource）主体，由多部分 body 组成，每部分包含不同的信息段。但这是比较少见的。
HTTP/2 帧
--------

HTTP/1.x 消息有一些性能上的缺点：

* 与主体不同，标头不会被压缩。
* 两个消息之间的标头通常非常相似，但它们仍然在连接中重复传输。
* 无法多路复用。当在同一个服务器打开几个连接时：TCP 热连接比冷连接更加有效。

HTTP/2 引入了一个额外的步骤：它将 HTTP/1.x 消息分成帧并嵌入到流（stream）中。数据帧和报头帧分离，这将允许报头压缩。将多个流组合，这是一个被称为*多路复用*（multiplexing）的过程，它允许更有效的底层 TCP 连接。

![HTTP/2 modify the HTTP message to divide them in frames (part of a single stream), allowing for more optimization.](/zh-CN/docs/Web/HTTP/Messages/binary_framing2.png)

HTTP 帧现在对 Web 开发人员是透明的。在 HTTP/2 中，这是一个在 HTTP/1.1 和底层传输协议之间附加的步骤。Web 开发人员不需要在其使用的 API 中做任何更改来利用 HTTP 帧；当浏览器和服务器都可用时，HTTP/2 将被打开并使用。

结论
--

HTTP 消息是使用 HTTP 的关键；它们的结构简单，并且具有高可扩展性。HTTP/2 帧机制是在 HTTP/1.x 语法和底层传输协议之间增加了一个新的中间层，而没有从根本上修改它，即它是建立在经过验证的机制之上。


HTTP 基础 - HTTP

HTTP 基础
=======

HTTP 是一个拓展性非常好的协议。它依赖于资源或统一资源定位符（URI）的概念、一个简单的消息结构和一个客户端——服务器结构的通信流。在这些基础概念之上，近年来已经出现了许多拓展，以增加新的 HTTP 方法或首部的方式为 HTTP 协议增加了新的功能和语义。

文章
--

HTTP 概览

描述了什么是 HTTP，它在 Web 架构中的角色，以及它在协议栈中的位置。

HTTP 演变

HTTP 是在 20 世纪 90 年代早期出现的，并且已经被扩充了多次。本文会回顾 HTTP 的发展史，描述 HTTP/0.9、HTTP/1.0、HTTP/1.1 和最新的 HTTP/2，以及这些年来出现的一些新特性。

资源和 URI

简单介绍了资源，标示符和 Web 位置的概念。

辨别 Web 资源

描述了 Web 资源是怎样被引用和找到的。

数据 URL

一种特殊的 URI，它直接包含了资源的内容。数据 URL 使用起来非常方便，但也有一些弊端。

资源 URL (en-US)
非标准

资源 URL（以 `resource` 前缀）是被 Firefox 和 Firefox 浏览器拓展插件用来内在地加载资源，当然有些资源在浏览器连接的网站上存在的。

MIME 类型

从 HTTP/1.0 开始，HTTP 协议允许传输不同类型的内容。本文解释了如何通过使用 `Content-Type` 首部和 MIME 标准实现这一过程的。

在 www 和非 www URL 间选择

在是否使用 www 前缀的域名这一问题上，本文解释了不同选择的影响，并给出了具体的实施方法。

HTTP 会话流程

本文描述了一个经典的 HTTP 会话：当你在浏览器中点击一个链接或在网页中加载一张图片时，背后到底发生了什么。

HTTP 消息

在请求和响应中传输的 HTTP 消息具有非常清晰的结构；本文介绍了消息的结构、设计目的以及其可能性。

HTTP/2 中的帧及消息结构

HTTP/2 用二进制帧来压缩和表示 HTTP/1.x 中的消息。本文解释了 HTTP/2 中的帧结构，其设计目的以及编码方式。

HTTP/1.x 的连接管理

HTTP/1.1 是首个支持持久化连接和管道化的 HTTP 版本。本文介绍了这两个概念。

HTTP/2 的连接管理

HTTP/2 完全改变了连接创建和维护的方式：本文介绍了 HTTP 帧是怎样支持多路复用并解决之前的 HTTP 版本中存在的队头阻塞问题的。

内容协商

HTTP 引入了一组以 `Accept` 开头的首部，作为浏览器用来声明它们期望得到的格式，语言或编码的方法。本文讲述了这一过程是怎样实现的，以及服务器是怎样处理并选择恰当的响应的。


HTTP 缓存 - HTTP

HTTP 缓存
=======

概览
--

HTTP 缓存会存储与请求关联的响应，并将存储的响应复用于后续请求。

可复用性有几个优点。首先，由于不需要将请求传递到源服务器，因此客户端和缓存越近，响应速度就越快。最典型的例子是浏览器本身为浏览器请求存储缓存。

此外，当响应可复用时，源服务器不需要处理请求——因为它不需要解析和路由请求、根据 cookie 恢复会话、查询数据库以获取结果或渲染模板引擎。这减少了服务器上的负载。

缓存的正确操作对系统的稳定运行至关重要。

不同种类的缓存
-------

在 HTTP Caching 标准中，有两种不同类型的缓存：**私有缓存**和**共享缓存**。

### 私有缓存

私有缓存是绑定到特定客户端的缓存——通常是浏览器缓存。由于存储的响应不与其他客户端共享，因此私有缓存可以存储该用户的个性化响应。

另一方面，如果个性化内容存储在私有缓存以外的缓存中，那么其他用户可能能够检索到这些内容——这可能会导致无意的信息泄露。

如果响应包含个性化内容并且你只想将响应存储在私有缓存中，则必须指定 `private` 指令。

```http
Cache-Control: private
```

个性化内容通常由 cookie 控制，但 cookie 的存在并不能表明它是私有的，因此单独的 cookie 不会使响应成为私有的。

请注意，如果响应具有 `Authorization` 标头，则不能将其存储在私有缓存（或共享缓存，除非 Cache-Control 指定的是 `public`）中。

### 共享缓存

共享缓存位于客户端和服务器之间，可以存储能在用户之间共享的响应。共享缓存可以进一步细分为**代理缓存**和**托管缓存**。

#### 代理缓存

除了访问控制的功能外，一些代理还实现了缓存以减少网络流量。这通常不由服务开发人员管理，因此必须由恰当的 HTTP 标头等控制。然而，在过去，过时的代理缓存实现——例如没有正确理解 HTTP 缓存标准的实现——经常给开发人员带来问题。

**Kitchen-sink 标头**如下所示，用于尝试解决不理解当前 HTTP 缓存规范指令（如 `no-store`）的“旧且未更新的代理缓存”的实现。

```http
Cache-Control: no-store, no-cache, max-age=0, must-revalidate, proxy-revalidate
```

然而，近年来，随着 HTTPS 变得越来越普遍，客户端/服务器通信变得加密，在许多情况下，路径中的代理缓存只能传输响应而不能充当缓存。因此，在这种情况下，无需担心甚至无法看到响应的过时代理缓存的实现。

另一方面，如果 TLS 桥接代理通过在 PC 上安装来自组织管理的 CA (en-US) 证书，以中间人方式解密所有通信，并执行访问控制等，则可以查看响应的内容并将其缓存。但是，由于证书透明度（certificate transparency）在最近几年变得很普遍，并且一些浏览器只允许使用证书签署时间戳（signed certificate timestamp）颁发的证书，因此这种方法需要应用于企业策略。在这样的受控环境中，无需担心代理缓存“已过时且未更新”。

#### 托管缓存

托管缓存由服务开发人员明确部署，以降低源服务器负载并有效地交付内容。示例包括反向代理、CDN 和 service worker 与缓存 API 的组合。

托管缓存的特性因部署的产品而异。在大多数情况下，你可以通过 `Cache-Control` 标头和你自己的配置文件或仪表板来控制缓存的行为。

例如，HTTP 缓存规范本质上没有定义显式删除缓存的方法——但是使用托管缓存，可以通过仪表板操作、API 调用、重新启动等实时删除已经存储的响应。这允许更主动的缓存策略。

也可以忽略标准 HTTP 缓存规范协议以支持显式操作。例如，可以指定以下内容以选择退出私有缓存或代理缓存，同时使用你自己的策略仅在托管缓存中进行缓存。

```http
Cache-Control: no-store
```

例如，Varnish Cache 使用 VCL（Varnish Configuration Language，一种 DSL (en-US)）逻辑来处理缓存存储，而 service worker 结合缓存 API 允许你在 JavaScript 中创建该逻辑。

这意味着如果托管缓存故意忽略 `no-store` 指令，则无需将其视为“不符合”标准。你应该做的是，避免使用 kitchen-sink 标头，但请仔细阅读你正在使用的任何托管缓存机制的文档，并确保你选择的方式可以正确的控制缓存。

请注意，某些 CDN 提供自己的标头，这些标头仅对该 CDN 有效（例如，`Surrogate-Control`）。目前，正在努力定义一个 `CDN-Cache-Control` 标头来标准化这些标头。

![缓存的类型](/zh-CN/docs/Web/HTTP/Caching/type-of-cache.png)

启发式缓存
-----

HTTP 旨在尽可能多地缓存，因此即使没有给出 `Cache-Control`，如果满足某些条件，响应也会被存储和重用。这称为**启发式缓存**。

例如，采取以下响应。此回复最后一次更新是在 1 年前。

```http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Last-Modified: Tue, 22 Feb 2021 22:22:22 GMT

<!doctype html>
…
```

试探性地知道，整整一年没有更新的内容在那之后的一段时间内不会更新。因此，客户端存储此响应（尽管缺少 `max-age`）并重用它一段时间。复用多长时间取决于实现，但规范建议存储后大约 10%（在本例中为 0.1 年）的时间。

启发式缓存是在 `Cache-Control` 被广泛采用之前出现的一种解决方法，基本上所有响应都应明确指定 `Cache-Control` 标头。

基于 age 的缓存策略
------------

存储的 HTTP 响应有两种状态：**fresh** 和 **stale**。*fresh* 状态通常表示响应仍然有效，可以重复使用，而 *stale* 状态表示缓存的响应已经过期。

确定响应何时是 fresh 的和何时是 stale 的标准是 **age**。在 HTTP 中，age 是自响应生成以来经过的时间。这类似于其他缓存机制中的 TTL (en-US)。

以下面的示例响应为例（604800 秒是一周）：

```http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Cache-Control: max-age=604800

<!doctype html>
…
```

存储示例响应的缓存会计算响应生成后经过的时间，并将结果用作响应的 *age*。

对于该示例的响应，`max-age` 的含义如下：

* 如果响应的 age *小于*一周，则响应为 *fresh*。
* 如果响应的 age *超过*一周，则响应为 *stale*。

只要存储的响应保持有效（fresh），它将用于兑现客户端请求。

当响应存储在共享缓存中时，有必要通知客户端响应的 age。继续看示例，如果共享缓存将响应存储了一天，则共享缓存将向后续客户端请求发送以下响应。

```http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Cache-Control: max-age=604800
Age: 86400

<!doctype html>
…
```

收到该响应的客户端会发现它在剩余的 518400 秒内是有效的，这是响应的 `max-age` 和 `Age` 之间的差异。

Expires 或 max-age
-----------------

在 HTTP/1.0 中，有效期是通过 `Expires` 标头来指定的。

`Expires` 标头使用明确的时间而不是通过指定经过的时间来指定缓存的生命周期。

```http
Expires: Tue, 28 Feb 2022 22:22:22 GMT
```

但是时间格式难以解析，也发现了很多实现的错误，有可能通过故意偏移系统时钟来诱发问题；因此，在 HTTP/1.1 中，`Cache-Control` 采用了 `max-age`——用于指定经过的时间。

如果 `Expires` 和 `Cache-Control: max-age` 都可用，则将 `max-age` 定义为首选。因此，由于 HTTP/1.1 已被广泛使用，无需特地提供 `Expires`。

Vary 响应
-------

区分响应的方式本质上是基于它们的 URL：

![使用 url 作为键](/zh-CN/docs/Web/HTTP/Caching/keyed-with-url.png)

但是响应的内容并不总是相同的，即使它们具有相同的 URL。特别是在执行内容协商时，来自服务器的响应可能取决于 `Accept`、`Accept-Language` 和 `Accept-Encoding` 请求标头的值。

例如，对于带有 `Accept-Language: en` 标头并已缓存的英语内容，不希望再对具有 `Accept-Language: ja` 请求标头的请求重用该缓存响应。在这种情况下，你可以通过在 `Vary` 标头的值中添加“`Accept-Language`”，根据语言单独缓存响应。

```http
Vary: Accept-Language
```

这会导致缓存基于响应 URL 和 `Accept-Language`请求标头的组合进行键控——而不是仅仅基于响应 URL。

![使用 url 和语言作为键](/zh-CN/docs/Web/HTTP/Caching/keyed-with-url-and-language.png)

此外，如果你基于用户代理提供内容优化（例如，响应式设计），你可能会想在 `Vary` 标头的值中包含“`User-Agent`”。但是，`User-Agent` 请求标头通常具有非常多的变体，这大大降低了缓存被重用的机会。因此，如果可能，请考虑一种基于特征检测而不是基于 `User-Agent` 请求标头来改变行为的方法。

对于使用 cookie 来防止其他人重复使用缓存的个性化内容的应用程序，你应该指定 `Cache-Control: private` 而不是为 `Vary` 指定 cookie。

验证响应
----

过时的响应不会立即被丢弃。HTTP 有一种机制，可以通过询问源服务器将陈旧的响应转换为新的响应。这称为**验证**，有时也称为**重新验证**。

验证是通过使用包含 `If-Modified-Since` 或 `If-None-Match` 请求标头的**条件请求**完成的。

### If-Modified-Since

以下响应在 22:22:22 生成，`max-age` 为 1 小时，因此你知道它在 23:22:22 之前是有效的。

```http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT
Cache-Control: max-age=3600

<!doctype html>
…
```

到 23:22:22 时，响应会过时并且不能重用缓存。因此，下面的请求显示客户端发送带有 `If-Modified-Since` 请求标头的请求，以询问服务器自指定时间以来是否有任何的改变。

```http
GET /index.html HTTP/1.1
Host: example.com
Accept: text/html
If-Modified-Since: Tue, 22 Feb 2022 22:00:00 GMT
```

如果内容自指定时间以来没有更改，服务器将响应 `304 Not Modified`。

由于此响应仅表示“没有变化”，因此没有响应主体——只有一个状态码——因此传输大小非常小。

```http
HTTP/1.1 304 Not Modified
Content-Type: text/html
Date: Tue, 22 Feb 2022 23:22:22 GMT
Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT
Cache-Control: max-age=3600
```

收到该响应后，客户端将存储的过期响应恢复为有效的，并可以在剩余的 1 小时内重复使用它。

服务器可以从操作系统的文件系统中获取修改时间，这对于提供静态文件的情况来说是比较容易做到的。但是，也存在一些问题；例如，时间格式复杂且难以解析，分布式服务器难以同步文件更新时间。

为了解决这些问题，`ETag` 响应标头被标准化作为替代方案。

### ETag/If-None-Match

`ETag` 响应标头的值是服务器生成的任意值。服务器对于生成值没有任何限制，因此服务器可以根据他们选择的任何方式自由设置值——例如主体内容的哈希或版本号。

举个例子，如果 `ETag` 标头使用了 hash 值，`index.html` 资源的 hash 值是 `deadbeef`，响应如下：

```http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
ETag: "deadbeef"
Cache-Control: max-age=3600

<!doctype html>
…
```

如果该响应是陈旧的，则客户端获取缓存响应的 `ETag` 响应标头的值，并将其放入 `If-None-Match` 请求标头中，以询问服务器资源是否已被修改：

```http
GET /index.html HTTP/1.1
Host: example.com
Accept: text/html
If-None-Match: "deadbeef"
```

如果服务器为请求的资源确定的 `ETag` 标头的值与请求中的 `If-None-Match` 值相同，则服务器将返回 `304 Not Modified`。

但是，如果服务器确定请求的资源现在应该具有不同的 `ETag` 值，则服务器将其改为 `200 OK` 和资源的最新版本进行响应。

**备注：** 在评估如何使用 `ETag` 和 `Last-Modified` 时，请考虑以下几点：在缓存重新验证期间，如果 `ETag` 和 `Last-Modified` 都存在，则 `ETag` 优先。因此，如果你只考虑缓存，你可能会认为 `Last-Modified` 是不必要的。然而，`Last-Modified` 不仅仅对缓存有用；相反，它是一个标准的 HTTP 标头，内容管理 (CMS) 系统也使用它来显示上次修改时间，由爬虫调整爬取频率，以及用于其他各种目的。所以考虑到整个 HTTP 生态系统，最好同时提供 `ETag` 和 `Last-Modified`。

### 强制重新验证

如果你不希望重复使用响应，而是希望始终从服务器获取最新内容，则可以使用 `no-cache` 指令强制验证。

通过在响应中添加 `Cache-Control: no-cache` 以及 `Last-Modified` 和 `ETag`——如下所示——如果请求的资源已更新，客户端将收到 `200 OK` 响应，否则，如果请求的资源尚未更新，则会收到 `304 Not Modified` 响应。

```http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT
ETag: deadbeef
Cache-Control: no-cache

<!doctype html>
…
```

`max-age=0` 和 `must-revalidate` 的组合与 `no-cache` 具有相同的含义。

```http
Cache-Control: max-age=0, must-revalidate
```

`max-age=0` 意味着响应立即过时，而 `must-revalidate` 意味着一旦过时就不得在没有重新验证的情况下重用它——因此，结合起来，语义似乎与 `no-cache` 相同。

然而，`max-age=0` 的使用是解决 HTTP/1.1 之前的许多实现无法处理 `no-cache` 这一指令——因此为了解决这个限制，`max-age=0` 被用作解决方法。

但是现在符合 HTTP/1.1 的服务器已经广泛部署，没有理由使用 `max-age=0` 和 `must-revalidate` 组合——你应该只使用 `no-cache`。

不使用缓存
-----

`no-cache` 指令不会阻止响应的存储，而是阻止在没有重新验证的情况下重用响应。

如果你不希望将响应存储在任何缓存中，请使用 `no-store`。

```http
Cache-Control: no-store
```

但是，一般来说，实践中“不缓存”的原因满足以下情况：

* 出于隐私原因，不希望特定客户以外的任何人存储响应。
* 希望始终提供最新信息。
* 不知道在过时的实现中会发生什么。

在这种情况下，`no-store` 并不总是最合适的指令。

以下部分更详细地介绍了这些情况。

### 不与其他用户共享

如果具有个性化内容的响应意外地对缓存的其他用户可见，那将是有问题的。

在这种情况下，使用 `private` 指令将导致个性化响应仅与特定客户端一起存储，而不会泄露给缓存的任何其他用户。

```http
Cache-Control: private
```

在这种情况下，即使设置了 `no-store`，也必须设置 `private`。

### 每次都提供最新的内容

`no-store` 指令阻止存储响应，但不会删除相同 URL 的任何已存储响应。

换句话说，如果已经为特定 URL 存储了旧响应，则返回 `no-store` 不会阻止旧响应被重用。

但是，`no-cache` 指令将强制客户端在重用任何存储的响应之前发送验证请求。

```http
Cache-Control: no-cache
```

如果服务端不支持条件请求，你可以强制客户端每次都访问服务端，总是得到最新的 200 OK 响应。

### 兼容过时的实现

作为忽略 `no-store` 的过时实现的解决方法，你可能会看到使用了诸如以下内容的 kitchen-sink 标头：

```http
Cache-Control: no-store, no-cache, max-age=0, must-revalidate, proxy-revalidate
```

推荐使用 `no-cache` 作为处理这种过时的实现的替代方案，如果从一开始就设置 `no-cache` 就没问题，因为服务器总是会收到请求。

如果你关心的是共享缓存，你可以通过添加 `private` 来防止意外缓存：

```http
Cache-Control: no-cache, private
```

### `no-store` 丢失了什么

你可能认为添加 `no-store` 是选择退出缓存的正确方法。

但是，不建议随意授予 `no-store`，因为你失去了 HTTP 和浏览器所拥有的许多优势，包括浏览器的后退/前进缓存。

因此，要获得 Web 平台的全部功能集的优势，最好将 `no-cache` 与 `private` 结合使用。

重新加载和强制重新加载
-----------

可以对请求和响应执行验证。

**重新加载**和**强制重新加载**操作是从浏览器端执行验证的常见示例。

### 重新加载

为了从页面错误中恢复或更新到最新版本的资源，浏览器为用户提供了重新加载功能。

在浏览器重新加载期间发送的 HTTP 请求的简化视图如下所示：

```http
GET / HTTP/1.1
Host: example.com
Cache-Control: max-age=0
If-None-Match: "deadbeef"
If-Modified-Since: Tue, 22 Feb 2022 20:20:20 GMT
```

（来自 Chrome、Edge 和 Firefox 的请求看起来很像上面的；来自 Safari 的请求看起来会有点不同。）

请求中的 `max-age=0` 指令指定“重用 age 为 0 或更少的响应”——因此，中间存储的响应不会被重用。

请求通过 `If-None-Match` 和 `If-Modified-Since` 进行验证。

该行为也在 Fetch 标准中定义，并且可以通过在缓存模式设置为 `no-cache` 的情况下，在 JavaScript 中调用 `fetch()` 来重现（注意 `reload` 不是这种情况下的正确模式）：

```js
// 注意：“reload”不是正常重新加载的正确模式；“no-cache”才是
fetch("/", { cache: "no-cache" });
```

### 强制重新加载

出于向后兼容的原因，浏览器在重新加载期间使用 `max-age=0`——因为在 HTTP/1.1 之前的许多过时的实现中不理解 `no-cache`。但是在这个用例中，`no-cache` 已被支持，并且**强制重新加载**是绕过缓存响应的另一种方法。

浏览器**强制重新加载**期间的 HTTP 请求如下所示：

```http
GET / HTTP/1.1
Host: example.com
Pragma: no-cache
Cache-Control: no-cache
```

（来自 Chrome、Edge 和 Firefox 的请求看起来很像上面的；来自 Safari 的请求看起来会有点不同。）

由于这不是带有 `no-cache` 的条件请求，因此你可以确定你会从源服务器获得 `200 OK`。

该行为也在 Fetch 标准中定义，并且可以通过在缓存模式设置为 `reload` 的情况下，在 JavaScript 中调用 `fetch()` 来重现（注意它不是 `force-reload`）：

```js
// 注意：“reload”——而不是“no-cache”——是“强制重新加载”的正确模式
fetch("/", { cache: "reload" });
```

### 避免重新验证

永远不会改变的内容应该被赋予一个较长的 `max-age`，方法是使用缓存破坏——也就是说，在请求 URL 中包含版本号、哈希值等。

但是，当用户重新加载时，即使服务器知道内容是不可变的，也会发送重新验证请求。

为了防止这种情况，`immutable` 指令可用于明确指示不需要重新验证，因为内容永远不会改变。

```http
Cache-Control: max-age=31536000, immutable
```

这可以防止在重新加载期间进行不必要的重新验证。

请注意，Chrome 已更改其实现而不是实现该指令，因此重新验证不是在重新加载子资源期间执行。

删除存储的响应
-------

基本上没有办法删除用很长的 `max-age` 存储的响应。

想象一下，来自 `https://example.com/` 的以下响应已被存储。

```http
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Cache-Control: max-age=31536000

<!doctype html>
…
```

一旦响应在服务器上过期，你可能希望覆盖该响应，但是一旦存储响应，服务器就无法执行任何操作——因为由于缓存，不再有请求到达服务器。

规范中提到的方法之一是使用不安全的方法（例如 POST）发送对同一 URL 的请求，但对于许多客户端而言，通常很难故意这样做。

还有一个 `Clear-Site-Data: cache` 标头和值的规范，但并非所有浏览器都支持它——即使使用它，它也只会影响浏览器缓存，而不会影响中间缓存。

因此，除非用户手动执行重新加载、强制重新加载或清除历史操作，否则应该假设任何存储的响应都将保留其 `max-age` 期间。

缓存减少了对服务器的访问，这意味着服务器失去了对该 URL 的控制。如果服务器不想失去对 URL 的控制——例如，在资源被频繁更新的情况下——你应该添加 `no-cache`，以便服务器始终接收请求并发送预期的响应。

请求折叠
----

共享缓存主要位于源服务器之前，旨在减少到源服务器的流量。

因此，如果多个相同的请求同时到达共享缓存，中间缓存将代表自己将单个请求转发到源，然后源可以将结果重用于所有客户端。这称为***请求折叠***。

当请求同时到达时会发生请求折叠，因此即使响应中给出了 `max-age=0` 或 `no-cache`，它也会被重用。

如果响应是针对特定用户个性化的，并且你不希望它在折叠中共享，则应添加 `private` 指令：

![请求折叠](/zh-CN/docs/Web/HTTP/Caching/request-collapse.png)

常见的缓存模式
-------

`Cache-Control` 规范中有很多指令，可能很难全部理解。但是大多数网站都可以通过几种模式的组合来覆盖。

本节介绍设计缓存的常见模式。

### 默认设置

如上所述，缓存的默认行为（即对于没有 `Cache-Control` 的响应）不是简单的“不缓存”，而是根据所谓的“启发式缓存”进行隐式缓存。

为了避免这种启发式缓存，最好显式地为所有响应提供一个默认的 `Cache-Control` 标头。

为确保默认情况下始终传输最新版本的资源，通常的做法是让默认的 `Cache-Control` 值包含 `no-cache`：

```http
Cache-Control: no-cache
```

另外，如果服务实现了 cookie 或其他登录方式，并且内容是为每个用户个性化的，那么也必须提供 `private`，以防止与其他用户共享：

```http
Cache-Control: no-cache, private
```

### 缓存破坏

最适合缓存的资源是静态不可变文件，其内容永远不会改变。而对于会变化的资源，通常的最佳实践是每次内容变化时都改变 URL，这样 URL 单元可以被缓存更长的时间。

例如，考虑以下 HTML：

```html
<script src="bundle.js"></script>
<link rel="stylesheet" href="build.css" />
<body>
  hello
</body>
```

在现代 Web 开发中，JavaScript 和 CSS 资源会随着开发的进展而频繁更新。此外，如果客户端使用的 JavaScript 和 CSS 资源的版本不同步，则显示将中断。

所以上面的 HTML 用 `max-age` 缓存 `bundle.js` 和 `build.css` 变得很困难。

因此，你可以使用包含基于版本号或哈希值的更改部分的 URL 来提供 JavaScript 和 CSS。一些方法如下所示。
```

# version in filename
bundle.v123.js

# version in query
bundle.js?v=123

# hash in filename
bundle.YsAIAAAA-QG4G6kCMAMBAAAAAAAoK.js

# hash in query
bundle.js?v=YsAIAAAA-QG4G6kCMAMBAAAAAAAoK
```

由于缓存根据它们的 URL 来区分资源，因此如果在更新资源时 URL 发生变化，缓存将不会再次被重用。

```html
<script src="bundle.v123.js"></script>
<link rel="stylesheet" href="build.v123.css" />
<body>
  hello
</body>
```

通过这种设计，JavaScript 和 CSS 资源都可以被缓存很长时间。那么 `max-age` 应该设置多长时间呢？QPACK 规范提供了该问题的答案。

QPACK 是一种用于压缩 HTTP 标头字段的标准，其中定义了常用字段值表。

一些常用的缓存头值如下所示。
```

36 cache-control max-age=0
37 cache-control max-age=604800
38 cache-control max-age=2592000
39 cache-control no-cache
40 cache-control no-store
41 cache-control public, max-age=31536000
```

如果你选择其中一个编号选项，则可以在通过 HTTP3 传输时将值压缩为 1 个字节。

数字“37”、“38”和“41”分别代表一周、一个月和一年。

因为缓存会在保存新条目时删除旧条目，所以一周后存储的响应仍然存在的可能性并不高——即使 `max-age` 设置为 1 周。因此，在实践中，你选择哪一种并没有太大的区别。

请注意，数字“41”具有最长的 `max-age`（1 年），但具有 `public`。

`public` 值具有使响应可存储的效果，即使存在 `Authorization` 标头。

**备注：** 只有在设置了 `Authorization` 标头时需要存储响应时才应使用 `public` 指令。否则不需要，因为只要给出了 `max-age`，响应就会存储在共享缓存中。

因此，如果响应是使用基本身份验证进行个性化的，`public` 的存在可能会导致问题。如果你对此感到担忧，你可以选择第二长的值 `38`（1 个月）。

```http
# response for bundle.v123.js

# If you never personalize responses via Authorization
Cache-Control: public, max-age=31536000

# If you can't be certain
Cache-Control: max-age=2592000
```

### 验证响应

不要忘记设置 `Last-Modified` 和 `ETag` 标头，以便在重新加载时不必重新传输资源。对于预构建的静态文件生成这些标头很容易。

这里的 `ETag` 值可能是文件的哈希值。

```http
# response for bundle.v123.js
Last-Modified: Tue, 22 Feb 2022 20:20:20 GMT
ETag: YsAIAAAA-QG4G6kCMAMBAAAAAAAoK
```

此外，可以添加 `immutable` 以防止重新加载时验证。

组合结果如下所示。

```http
# bundle.v123.js
200 OK HTTP/1.1
Content-Type: application/javascript
Content-Length: 1024
Cache-Control: public, max-age=31536000, immutable
Last-Modified: Tue, 22 Feb 2022 20:20:20 GMT
ETag: YsAIAAAA-QG4G6kCMAMBAAAAAAAoK
```

**缓存破坏**是一种通过在内容更改时更改 URL 来使响应在很长一段时间内可缓存的技术。该技术可以应用于所有子资源，例如图像。

**备注：** 在评估 `immutable` 和 QPACK 的使用时：如果你担心 `immutable` 会更改 QPACK 提供的预定义值，请考虑在这种情况下，`immutable` 部分可以通过将 `Cache-Control` 值分成两行来单独编码——尽管这取决于特定 QPACK 实现使用的编码算法。

```http
Cache-Control: public, max-age=31536000
Cache-Control: immutable
```

### 主要资源

与子资源不同，主资源不能使用缓存破坏，因为它们的 URL 不能像子资源 URL 一样被修饰。

如果存储以下 HTML 本身，即使在服务器端更新内容，也无法显示最新版本。

```html
<script src="bundle.v123.js"></script>
<link rel="stylesheet" href="build.v123.css" />
<body>
  hello
</body>
```

对于这种情况，`no-cache` 将是合适的——而不是 `no-store`——因为我们不想存储 HTML，而只是希望它始终是最新的。

此外，添加 `Last-Modified` 和 `ETag` 将允许客户端发送条件请求，如果 HTML 没有更新，则可以返回 `304 Not Modified`：

```http
200 OK HTTP/1.1
Content-Type: text/html
Content-Length: 1024
Cache-Control: no-cache
Last-Modified: Tue, 22 Feb 2022 20:20:20 GMT
ETag: AAPuIbAOdvAGEETbgAAAAAAABAAE
```

该设置适用于非个性化 HTML，但对于使用 cookie 进行个性化的响应（例如，在登录后），不要忘记同时指定 `private`：

```http
200 OK HTTP/1.1
Content-Type: text/html
Content-Length: 1024
Cache-Control: no-cache, private
Last-Modified: Tue, 22 Feb 2022 20:20:20 GMT
ETag: AAPuIbAOdvAGEETbgAAAAAAABAAE
Set-Cookie: \_\_Host-SID=AHNtAyt3fvJrUL5g5tnGwER; Secure; Path=/; HttpOnly
```

`favicon.ico`、`manifest.json`、`.well-known` 和无法使用缓存破坏更改 URL 的 API 端点也是如此。

大多数 Web 内容都可以通过上述两种模式的组合来覆盖。

### 有关托管缓存的更多信息

使用前面章节描述的方法，子资源可以通过缓存破坏来缓存很长时间，但主资源（通常是 HTML 文档）不能。

缓存主要资源很困难，因为仅使用 HTTP 缓存规范中的标准指令，在服务器上更新内容时无法主动删除缓存内容。

但是，可以通过部署托管缓存（例如 CDN 或 service worker）来实现。

例如，允许通过 API 或仪表板操作清除缓存的 CDN 将通过存储主要资源并仅在服务器上发生更新时显式清除相关缓存来实现更积极的缓存策略。

如果 service worker 可以在服务器上发生更新时删除缓存 API 中的内容，它也可以这样做。

有关更多信息，请参阅 CDN 文档，并查阅 service worker 文档。

参见
--

* RFC 9111: Hypertext Transfer Protocol (HTTP/1.1): Caching
* Caching Tutorial - Mark Nottingham


内容安全策略（CSP） - HTTP

内容安全策略（CSP）
===========

**内容安全策略**（CSP）是一个额外的安全层，用于检测并削弱某些特定类型的攻击，包括跨站脚本（XSS）和数据注入攻击等。无论是数据盗取、网站内容污染还是恶意软件分发，这些攻击都是主要的手段。

CSP 被设计成完全向后兼容（除 CSP2 在向后兼容有明确提及的不一致; 更多细节查看这里 章节 1.1）。不支持 CSP 的浏览器也能与实现了 CSP 的服务器正常工作，反之亦然：不支持 CSP 的浏览器只会忽略它，如常运行，默认为网页内容使用标准的同源策略。如果网站不提供 CSP 标头，浏览器也使用标准的同源策略。

为使 CSP 可用，你需要配置你的网络服务器返回 `Content-Security-Policy` HTTP 标头（有时你会看到 `X-Content-Security-Policy` 标头，但那是旧版本，并且你无须再如此指定它）。

除此之外，`<meta>` 元素也可以被用来配置该策略，例如

```html
<meta
 http-equiv="Content-Security-Policy"
 content="default-src 'self'; img-src https://\*; child-src 'none';" />
```

**备注：** 某些功能（例如发送 CSP 违规报告）仅在使用 HTTP 标头时可用。

威胁
--

### 缓解跨站脚本攻击

CSP 的主要目标是减少和报告 XSS 攻击。XSS 攻击利用了浏览器对于从服务器所获取的内容的信任。恶意脚本在受害者的浏览器中得以运行，因为浏览器信任其内容来源，即使有的时候这些脚本并非来自于它本该来的地方。

CSP 通过指定有效域——即浏览器认可的可执行脚本的有效来源——使服务器管理者有能力减少或消除 XSS 攻击所依赖的载体。一个 CSP 兼容的浏览器将会仅执行从白名单域获取到的脚本文件，忽略所有的其他脚本（包括内联脚本和 HTML 的事件处理属性）。

作为一种终极防护形式，始终不允许执行脚本的站点可以选择全面禁止脚本执行。

### 缓解数据包嗅探攻击

除限制可以加载内容的域，服务器还可指明哪种协议允许使用；比如（从理想化的安全角度来说），服务器可指定所有内容必须通过 HTTPS 加载。一个完整的数据安全传输策略不仅强制使用 HTTPS 进行数据传输，也为所有的 cookie 标记 `secure` 标识，并且提供自动的重定向使得 HTTP 页面导向 HTTPS 版本。网站也可以使用 `Strict-Transport-Security` HTTP 标头确保连接它的浏览器只使用加密通道。

使用 CSP
------

配置内容安全策略涉及到添加 `Content-Security-Policy` HTTP 标头到一个页面，并配置相应的值，以控制用户代理（浏览器等）可以为该页面获取哪些资源。比如一个可以上传文件和显示图片页面，应该允许图片来自任何地方，但限制表单的 action 属性只可以赋值为指定的端点。一个经过恰当设计的内容安全策略应该可以有效的保护页面免受跨站脚本攻击。本文阐述如何恰当的构造这样的标头，并提供了一些例子。

### 制定策略

你可以使用 `Content-Security-Policy` HTTP 标头来指定你的策略，像这样：

```http
Content-Security-Policy: policy
```

策略（policy）参数是一个包含了各种描述你的 CSP 策略指令的字符串。

### 编写策略

策略由一系列策略指令所组成，每个策略指令都描述了针对某个特定资源的类型以及策略生效的范围。你的策略应当包含一个 `default-src` 策略指令，在其他资源类型没有符合自己的策略时应用该策略（有关完整列表，请查看 `default-src` 指令的描述）。一个策略可以包含 `default-src` 或者 `script-src` (en-US) 指令来防止内联脚本运行，并杜绝 `eval()` 的使用。一个策略也可包含一个 `default-src` 或 `style-src` (en-US) 指令去限制来自一个 `<style>` 元素或者 `style` 属性的內联样式。对于不同类型的项目都有特定的指令，因此每种类型都可以有自己的指令，包括字体、frame、图像、音频和视频媒体、script 和 worker。

示例：常见用例
-------

这一部分提供了一些常用的安全策略方案示例。

### 示例 1

一个网站管理者想要所有内容均来自站点的同一个源（不包括其子域名）。

```http
Content-Security-Policy: default-src 'self'
```

### 示例 2

一个网站管理者允许内容来自信任的域名及其子域名（域名不必须与 CSP 设置所在的域名相同）。

```http
Content-Security-Policy: default-src 'self' \*.trusted.com
```

### 示例 3

一个网站管理者允许网页应用的用户在他们自己的内容中包含来自任何源的图片，但是限制音频或视频需从信任的资源提供者，所有脚本必须从特定主机服务器获取可信的代码。

```http
Content-Security-Policy: default-src 'self'; img-src \*; media-src media1.com media2.com; script-src userscripts.example.com
```

在这里，各种内容默认仅允许从文档所在的源获取，但存在如下例外：

* 图片可以从任何地方加载 (注意“\*”通配符)。
* 多媒体文件仅允许从 media1.com 和 media2.com 加载（不允许从这些站点的子域名）。
* 可运行脚本仅允许来自于 userscripts.example.com。
### 示例 4

一个线上银行网站的管理者想要确保网站的所有内容都要通过 SSL 方式获取，以避免攻击者窃听用户发出的请求。

```http
Content-Security-Policy: default-src https://onlinebanking.jumbobank.com
```

该服务器仅允许通过 HTTPS 方式并仅从 onlinebanking.jumbobank.com 域名来访问文档。

### 示例 5

一个在线邮箱的管理者想要允许在邮件里包含 HTML，同样图片允许从任何地方加载，但不允许 JavaScript 或者其他潜在的危险内容（从任意位置加载）。

```http
Content-Security-Policy: default-src 'self' \*.mailsite.com; img-src \*
```

注意这个示例并未指定 `script-src` (en-US)；在此 CSP 示例中，站点通过 `default-src` 指令的对其进行配置，这也同样意味着脚本文件仅允许从原始服务器获取。

对策略进行测试
-------

为降低部署成本，CSP 可以部署为仅报告（report-only）模式。在此模式下，CSP 策略不是强制性的，但是任何违规行为将会报告给一个指定的 URI 地址。此外，仅报告标头可以用来测试对策略未来的修订，而不用实际部署它。

你可以用 `Content-Security-Policy-Report-Only` HTTP 标头来指定你的策略，像这样：

```http
Content-Security-Policy-Report-Only: policy
```

如果 `Content-Security-Policy-Report-Only` 标头和 `Content-Security-Policy` 同时出现在一个响应中，两个策略均有效。在 `Content-Security-Policy` 标头中指定的策略有强制性，而 `Content-Security-Policy-Report-Only` 中的策略仅产生报告而不具有强制性。

支持 CSP 的浏览器将始终对于每个企图违反你所建立的策略都发送违规报告，如果策略里包含一个有效的`report-uri` (en-US) 指令。

启用报告
----

默认情况下，违规报告并不会发送。为启用发送违规报告，你需要指定 `report-to` 策略指令，并提供至少一个 URI 地址去递交报告：

```http
Content-Security-Policy: default-src 'self'; report-uri http://reportcollector.example.com/collector.cgi
```

然后你需要设置你的服务器能够接收报告；使其能够以你认为恰当的方式存储并处理这些报告。

违规报告的语法
-------

作为报告的 JSON 对象和 `application/csp-report` `Content-Type` 一起发送，并包含了以下数据：

`blocked-uri`

被 CSP 阻止的资源 URI。如果被阻止的 URI 来自不同的源而非 `document-uri`，那么被阻止的资源 URI 会被删减，仅保留协议、主机和端口号。

`disposition`

根据 `Content-Security-Policy-Report-Only` 和 `Content-Security-Policy` 标头使用情况的不同，值为 `"enforce"` 或 `"report"`。

`document-uri`

发生违规的文档的 URI。

`effective-directive`

导致违规行为发生的指令。一些浏览器可能提供不同的值，例如 Chrome 提供 `style-src-elem` 或 `style-src-attr`，即使实际执行的指令是 `style-src`。

`original-policy`

由 `Content-Security-Policy` HTTP 标头指定的原始策略值。

`referrer`
已弃用

非标准

违规发生处的文档引用（地址）。

`script-sample`

导致该违规的内联代码、事件处理器或样式的前 40 个字符。只适用于 `script-src*` 或 `style-src*` 包含 `'report-sample'` 的情况。

`status-code`

全局对象被实例化的资源的 HTTP 状态代码。

`violated-directive`
已弃用

导致违反策略的指令。`violated-directive` 是 `effective-directive` 字段的历史名称，并包含相同的值。

违规报告的样本
-------

我们假设页面位于 `http://example.com/signup.html`。它使用如下策略，该策略禁止任何资源的加载，除了来自 `cdn.example.com` 的样式表。

```http
Content-Security-Policy: default-src 'none'; style-src cdn.example.com; report-uri /\_/csp-reports
```

`signup.html` 的 HTML 像这样：

```html
<!doctype html>
<html lang="en-US">
  <head>
    <meta charset="UTF-8" />
    <title>Sign Up</title>
    <link rel="stylesheet" href="css/style.css" />
  </head>
  <body>
    Here be content.
  </body>
</html>
```

你能看出其中错误吗？这里仅允许加载自 `cdn.example.com` 的样式表，然而该页面企图从自己的源（`http://example.com`）加载。当该文档被访问时，一个兼容 CSP 的浏览器将以 POST 请求的形式发送违规报告到 `http://example.com/_/csp-reports`，内容如下：

```json
{
  "csp-report": {
    "blocked-uri": "http://example.com/css/style.css",
    "disposition": "report",
    "document-uri": "http://example.com/signup.html",
    "effective-directive": "style-src-elem",
    "original-policy": "default-src 'none'; style-src cdn.example.com; report-to /\_/csp-reports",
    "referrer": "",
    "status-code": 200,
    "violated-directive": "style-src-elem"
  }
}
```

如你所见，该报告在 `blocked-uri` 字段中包含了违规资源的完整路径，但情况并非总是如此。比如，当 `signup.html` 试图从 `http://anothercdn.example.com/stylesheet.css` 加载 CSS 时，浏览器将*不会*包含完整路径，而只会保留源路径（`http://anothercdn.example.com`）。CSP 技术规范小组对此古怪行为给出了解释。大体上说，这样是为了防止泄露跨源资源的敏感信息。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

### 兼容性备注

在某些版本的 Safari 网络浏览器中存在一种特殊的不兼容性，即如果设置了内容安全策略标头，但没有设置相同来源（Same Origin）标头。浏览器将阻止自我托管的内容和网站外的内容，并错误地报告说这是由于内容安全政策不允许该内容。

参见
--

* `Content-Security-Policy` HTTP 标头
* `Content-Security-Policy-Report-Only` HTTP 标头
* WebExtensions 中的安全策略
* Web Worker 中的 CSP
* 隐私、权限和信息安全 (en-US)
* CSP 评估器——评估你的内容安全策略


HTTP 概述 - HTTP

HTTP 概述
=======

**HTTP** 是一种用作获取诸如 HTML 文档这类资源的协议。它是 Web 上进行任何数据交换的基础，同时，也是一种客户端—服务器（client-server）协议，也就是说，请求是由接受方——通常是浏览器——发起的。一个完整网页文档是由获取到的不同文档组件——像是文本、布局描述、图片、视频、脚本等——重新构建出来的。

![一个网页文档是多种不同资源的组合](/zh-CN/docs/Web/HTTP/Overview/fetching_a_page.png)

客户端与服务端之间通过交换一个个独立的消息（而非数据流）进行通信。由客户端——通常是个浏览器——发出的消息被称作*请求*（request），由服务端发出的应答消息被称作*响应*（response）。

![HTTP 作为应用层协议，处于 TCP（传输层）和 IP（网络层）之上，表示层之下](/zh-CN/docs/Web/HTTP/Overview/http-layers.png)

20 世纪 90 年代，HTTP 作为一套可扩展的协议被设计出来，并随时间不断演进。HTTP 是一种应用层的协议，通过 TCP，或者是 TLS——一种加密过的 TCP 连接——来发送，当然，理论上来说可以借助任何可靠的传输协议。受益于 HTTP 的可扩展性，时至今日，它不仅可以用来获取超文本文档，还可用来获取图片、视频或者向服务端发送信息，比如填写好的 HTML 表单。HTTP 还可以用来获取文档的部分内容，以便按需更新 Web 页面。

基于 HTTP 的组件系统
-------------

HTTP 是一个客户端—服务器协议：请求由一个实体，即用户代理（user agent），或是一个可以代表它的代理方（proxy）发出。大多数情况下，这个用户代理都是一个网页浏览器，不过它也可能是任何东西，比如一个爬取网页来充实、维护搜索引擎索引的机器爬虫。

每个请求都会被发送到一个服务端，它会处理这个请求并提供一个称作*响应*的回复。在客户端与服务端之间，还有许许多多的被称为代理的实体，履行不同的作用，例如充当网关或缓存。

![客户端与服务器间的链条](/zh-CN/docs/Web/HTTP/Overview/client-server-chain.png)

实际上，在浏览器和处理其请求的服务器之间，还有路由器、调制解调器等等许多计算机。归功于 Web 的分层设计，这些机器都隐藏在网络层和传输层内。而 HTTP 位于这些机器之上的应用层。虽然下面的层级在诊断网络问题时很重要，但在描述 HTTP 的设计时，它们大多是不相干的。

### 客户端：用户代理

*用户代理*是任何能够代表用户行为的工具。这类工具以浏览器为主，不过，它也可能是工程师和 Web 开发人员调试应用所使用的那些程序。

浏览器**总是**首先发起请求的那个实体，永远不会是服务端（不过，后来已经加入了一些机制，能够模拟出由服务端发起的消息）。

为了展现一个网页，浏览器需要发送最初的请求来获取描述这个页面的 HTML 文档。接着，解析文档，并发送数个其他请求，响应地获取可执行脚本、展示用的布局信息（CSS）以及其他页面内的资源（一般是图片和视频等）。然后，浏览器将这些资源整合到一起，展现出一个完整的文档，即 Web 页面。之后的阶段，浏览器中执行的脚本可以获取更多资源，同时浏览器相应地更新网页。

一个 Web 页面是一个超文本文档。这意味着有一部分展示的内容会是链接——可以被激活（通常是点击鼠标）来获取一个新的网页——用户可以通过这些链接指示用户代理并进行网络浏览。浏览器会将收到的指示转换成 HTTP 请求，并进一步解析 HTTP 响应，向用户提供清楚的回复。

### Web 服务端

在上述通信过程的另一侧是服务端，它*负责提供*客户端所请求的文档。一个服务端可以假装表现为仅有一台机器，但实际上，它可以是共享负载的一组服务器集群（负载均衡）或是其他类型的软件（如缓存、数据库服务、电商服务等），按需完整或部分地生成文档。

一个服务器可以不仅仅只有一台机器，而多个服务端软件实例也可部署在同一台机器上。利用 HTTP/1.1 和 `Host` 标头，它们甚至可以共用同一个 IP 地址。

### 代理

在浏览器和服务器之间，有许多计算机和设备参与传递了 HTTP 消息。依靠 Web 技术栈的层次化的结构，传递过程中的多数操作都位于传输层、网络层或物理层，它们对于 HTTP 应用层而言就是透明的，并默默地对网络性能产生着重要影响。还有一部分实体在应用层参与消息传递，一般被称为**代理**（Proxy）。代理可以是透明的，即转发它们收到的请求并不做任何修改，也可以表现得不透明，将它传递给服务端之前使用一些手段修改这个请求。代理可以发挥很多种作用：

* 缓存（可以是公开的也可以是私有的，如浏览器的缓存）
* 过滤（如反病毒扫描、家长控制...）
* 负载均衡（让多个服务器服务不同的请求）
* 认证（控制对不同资源的访问）
* 日志（使得代理可以存储历史信息）
HTTP 的基本性质
----------

### HTTP 是简约的

大体上看，HTTP 被设计得简单且易读，尽管在 HTTP/2 中，HTTP 消息被封装进帧（frame）这点引入了额外的复杂度。HTTP 报文能够被人读懂并理解，向开发者提供了更简单的测试方式，也对初学者降低了门槛。

### HTTP 是可扩展的

在 HTTP/1.0 中引入的 HTTP 标头（header）让协议扩展变得非常容易。只要服务端客户端之间对新标头的语义经过简单协商，新功能就可以被加入进来。

### HTTP 无状态，但并非无会话

HTTP 是无状态的：在同一个连接中，两个执行成功的请求之间是没有关系的。这就带来了一个问题，用户没有办法在同一个网站中进行连贯的交互，比如在电商网站中使用购物车功能。尽管 HTTP 根本上来说是无状态的，但借助 HTTP Cookie 就可使用有状态的会话。利用标头的扩展性，HTTP Cookie 被加进了协议工作流程，每个请求之间就能够创建会话，让每个请求都能共享相同的上下文信息或相同的状态。

### HTTP 和网络连接

一个网络连接是由传输层来控制的，因此从根本上说不属于 HTTP 的范畴。HTTP 协议并不需要下面的传输层协议是面向连接的，仅仅需要它是*可靠的*，或不会丢失消息（至少，某个情况下告知错误）。在互联网两个最常用的传输层协议中，TCP 是可靠的而 UDP 不是。HTTP 因此而依靠于 TCP 的标准，即面向链接的。

在客户端与服务端能够传递请求、响应之前，这两者间必须建立一个 TCP 链接，这个过程需要多次往返交互。HTTP/1.0 默认为每一对 HTTP 请求/响应都打开一个单独的 TCP 连接。当需要接连发起多个请求时，工作效率相比于它们之间共享同一个 TCP 连接要低。

为了减轻这个缺陷，HTTP/1.1 引入了*流水线*（已被证明难以实现）和*持久化连接*：可以通过 `Connection` 标头来部分控制底层的 TCP 连接。HTTP/2 则更进一步，通过在一个连接中复合多个消息，让这个连接始终平缓并更加高效。

为了设计一种更匹配 HTTP 的传输层协议，各种实验正在进行中。例如，Google 正在测试一种基于 UDP 构建，更可靠、高效的传输层协议——QUIC。

HTTP 能控制什么
----------

多年以来，良好的扩展性使得 HTTP 涉及到更多的 Web 功能与控制权。在 HTTP 诞生的早期，缓存和认证就可以由这个协议来处理了。而直到 2010 年，放行*同源限制*的能力才加入到协议里。

以下是可以被 HTTP 控制的常见特性。

* *缓存*：文档如何被缓存可以通过 HTTP 来控制。服务端能指示代理和客户端缓存哪些内容以及缓存多长时间，客户端能够指示中间的缓存代理来忽略已存储的文档。
* *开放同源限制*：为了阻止网络窥听和其它侵犯隐私的问题，浏览器强制在不同网站之间做了严格分割。只有来自于**相同来源**（same origin）的网页才能够获取一个网页的全部信息。这种限制有时对服务器是一种负担，服务器的 HTTP 标头可以减弱此类严格分离，使得一个网页可以是由源自不同地址的信息拼接而成。某些情况下，放开这些限制还有安全相关的考虑。
* *认证*：一些页面可能会被保护起来，仅让特定的用户进行访问。基本的认证功能可以直接由 HTTP 提供，既可以使用 `WWW-Authenticate` 或其他类似的标头，也可以用 HTTP cookie 来设置一个特定的会话。
* *代理服务器和隧道*：
 服务器或客户端常常是处于内网的，对其他计算机隐藏真实 IP 地址。因此 HTTP 请求就要通过代理服务器越过这个网络屏障。并非所有的代理都是 HTTP 代理，例如，SOCKS 协议就运作在更底层。其他的协议，比如 ftp，也能够被这些代理处理。
* *会话*：使用 HTTP Cookie 可以利用服务端的状态将不同请求联系在一起。这就创建了会话，尽管 HTTP 本身是无状态协议。这不仅仅对电商平台购物车很有用，也让任何网站都能够允许用户自由定制内容了。
HTTP 流
------

当客户端想要和服务端——不管是最终的服务端还是中间的代理——进行信息交互时，过程表现为下面几步：

1. 打开一个 TCP 连接：TCP 连接被用来发送一条或多条请求，以及接受响应消息。客户端可能打开一条新的连接，或重用一个已经存在的连接，或者也可能开几个新的 TCP 连接连向服务端。
2. 发送一个 HTTP 报文：HTTP 报文（在 HTTP/2 之前）是语义可读的。在 HTTP/2 中，这些简单的消息被封装在了帧中，这使得报文不能被直接读取，但是原理仍是相同的。

```http
GET / HTTP/1.1
Host: developer.mozilla.org
Accept-Language: zh
```

3. 读取服务端返回的报文信息：

```http
HTTP/1.1 200 OK
Date: Sat, 09 Oct 2010 14:28:02 GMT
Server: Apache
Last-Modified: Tue, 01 Dec 2009 20:18:22 GMT
ETag: "51142bc1-7449-479b075b2891b"
Accept-Ranges: bytes
Content-Length: 29769
Content-Type: text/html

<!DOCTYPE html>…（此处是所请求网页的 29769 字节）
```

4. 关闭连接或者为后续请求重用连接。

当启用 HTTP 流水线时，后续请求都可以直接发送，而不用等待第一个响应被全部接收。然而 HTTP 流水线已被证明很难在现有的网络中实现，因为现有网络中有老旧的软件与现代版本的软件同时存在。因此，HTTP 流水线已在 HTTP/2 中被更健壮、使用帧的多路复用请求所取代。

HTTP 报文
-------

HTTP/1.1 以及更早的 HTTP 协议报文都是语义可读的。在 HTTP/2 中，这些报文被嵌入到了一个新的二进制结构，帧。帧允许实现很多优化，比如报文标头的压缩以及多路复用。即使只有原始 HTTP 报文的一部分以 HTTP/2 发送出来，每条报文的语义依旧不变，客户端会重组原始 HTTP/1.1 请求。因此用 HTTP/1.1 格式来理解 HTTP/2 报文仍旧有效。

有两种 HTTP 报文的类型，请求与响应，每种都有其特定的格式。

### 请求

HTTP 请求的一个例子：

![一个基础的 HTTP 请求](/zh-CN/docs/Web/HTTP/Overview/http_request.png)

请求由以下元素组成：

* 一个 HTTP 方法，通常是由一个动词，像 `GET`、`POST` 等，或者一个名词，像 `OPTIONS`、`HEAD` 等，来定义客户端执行的动作。典型场景有：客户端意图抓取某个资源（使用`GET`）；发送 HTML 表单的参数值（使用`POST`）；以及其他情况下需要的那些其他操作。
* 要获取的那个资源的路径——去除了当前上下文中显而易见的信息之后的 URL，比如说，它不包括协议（`http://`）、域名（这里的域名是`developer.mozilla.org`），或是 TCP 的端口（这里的端口是 `80`）。
* HTTP 协议版本号。
* 为服务端表达其他信息的可选标头。
* 请求体（body），类似于响应中的请求体，一些像 POST 这样的方法，请求体内包含需要了发送的资源。
### 响应

HTTP 响应的一个例子：

![HTTP 响应的图片](/zh-CN/docs/Web/HTTP/Overview/http_response.png)

响应报文包含了下面的元素：

* HTTP 协议版本号。
* 一个状态码（status code），来指明对应请求已成功执行或是没有，以及相应的原因。
* 一个状态信息，这个信息是一个不权威、简短的状态码描述
* HTTP 标头，与请求标头类似。
* 可选项，一个包含了被获取资源的主体。
基于 HTTP 的 API
-------------

`XMLHttpRequest` 是基于 HTTP 的最常用 API，可用于在用户代理和服务端之间交换数据。现代 `Fetch API` 提供相同的功能，并具有更强大和灵活的功能集。

另一种 API，server-sent 事件，是一种单向服务，允许服务端借助作为 HTTP 传输机制向客户端发送事件。使用 `EventSource` 接口，客户端可打开连接并创建事件处理器。客户端浏览器自动将 HTTP 流里到达的消息转换为适当的 `Event` 对象。继而将已知类型的事件，传递给先前注册过的事件处理器，其他未指明类型的事件则传递给 `onmessage` 事件处理器。

总结
--

HTTP 是一种简单、易用、具有可扩展性的协议，其客户端—服务器模式的结构，加上能够增加标头的能力，使得 HTTP 随 Web 中不断扩展的能力一起发展。

虽然增加了一些复杂度——为了提高性能，HTTP/2 将 HTTP 报文嵌入到帧中——但是报文的基本结构自 HTTP/1.0 起仍保持不变。会话流依旧简单，通过一个简单的 HTTP 消息监视器就可以查看和纠错。


HTTP/1.x 的连接管理 - HTTP

HTTP/1.x 的连接管理
==============

连接管理是一个 HTTP 的关键话题：打开和保持连接在很大程度上影响着网站和 Web 应用程序的性能。在 HTTP/1.x 里有多种模型：*短连接*、*长连接*和 *HTTP 流水线*。

HTTP 的传输协议主要依赖于 TCP 来提供从客户端到服务器端之间的连接。在早期，HTTP 使用一个简单的模型来处理这样的连接。这些连接的生命周期是短暂的：每发起一个请求时都会创建一个新的连接，并在收到应答时立即关闭。

这个简单的模型对性能有先天的限制：打开每一个 TCP 连接都是相当耗费资源的操作。客户端和服务器端之间需要交换好些个消息。当请求发起时，网络延迟和带宽都会对性能造成影响。现代浏览器往往要发起很多次请求（十几个或者更多）才能拿到所需的完整信息，证明了这个早期模型的效率低下。

有两个新的模型在 HTTP/1.1 诞生了。首先是长连接模型，它会保持连接去完成多次连续的请求，减少了不断重新打开连接的时间。然后是 HTTP 流水线模型，它还要更先进一些，多个连续的请求甚至都不用等待立即返回就可以被发送，这样就减少了耗费在网络延迟上的时间。

![Compares the performance of the three HTTP/1.x connection models: short-lived connections, persistent connections, and HTTP pipelining.](/zh-CN/docs/Web/HTTP/Connection_management_in_HTTP_1.x/http1_x_connections.png)

**备注：** HTTP/2 新增了其他连接管理模型。

要注意的一个重点是 HTTP 的连接管理适用于两个连续节点之间的连接，它是逐跳（Hop-by-hop）标头，而不是端到端（End-to-end）标头。当模型用于从客户端到第一个代理服务器的连接和从代理服务器到目标服务器之间的连接时（或者任意中间代理）效果可能是不一样的。HTTP 协议头受不同连接模型的影响，比如 `Connection` 和 `Keep-Alive`，就是逐跳标头标头，它们的值是可以被中间节点修改的。

一个相关的话题是 HTTP 连接升级的概念，其中 HTTP/1.1 连接升级为一个不同的协议，比如 TLS/1.0、Websocket、甚至明文形式的 HTTP/2。更多细节参阅协议升级机制。

短连接
---

HTTP 最早期的模型和 HTTP/1.0 的默认模型，是短连接。每一个 HTTP 请求都由它自己独立的连接完成；这意味着发起每一个 HTTP 请求之前都会有一次 TCP 握手，而且是连续不断的。

TCP 协议握手本身就是耗费时间的，所以 TCP 可以保持更多的热连接来适应负载。短连接破坏了 TCP 具备的能力，并且新的冷连接降低了其性能。

这是 HTTP/1.0 的默认模型（如果没有指定 `Connection` 协议头，或者是值被设置为 `close`）。而在 HTTP/1.1 中，只有当 `Connection` 被设置为 `close` 时才会用到这个模型。

**备注：** 除非是要兼容一个非常古老的，不支持长连接的系统，没有一个令人信服的理由继续使用这个模型。

长连接
---

短连接有两个比较大的问题：创建新连接耗费的时间尤为明显，另外 TCP 连接的性能只有在该连接被使用一段时间后（热连接）才能得到改善。为了缓解这些问题，*长连接*的概念便被设计出来了，甚至在 HTTP/1.1 之前。或者，这被称之为一个 *keep-alive* 连接。

一个长连接会保持一段时间，重复用于发送一系列请求，节省了新建 TCP 连接握手的时间，还可以利用 TCP 的性能增强能力。当然这个连接也不会一直保留着：连接在空闲一段时间后会被关闭（服务器可以使用 `Keep-Alive` 协议头来指定一个最小的连接保持时间）。

长连接也还是有缺点的；就算是在空闲状态，它还是会消耗服务器资源，而且在重负载时，还有可能遭受 DoS 攻击。这种场景下，可以使用非长连接，即尽快关闭那些空闲的连接，也能对性能有所提升。

HTTP/1.0 里默认并不使用长连接。把 `Connection` 设置成 `close` 以外的其他参数都可以让其保持长连接，通常会设置为 `retry-after`。

在 HTTP/1.1 里，默认就是长连接的，不再需要标头（但我们还是会把它加上，万一某个时候因为某种原因要退回到 HTTP/1.0 呢）。

HTTP 流水线
--------

**备注：** HTTP 流水线在现代浏览器中并不是默认被启用的：

* 有缺陷的代理服务器仍然很常见，这些会导致 Web 开发人员无法预见和轻松诊断的奇怪和不稳定行为。
* 正确的实现流水线是复杂的：传输中的资源大小、多少有效的 RTT 会被用到以及有效带宽都会直接影响到流水线提供的改善。不知道这些的话，重要的消息可能被延迟到不重要的消息后面。这个重要性的概念甚至会演变为影响到页面布局！因此 HTTP 流水线在大多数情况下带来的改善并不明显。
* 流水线受制于队头阻塞（HOL）问题。

由于这些原因，流水线已被 HTTP/2 中更好的算法——*多路复用*（multiplexing）所取代。

默认情况下，HTTP 请求是按顺序发出的。下一个请求只有在当前请求收到响应过后才会被发出。由于会受到网络延迟和带宽的限制，在下一个请求被发送到服务器之前，可能需要等待很长时间。

流水线是在同一条长连接上发出连续的请求，而不用等待应答返回。这样可以避免连接延迟。理论上讲，性能还会因为两个 HTTP 请求有可能被打包到一个 TCP 消息包中而得到提升。就算 HTTP 请求不断的继续，尺寸会增加，但设置 TCP 的最大分段大小（MSS）选项，仍然足够包含一系列简单的请求。

并不是所有类型的 HTTP 请求都能用到流水线：只有幂等方式，比如 `GET`、`HEAD`、`PUT` 和 `DELETE` 能够被安全地重试。如果有故障发生时，流水线的内容要能被轻易的重试。

今天，所有遵循 HTTP/1.1 标准的代理和服务器都应该支持流水线，虽然实际情况中还是有很多限制：一个很重要的原因是，目前没有现代浏览器默认启用这个特性。

域名分片
----

**备注：** 除非你有紧急而迫切的需求，不要使用这一过时的技术；而是升级到 HTTP/2。在 HTTP/2 里，做域名分片就没必要了：HTTP/2 的连接可以很好的处理并发的无优先级的请求。域名分片甚至会影响性能。大多数 HTTP/2 的实现还会使用一种称作连接聚合的技术去尝试合并被分片的域名。

作为 HTTP/1.x 的连接，请求是序列化的，哪怕本来是无序的，在没有足够庞大可用的带宽时，也无从优化。一个解决方案是，浏览器为每个域名建立多个连接，以实现并发请求。曾经默认的连接数量为 2 到 3 个，现在比较常用的并发连接数已经增加到 6 条。如果尝试大于这个数字，就有触发服务器 DoS 保护的风险。

如果服务器端想要更快速的响应网站或应用程序的应答，它可以迫使客户端建立更多的连接。例如，不要在同一个域名下获取所有资源，假设有个域名是 `www.example.com`，我们可以把它拆分成好几个域名：`www1.example.com`、`www2.example.com`、`www3.example.com`。所有这些域名都指向同一台服务器，浏览器会同时为每个域名建立 6 条连接（在我们这个例子中，连接数会达到 18 条）。这一技术被称作域名分片。

![Without domain sharding, a client requests six images from a domain with a maximum of two requests taking place in parallel. With domain sharding, the images are available from two domains and the client can run four requests in parallel, downloading the images in less time.](/zh-CN/docs/Web/HTTP/Connection_management_in_HTTP_1.x/httpsharding.png)

结论
--

改进后的连接管理极大的提升了 HTTP 的性能。不管是 HTTP/1.1 还是 HTTP/1.0，使用长连接——直到进入空闲状态——都能达到最佳的性能。然而，解决流水线故障需要设计更先进的连接管理模型，这些模型已经被纳入 HTTP/2 了。


HTTP 身份验证 - HTTP

HTTP 身份验证
=========

HTTP 提供一个用于权限控制和认证的通用框架。本页介绍了通用的 HTTP 认证框架，并且展示了如何通过 HTTP 的“Basic”模式限制对你服务器的访问。

通用的 HTTP 认证框架
-------------

RFC 7235 定义了一个 HTTP 身份验证框架，服务器可以用来质询（challenge）客户端的请求，客户端则可以提供身份验证凭据。

质询与响应的工作流程如下：

1. 服务器端向客户端返回 `401`（Unauthorized，未被授权的）响应状态码，并在 `WWW-Authenticate` 响应标头提供如何进行验证的信息，其中至少包含有一种质询方式。
2. 之后，想要使用服务器对自己身份进行验证的客户端，可以通过包含凭据的 `Authorization` 请求标头进行验证。
3. 通常，客户端会向用户显示密码提示，然后发送包含正确的 `Authorization` 标头的请求。

![](/zh-CN/docs/Web/HTTP/Authentication/http-auth-sequence-diagram.png)

上述整体的信息流程，对于大多数（并非是全部）身份验证方案都是相同的。标头中的真实信息和编码的方式确实发生了变化。

**警告：** 上图使用的“Basic”身份验证方案会对凭据进行编码，但是并不会进行加密。除非信息交换通过安全的连接（HTTPS/TLS），否则这件事极其不安全的。

### 代理认证

与上述同样的询问质疑和响应原理适用于代理认证。由于资源认证和代理认证可以并存，区别于独立的标头和响应状态码。对于代理，询问质疑的状态码是 `407`（必须提供代理证书），响应标头 `Proxy-Authenticate` 至少包含一个可用的质询，并且请求标头 `Proxy-Authorization` 用作向代理服务器提供凭据。

### 禁止访问

如果（代理）服务器收到*无效*的凭据，它应该响应 `401` `Unauthorized` 或 `407` `Proxy Authentication Required`，用户可以发送新的请求或替换 `Authorization` 标头字段。

如果（代理）服务器接受的有效凭据**不足以**访问给定的资源，服务器将响应 `403` `Forbidden` 状态码。与 `401` `Unauthorized` 或 `407` `Proxy Authentication Required` 不同的是，该用户无法进行身份验证并且浏览器不会提出新的的尝试。

在所有情况下，服务器更可能返回 `404` `Not Found` 状态码，以向没有足够权限或者未正确身份验证的用户隐藏页面的存在。

### 跨源图片认证

一个被浏览器最近修复了的潜在的安全漏洞是跨站点图片的认证。从 Firefox 59 起，浏览器在从不同源的加载图片资源到当前的文档时，将不会再触发 HTTP 认证对话框（Firefox bug 1423146），如果攻击者可以将任意图片嵌入到第三方页面中，禁止触发 HTTP 认证对话框可避免用户的身份凭据被窃取。

### HTTP 认证的字符编码

浏览器使用 `utf-8` 编码用户名和密码。

Firefox 曾使用 `ISO-8859-1`，但为与其他浏览器保持一致改为 `utf-8`，也为了避免 Firefox bug 1419658 中所描述的潜在问题。

### WWW-Authenticate 与 Proxy-Authenticate 标头

`WWW-Authenticate` 与 `Proxy-Authenticate` 响应标头指定了为获取资源访问权限而进行身份验证的方法。它们需要明确要进行验证的方案，这样希望进行授权的客户端就知道该如何提供凭据。

这两个标头的语法形式如下：

```http
WWW-Authenticate: <type> realm=<realm>
Proxy-Authenticate: <type> realm=<realm>
```

在这里，`<type>` 指的是验证的方案（“Basic”是最常见的验证方案，会在下面进行介绍）。realm 用来描述进行保护的区域，或者指代保护的范围。它可以是类似于“Access to the staging site”的消息，这样用户就可以知道他们正在试图访问哪一空间。

### Authorization 与 Proxy-Authorization 标头

`Authorization` 与 `Proxy-Authorization` 请求标头包含有用来向（代理）服务器证明用户代理身份的凭据。这里同样需要指明验证的 `<type>`，其后跟有凭据信息，该凭据信息可以被编码或者加密，取决于采用的是哪种验证方案。

```http
Authorization: <type> <credentials>
Proxy-Authorization: <type> <credentials>
```

### 身份验证方案

通用 HTTP 身份验证框架可以被多个验证方案使用。不同的验证方案会在安全强度以及在客户端或服务器端软件中可获得的难易程度上有所不同。

IANA 维护了一系列的验证方案，除此之外还有其他类型的验证方案由虚拟主机服务提供，例如 Amazon AWS。

常见的验证方案包括：

**Basic**

参见 RFC 7617，base64 编码凭据。详情请参阅下文。

**Bearer**

参见 RFC 6750，bearer 令牌通过 OAuth 2.0 保护资源。

**Digest**

参见 RFC 7616，Firefox 93 及更高版本支持 SHA-256 算法。以前的版本仅支持 MD5 散列（不建议）。

**HOBA**

参见 RFC 7486，阶段三，**H**TTP **O**rigin-**B**ound 认证，基于数字签名。

**Mutual**

参见 RFC 8120

**Negotiate** / **NTLM**

参见 RFC4599

**VAPID**

参见 RFC 8292

**SCRAM**

参见 RFC 7804

**AWS4-HMAC-SHA256**

参见 AWS 文档。此方案用于 AWS3 服务器验证。

方案在安全强度以及在客户端或服务器软件中的可用性方面可能有所不同。

“Basic”身份验证方案安全性很差，但得到了广泛的支持且易于设置。下文将更详细地介绍它。

Basic 验证方案
----------

“Basic”HTTP 验证方案是在 RFC 7617 中规定的，在该方案中，使用用户的 ID/密码作为凭据信息，并且使用 base64 算法进行编码。

### Basic 验证方案的安全性

由于用户 ID 与密码是是以明文的形式在网络中进行传输的（尽管采用了 base64 编码，但是 base64 算法是可逆的），所以基本验证方案并**不安全**。basic 验证方案应与 HTTPS/TLS 协议搭配使用。假如没有这些安全方面的增强，那么 basic 验证方案不应该被来用保护敏感或者极具价值的信息。

### 使用 Apache 限制访问和 basic 身份验证

要对 Apache 服务器上的目录进行密码保护，你需要一个 `.htaccess` 和 a `.htpasswd` 文件。

该 `.htaccess` 文件格式通常看起来像这样：
```

AuthType Basic
AuthName "Access to the staging site"
AuthUserFile /path/to/.htpasswd
Require valid-user
```

该 `.htaccess` 文件引用一个 `.htpasswd` 文件，其中每行用冒号（`:`）分隔的用户名和密码。你不能看到真实的密码因为它们是散列（在这个例子中是使用了 MD5）。你可以命名 `.htpasswd` 文件为你所喜欢的名字，但是应该保证这个文件不被其他人访问。(Apache 通常配置阻止访问 `.ht*` 类的文件).
```

aladdin:$apr1$ZjTqBB3f$IF9gdYAGlMrs2fuINjHsz.
user2:$apr1$O04r.y2H$/vEkesPhVInBByJUkXitA/
```

### nginx 访问限制和 basic 认证

在 nginx 配置中，你需要指定一个要保护的 location 并且 `auth_basic` 指令提供密码保护区域的名称。

`auth_basic_user_file` 指令指定包含加密的用户凭据 `.htpasswd` 文件，就像上面的 apache 例子。
```

location /status {
    auth_basic           "Access to the staging site";
    auth_basic_user_file /etc/apache2/.htpasswd;
}
```

### 使用 URL 中的身份凭据进行的访问

许多客户端同时支持避免弹出登录框，而是使用包含用户名和密码的经过编码的 URL，如下所示：
```

https://username:password@www.example.com/
```

**这种 URL 已被弃用**。在 Chrome 中，URL 中的 `username:password@` 部分甚至会因为安全原因而被移除。Firefox 则会检查该站点是否真的需要身份验证，假如不是，则会弹出一个警告窗口：你即将使用用户名 `username` 登录 `www.example.com` 站点，但是该站点不需要进行身份验证。这可能是在试图进行欺诈。

参见
--

* `WWW-Authenticate`
* `Authorization`
* `Proxy-Authorization`
* `Proxy-Authenticate`
* `401`、`403`、`407`


HTTP 资源与规范 - HTTP

HTTP 资源与规范
==========

HTTP 协议于 20 世纪 90 年代初期被规范化。得益于其扩展性，该规范至今又添加了大量的补充内容。这些增补规范文档散落在网络的不同位置。本文列出了与 HTTP 协议相关的规范文档。

| 规范 | 标题 | 状态 |
| --- | --- | --- |
| RFC 7230 | 超文本传输协议（HTTP/1.1）：报文格式与路由 | 建议标准 |
| RFC 7231 | 超文本传输协议（HTTP/1.1）：语义与内容 | 建议标准 |
| RFC 7232 | 超文本传输协议（HTTP/1.1）：条件请求 | 建议标准 |
| RFC 7233 | 超文本传输协议（HTTP/1.1）：范围请求 | 建议标准 |
| RFC 7234 | 超文本传输协议（HTTP/1.1）：缓存 | 建议标准 |
| RFC 5861 | 针对陈旧内容的 HTTP 缓存控制扩展 | 信息参考 |
| RFC 8246 | 不可变的 HTTP 响应 | 建议标准 |
| RFC 7235 | 超文本传输协议（HTTP/1.1）：认证 | 建议标准 |
| RFC 6265 | HTTP 状态控制机制：定义 Cookies | 建议标准 |
| Draft spec | Cookie 前缀 | IETF 草案 |
| Draft spec | 同源 Cookies | IETF 草案 |
| Draft spec | 反对非安全的源站对安全 cookies 进行修改 | IETF 草案 |
| RFC 2145 | HTTP 版本号的使用与解释 | 信息参考 |
| RFC 6585 | 扩展的 HTTP 状态码 | 建议标准 |
| RFC 7538 | 超文本传输协议的 308 状态码（永久重定向） | 建议标准 |
| RFC 7725 | 用于报告法律限制的 HTTP 状态码 | 即将成为标准 |
| RFC 2397 | URL 的“data“方案 | 建议标准 |
| RFC 3986 | 统一资源定位符（URI）：通用语法 | 因特网标准 |
| RFC 5988 | 网络链接 *定义了 `Link` 首部字段* | 建议标准 |
| Experimental spec | 超文本传输协议（HTTP)）的 Keep-Alive 首部字段 | 信息参考（已过期） |
| Draft spec | HTTP 客户端提示 | IETF 草案 |
| RFC 7578 | 从表单中返回数据：multipart/form-data | 建议标准 |
| RFC 6266 | 在超文本传输协议（HTTP）中使用 Content-Disposition 首部字段 | 建议标准 |
| RFC 2183 | Communicating Presentation Information in Internet Messages: The Content-Disposition Header Field *Only a subset of syntax of the `Content-Disposition` header can be used in the context of HTTP messages.* | 建议标准 |
| RFC 7239 | 前向 HTTP 扩展 | 建议标准 |
| RFC 6455 | WebSocket 协议 | 建议标准 |
| RFC 5246 | 安全传输层（TLS）协议 1.2 版本 *该规范已经被后续 RFC 文档进行了修改，不过这些修改不会对 HTTP 协议本身产生效力。* | 建议标准 |
| Draft spec | 安全传输层（TLS）协议 1.3 版本 *该版本就绪之后，将取代之前的 1.2 版本* | IETF 草案 |
| RFC 2817 | 在 HTTP/1.1 中进行 TLS 升级 | 建议标准 |
| RFC 7540 | 超文本传输协议版本 2（HTTP/2） | 建议标准 |
| RFC 7541 | HPACK：对 HTTP/2 的首部进行压缩 | 即将成为标准 |
| RFC 7838 | HTTP 替代服务 | 即将成为标准 |
| RFC 7301 | 对安全传输层（TLS）的应用层协议的协商的扩展 *用于在传输 HTTP/2 报文时进行协商，以减少一对额外的请求／响应* | 建议标准 |
| RFC 6454 | 网络源站的概念 | 建议标准 |
| Fetch | 跨源资源共享 | 实时标准 |
| RFC 7034 | HTTP 首部字段 X-Frame-Options | 信息参考 |
| RFC 6797 | HTTP 严格安全传输（HSTS） | 建议标准 |
| Upgrade Insecure Requests | 对不安全的请求进行升级 | 推荐候选 |
| Content Security Policy 1.0 | 内容安全策略 1.0 版本。*CSP 1.1 与 CSP 3.0 并未扩展 HTTP 协议标准* | 已废弃 |
| Microsoft document | *指定传统文档模式定义 X-UA-Compatible 规范* | 注记 |
| RFC 5689 | HTTP 拓展之 Web 分布式创作与版本控制 (WebDAV) *这些 Web 上的拓展，与 CardDAV 和 CalDAV 一起超出了 Web 上的 HTTP 的范围。当下应用的现代 API 使用了 RESTful 的接口。* | 建议标准 |
| RFC 2324 | 超文本咖啡壶控制协议（HTCPCP/1.0） | 愚人节玩笑 |
| RFC 7168 | 超文本咖啡壶控制协议的茶壶扩展（HTCPCP-TEA） | 愚人节玩笑 |
| HTML Living Standard | HTML。*针对服务端发送的事件定义了 HTTP 扩展* | 实时标准 |
| Tracking Preference Expression | DNT 首部字段 | 编辑草案／推荐候选 |
| Reporting API | `Report-To` 首部字段 | 草案 |
| Draft spec | Expect-CT 拓展于 HTTP | IETF 草案 |


HTTP 条件请求 - HTTP

HTTP 条件请求
=========

在 HTTP 协议中有一个“**条件式请求**”的概念，在这类请求中，请求的结果，甚至请求成功的状态，都会随着验证器与受影响资源的比较结果的变化而变化。这类请求可以用来验证缓存的有效性，省去不必要的控制手段，以及验证文件的完整性，例如在断点续传的场景下或者在上传或者修改服务器端的文件的时候避免更新丢失问题。

基本原理
----

在 HTTP 协议中，条件请求指的是请求的执行结果会因特定首部的值不同而不同。这些首部规定了请求的前置条件，请求结果则视条件匹配与否而有所不同。

请求引发的不同的反应取决于请求所使用的方法，以及组成前置条件首部集合：

* 对于安全（safe）方法来说，例如 `GET`，通常用来获取文件，条件请求可以被用来限定仅在满足条件的情况下返回文件。这样可以节省带宽。
* 对于非安全（unsafe）方法来说，例如 `PUT` 方法，通常用来上传文件，条件请求可以被用来限定仅在满足文件的初始版本与服务器上的版本相同的条件下才会将其上传。
验证器
---

所有的条件请求首部都是试图去检测服务器上存储的资源是否与某一特定版本相匹配。为了达到这个目的，条件请求需要指明资源的版本。由于逐个字节去比较完整资源是不切实际的，况且这也并非总是想要的结果，所以在请求中会传递一个描述资源版本的值。这些值称为“验证器”，并且分为两大类：

* 文件的最后修改时间，即 *last-modified*（最后修改）时间。
* 一个意义模糊的字符串，指代一个独一无二的版本，称为“实体标签”，或者 *etag。*

比较同一份资源的不同版本有一定的技巧性：取决于上下文环境的不同，有两种不同的等值检查（*equality checks*）类型：

* 强验证类型（*Strong validation*）应用于需要逐个字节相对应的情况，例如需要进行断点续传的时候。
* 弱验证类型（*Weak validation*）应用于用户代理只需要确认资源内容相同即可。即便是有细微差别也可以接受，比如显示的广告不同，或者是页脚的时间不同。

验证类型与验证器的类型是相互独立的。 `Last-Modified` 和 `ETag` 首部均可应用于两种验证类型，尽管在服务器端实现的复杂程度可能会有所不同。HTTP 协议默认使用强验证类型，可以指定何时使用弱验证类型。

### 强验证类型

强验证类型的作用在于确保要比较的资源与其相比较的对象之间每一个字节都相同。对于有些首部来说需要明确指定该验证类型，而对于另外一些来说则是默认值就是强验证类型。强验证类型的要求相当严格，在服务器层面来说可能较难保证。但是它确保了数据在任何时候都没有缺损，有时候则需要以牺牲性能为代价。

使用 `Last-Modified` 首部很难为强验证类型提供一个唯一标识。通常这是由 `ETag` 首部来完成的，该首部可以提供使用 MD5 算法获取的资源（或其衍生品）的散列值。

### 弱验证类型

弱验证类型与强验证类型不同，因为它会把内容相同的两份文件看做是一样的。例如，使用弱验证类型，一个页面与另外一个页面只是在页脚显示的时间上有所不同，或者是展示的广告不相同，那么就会被认为是**相同的**。但是在使用强验证的情况下，二者是**不同的**。构建应用于弱验证类型的标签（etag）体系可能会比较复杂，因为这会涉及到对页面上不同的元素的重要性进行排序，但是会对缓存性能优化相当有帮助。

条件首部
----

一些被称为条件首部的 HTTP 首部，可以引发条件请求。它们是：

`If-Match`

如果远端资源的实体标签与在 `ETag` 这个首部中列出的值相同的话，表示条件匹配成功。默认地，除非实体标签带有 'W/' 前缀，否者它将会执行强验证。

`If-None-Match`

如果远端资源的实体标签与在 `ETag` 这个首部中列出的值都不相同的话，表示条件匹配成功。默认地，除非实体标签带有 'W/' 前缀，否者它将会执行强验证。

`If-Modified-Since`

如果远端资源的 `Last-Modified` 首部标识的日期比在该首部中列出的值要更晚，表示条件匹配成功。

`If-Unmodified-Since`

如果远端资源的 HTTPHeader("Last-Modified")}} 首部标识的日期比在该首部中列出的值要更早或相同，表示条件匹配成功。

`If-Range`

与 `If-Match` 或 `If-Unmodified-Since` 相似，但是只能含有一个实体标签或者日期值。如果匹配失败，则条件请求宣告失败，此时将不会返回 `206` `Partial Content` 响应码，而是返回 `200` `OK` 响应码，以及完整的资源。

应用场景
----

### 缓存更新

条件式请求最常见的应用场景是更新缓存。假如缓存为空，或者是没有缓存的话，被请求资源会以状态码 `200` `OK` 返回。

![The request issued when the cache is empty triggers the resource to be downloaded, with both validator value sent as headers. The cache is then filled.](/zh-CN/docs/Web/HTTP/Conditional_requests/cache1.png)

验证器会同资源一起返回，它们出现在首部字段中。在这个例子中， `Last-Modified` 与 `ETag` 都被返回，不过如果只返回其中的一个也是可以的。这些验证器会同资源一起被缓存起来（与所有的首部一样），并在在缓存失效的时候用来发起条件式请求。

只要缓存未失效，就不会发起任何请求。但是一旦失效——主要是由 `Cache-Control` 首部控制——客户端就不会采用缓存值而是发起条件式请求。验证器的值会用作 `If-Modified-Since` 和 `If-Match` 首部字段的参数。

假如资源未发生变化，服务器就返回状态码为 `304` `Not Modified` 的响应。这样相当于对缓存资源进行了刷新，而客户端则采用被缓存的资源。尽管这里有一次请求/响应往返会消耗一定的资源，但是这样做比将整个资源通过网络再传输一遍更高效。

![With a stale cache, the conditional request is sent. The server can determine if the resource changed, and, as in this case, decide not to send it again as it is the same.](/zh-CN/docs/Web/HTTP/Conditional_requests/httpcache2.png)

假如资源发生了变化，服务器就直接返回 `200` `OK` 响应码，连同新版本的资源，就像是没有应用条件式请求一样；客户端则采用新版本资源（并将其缓存起来）。

![In the case where the resource was changed, it is sent back as if the request wasn't conditional.](/zh-CN/docs/Web/HTTP/Conditional_requests/httpcache3.png)

除了需要在服务器端对验证器进行设置以外，该机制是透明的：所有的浏览器都会对缓存资源进行管理，在不需要 Web 开发者进行任何特殊处理的情况下发送条件式请求。

### 增量下载的完整性

文件的增量下载是 HTTP 协议规定的一项功能，它允许恢复先前的操作，通过保存先前已经获得的信息来节省带宽和时间：

![A download has been stopped and only partial content has been retrieved.](/zh-CN/docs/Web/HTTP/Conditional_requests/httpresume1.png)

支持增量下载的服务器会通过 `Accept-Ranges` 首部来广播这项能力。此后客户端就可以通过发送 `Ranges` (en-US) 首部字段以及缺失的范围值来进行断点续传了：

![The client resumes the requests by indicating the range he needs and preconditions checking the validators of the partially obtained request.](/zh-CN/docs/Web/HTTP/Conditional_requests/httpresume2.png)

基本原理很简单，但是这里有一个潜在的问题：如果要下载的资源在两次下载之间进行了修改，得到的数据范围就会对应该资源的两个不同的版本，那么最终获得的文件是损坏的。

为了防止这种情况的发生，需要使用条件式请求。对于范围请求来说，有两种方法可以实现这个目的。更灵活一些的方法是使用 `If-Modified-Since` 和 `If-Match` 首部，假如前置条件失败，服务器端会返回错误提示，然后客户端可以从头开始重新下载资源：

![When the partially downloaded resource has been modified, the preconditions will fail and the resource will have to be downloaded again completely.](/zh-CN/docs/Web/HTTP/Conditional_requests/httpresume3.png)

尽管这种方法行得通，但是它在文件发生变化的情况下增加了一次额外的请求/响应往返。这一点会影响性能。为此 HTTP 协议规定了一个特定的首部——`If-Range`——来避免这种情况的发生：

![The If-Range headers allows the server to directly send back the complete resource if it has been modified, no need to send a 412 error and wait for the client to re-initiate the download.](/zh-CN/docs/Web/HTTP/Conditional_requests/httpresume4.png)

该方法更高效，但是缺乏一定的灵活性，因为条件值只能是实体标签。不过这种额外的灵活性很少会需要。

### 使用乐观锁避免更新丢失问题

对于 Web 应用的一项常见操作是远程更新文件。这在各种类型的文件系统以及版本控制软件中都很常见，但是任何允许远程存储资源的软件也都需要这样一个乐观锁机制。常见的 Web 站点，例如 wiki 系统或其他类型的内容管理系统（CMS），都存在这样的需求。

使用 `PUT` 方法可以实现上述机制。客户端首先读取原始文件，然后进行修改，最后将它们推送到服务器上：

![Updating a file with a PUT is very simple when concurrency is not involved.](/zh-CN/docs/Web/HTTP/Conditional_requests/httplock1.png)

不幸的是，当把并发情况考虑在内的时候，事情变得有些不那么确定。当一个客户端在本地修改它新获得的资源副本的时候，另外一个客户端同样可以获取一份资源副本并进行同样的操作。接下来要发生的事情就不那么顺利了：当二者提交回服务器的时候，前一个客户端作出的修改会被第二个客户端的推送所覆盖，因为第二个客户端对于第一个客户端做出的修改一无所知。最终的结果则取决于获胜的一方，但是该结果不会通知给另一方。哪一个客户端作出的修改将会被保存下来，会由于它们提交的速度而有所不同；提交的速度则依赖于客户端及服务器端的性能，甚至是使用客户端进行编辑的人的表现。每一次的胜出者都会有所不同。这种情况被称作竞态条件（race condition ），会导致难以捉摸的情况的发生，并且难以探测和除错：

![When several clients update the same resource in parallel, we are facing a race condition: the slowest win, and the others don't even know they lost. Problematic!](/zh-CN/docs/Web/HTTP/Conditional_requests/httplock2.png)

不存在解决这一问题而不打扰双方某一方的办法。然而，更新丢失问题以及竞态条件是需要避免的。我们希望获得可预测的结果，并且希望在更新操作被拒绝的时候客户端可以得到反馈。

条件式请求可以被用在**乐观锁算法**（大多数 wiki 系统和版本管理系统采用的是该算法）的实现上。其思路是，允许所有的客户端获得资源的副本，然后在本地进行编辑，通过只允许第一个客户端成功提交的方式来控制并发操作。其余的基于现今已过期版本的更新操作都会被拒绝：

![Conditional requests allow to implement optimistic locking: now the quickest wins, and the others get an error.](/zh-CN/docs/Web/HTTP/Conditional_requests/httplock3.png)

这种方式的实现需要用到 `If-Match` 或 `If-Unmodified-Since` 首部。假如实体标签与源头文件的实体标签不一致，或者源头文件在被获取副本之后经过了修改，那么此次变更请求就会被拒绝，收到 `412` `Precondition Failed` 的错误提示。之后就需要依靠客户端来处理该错误了：或者通知用户重新开始（基于最新的版本），或者是给用户展示两个版本之间的**差异**，辅助他们决定要保留哪些变更。

### 处理资源的首次上传问题

资源的首次上传问题是前面所描述的情况的一个极端情况。与任何资源更新问题一样，当两个客户端在大致相同的时间进行上传操作的时候，就会遇到竞态条件。为了防止这种情况的发生，可以使用条件式请求：添加 `If-None-Match` 首部，并将其值设置为`'*'`, 表示任意实体标签。当且仅当资源先前并不存在的情况下请求的操作才会成功执行：

![Like for a regular upload, the first upload of a resource is subject to a race condition: If-None-Match can prevent it.](/zh-CN/docs/Web/HTTP/Conditional_requests/httpfirst.png)

`If-None-Match` 首部只可应用于兼容 HTTP/1.1（及后续版本）的服务器。假如不确定所访问的服务器是否兼容，需要首先向要访问的资源发送一次 `HEAD` 请求来进行确认。

结语
--

条件式请求是 HTTP 协议中一项非常重要的特性，它使高效复杂的应用系统的构建得以实现。对于缓存或断点续传功能来说，站点管理员只需要正确配置服务器就可以了；在某些环境中设置正确的实体标签可能需要一些技巧。但一旦设置成功，浏览器就能够按照预期地发送条件式请求。

而对于锁机制，则恰恰相反：Web 开发者需要使用合适的消息首部生成请求，另一方面，站点管理员在大多时候可以依赖应用来检查这些首部信息。

显然在两种情况下，条件式请求都发挥着基础性作用，成为 Web 应用的有力支撑。


HTTP 协议中的数据压缩 - HTTP

HTTP 协议中的数据压缩
=============

**数据压缩**是提高 Web 站点性能的一种重要手段。对于有些文件来说，高达 70% 的压缩比率可以大大减低对于带宽的需求。随着时间的推移，压缩算法的效率也越来越高，同时也有新的压缩算法被发明出来，应用在客户端与服务器端。

在实际应用时，web 开发者不需要亲手实现压缩机制，浏览器及服务器都已经将其实现了，不过他们需要确保在服务器端进行了合理的配置。数据压缩会在三个不同的层面发挥作用：

* 首先某些格式的文件会采用特定的优化算法进行压缩，
* 其次在 HTTP 协议层面会进行通用数据加密，即数据资源会以压缩的形式进行端到端传输，
* 最后数据压缩还会发生在网络连接层面，即发生在 HTTP 连接的两个节点之间。
文件格式压缩
------

每一种文件类型都会存有冗余，也就是*浪费的空间*。如果一个典型的文本文件存在 60% 的冗余的话，那么对于其他类型的文件，例如音频或视频文件来说，这个比率会更高。不同于文本文件，这些其他类型的媒体文件占据的空间也更大，所以很早就出现了回收这些浪费的空间的需求。工程师们设计了可以应用于特定用途的文件类型的经过优化的算法。用于文件的压缩算法可以大致分为两类：

* *无损压缩*。在压缩与解压缩的循环期间，不会对要恢复的数据进行修改。复原后的数据与原始数据是一致的（比特与比特之间一一对应）。对于图片文件来说，`gif` 或者 `png` 格式的文件就是采用了无损压缩算法。
* *有损压缩*。在压缩与解压缩的循环期间，会对原始数据进行修改，但是会（希望）以用户无法觉察的方式进行。网络上的视频文件通常采用有损压缩算法，`jpeg` 格式的图片也是有损压缩。

一些特定的文件格式既可以采用无损压缩算法，又可以采用有损压缩算法，例如 `webp`，并且有损压缩算法可以对压缩比率进行配置，当然这会导致压缩品质的不同。为了使一个站点获得更好的性能，理想情况是在保持可以接受的品质水准的前提下，压缩比率尽可能得高。对于图片来说，通过压缩工具生成的图片对于 Web 应用来说，优化程度可能依然不够高。一般建议选用在保持所要求的品质的前提下压缩比率尽可能高的工具。这里有各种各样的工具专门用来干这个。

有损压缩通常会比无损压缩效率更高一些。

**备注：** 由于数据压缩技术在一些特定类型的文件上效果很好，再次进行压缩通常没有什么效果。事实上，这种做法常常会适得其反，因为间接开销（该类型算法通常需要使用字典，而字典的大小是会计入初始大小的）会比在压缩过程中获取的额外收益要高，从而会使文件的体积增加。不要对压缩格式的文件应用如下两种压缩技术。

端到端压缩技术
-------

对于各种压缩手段来说，端到端压缩技术是 Web 站点性能提升最大的地方。端到端压缩技术指的是消息主体的压缩是在服务器端完成的，并且在传输过程中保持不变，直到抵达客户端。不管途中遇到什么样的中间节点，它们都会使消息主体保持原样。

![服务器通过网络节点向客户端发送一个压缩的 HTTP 主体。该主体直到到达客户端之前，不会在网络中的任何一跳之间进行解压缩。](/zh-CN/docs/Web/HTTP/Compression/httpenco1.png)

所有的现代浏览器及服务器都支持该技术，唯一需要协商的是所采用的压缩算法。这些压缩算法是为文本内容进行过优化的。在上世纪 90 年代，压缩技术快速发展，为数众多的算法相继出现，扩大了可选的范围。如今只有两种算法有着举足轻重的地位：`gzip` 应用最广泛，`br` 则是新的挑战者。

为了选择要采用的压缩算法，浏览器和服务器之间会使用主动协商机制。浏览器发送 `Accept-Encoding` 标头，其中包含有它所支持的压缩算法，以及各自的优先级，服务器则从中选择一种，使用该算法对响应的消息主体进行压缩，并且发送 `Content-Encoding` 标头来告知浏览器它选择了哪一种算法。由于该内容协商过程是基于编码类型来选择资源的展现形式的，在响应时，服务器至少发送一个包含 `Accept-Encoding` 的 `Vary` 标头以及该标头；这样的话，缓存服务器就可以对资源的不同展现形式进行缓存。

![客户端使用“Accept-Encoding:br, gzip”标头请求内容。服务器使用 Brotli 算法压缩的主体以及所需的“Content-Encoding”和“Vary”标头进行响应。](/zh-CN/docs/Web/HTTP/Compression/httpcompression1.png)

由于压缩技术可以带来很大的性能提升，建议对除了已经经过压缩的文件如图片、音频和视频文件之外的其他类型的文件均进行压缩。

Apache 服务器支持数据压缩，有 mod\_deflate可供使用；nginx 中有ngx\_http\_gzip\_module 模块；在 IIS 中则可以使用 `<httpCompression>` 元素。

逐跳压缩技术
------

逐跳压缩技术尽管与端到端压缩技术有些类似，但是它们在一点上有着本质的区别：即这里的压缩指的不是对源头服务器上的资源的压缩，以此来创建一份特定的展现形式然后进行传输，而是对客户端与服务器端之间的任意两个节点之间传递的消息的主体的压缩。在两个相邻的中间节点之间的连接上，可能会应用*不同*的压缩方式。

![服务器通过网络节点向客户端发送一个未经压缩的 HTTP 主体。该主体在到达客户端之前，由网络上的节点根据“Transfer-Encoding”标头进行压缩和解压缩。](/zh-CN/docs/Web/HTTP/Compression/httpte1.png)

为了实现这个目的，HTTP 协议中采用了与端到端压缩技术所使用的内容协商机制相类似的机制：节点发送请求，使用 `TE` 标头来宣告它的意愿，另外一个节点则从中选择合适的方法，进行应用，然后在 `Transfer-Encoding` 标头中指出它所选择的方法。

![客户端从没有压缩相关标头的服务器请求内容。服务器会使用未经压缩的主体进行响应。该主体在到达客户端之前，由网络上的节点进行压缩和解压缩。](/zh-CN/docs/Web/HTTP/Compression/httpcomp2.png)

在实际应用中，逐跳压缩对于服务器和客户端来说是不可见的，并且很少使用。`TE` 标头和 `Transfer-Encoding` 标头最常用来发送分块响应，允许在获得资源的确切长度之前就可以开始传输。

注意，在单次转发层面使用 `Transfer-Encoding` 标头和压缩技术是如此地少见，以至于 Apache、nginx 或 IIS 等服务器都不太容易配置。此类配置通常用在代理服务器层面。


HTTP 请求方法 - HTTP

HTTP 请求方法
=========

HTTP 定义了一组**请求方法**，以表明要对给定资源执行的操作。指示针对给定资源要执行的期望动作。虽然它们也可以是名词，但这些请求方法有时被称为 *HTTP 动词*。每一个请求方法都实现了不同的语义，但一些共同的特征由一组共享：例如一个请求方法可以是安全的、幂等的或可缓存的。

`GET`

`GET` 方法请求一个指定资源的表示形式，使用 `GET` 的请求应该只被用于获取数据。

`HEAD`

`HEAD` 方法请求一个与 `GET` 请求的响应相同的响应，但没有响应体。

`POST`

`POST` 方法用于将实体提交到指定的资源，通常导致在服务器上的状态变化或副作用。

`PUT`

`PUT` 方法用有效载荷请求替换目标资源的所有当前表示。

`DELETE`

`DELETE` 方法删除指定的资源。

`CONNECT`

`CONNECT` 方法建立一个到由目标资源标识的服务器的隧道。

`OPTIONS`

`OPTIONS` 方法用于描述目标资源的通信选项。

`TRACE`

`TRACE` 方法沿着到目标资源的路径执行一个消息环回测试。

`PATCH`

`PATCH` 方法用于对资源应用部分修改。

规范
--

| Specification |
| --- |
| HTTP Semantics # CONNECT |
| HTTP Semantics # DELETE |
| HTTP Semantics # GET |
| HTTP Semantics # HEAD |
| HTTP Semantics # OPTIONS |
| HTTP Semantics # POST |
| HTTP Semantics # PUT |
| HTTP Semantics # TRACE |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* HTTP 标头


HTTP 响应状态码 - HTTP

HTTP 响应状态码
==========

 HTTP 响应状态码用来表明特定 HTTP 请求是否成功完成。
 响应被归为以下五大类：

1. 信息响应 (`100`–`199`)
2. 成功响应 (`200`–`299`)
3. 重定向消息 (`300`–`399`)
4. 客户端错误响应 (`400`–`499`)
5. 服务端错误响应 (`500`–`599`)

以下状态码由 section 10 of RFC 2616定义。你可以在RFC 7231中找到更新后的规范。

**备注：** 如果你收到的响应不在 此列表 中，则它为非标准响应，可能是服务器软件的自定义响应。

信息响应
----

`100 Continue`

这个临时响应表明，迄今为止的所有内容都是可行的，客户端应该继续请求，如果已经完成，则忽略它。

`101 Switching Protocols`

该代码是响应客户端的 `Upgrade` (en-US) 请求头发送的，指明服务器即将切换的协议。

`102 Processing` (WebDAV)

此代码表示服务器已收到并正在处理该请求，但当前没有响应可用。

`103 Early Hints`

此状态代码主要用于与 `Link` 链接头一起使用，以允许用户代理在服务器准备响应阶段时开始预加载 preloading 资源。

成功响应
----

`200 OK`

请求成功。成功的含义取决于 HTTP 方法：

* `GET`: 资源已被提取并在消息正文中传输。
* `HEAD`: 实体标头位于消息正文中。
* `PUT` or `POST`: 描述动作结果的资源在消息体中传输。
* `TRACE`: 消息正文包含服务器收到的请求消息。

`201 Created`

该请求已成功，并因此创建了一个新的资源。这通常是在 POST 请求，或是某些 PUT 请求之后返回的响应。

`202 Accepted`

请求已经接收到，但还未响应，没有结果。意味着不会有一个异步的响应去表明当前请求的结果，预期另外的进程和服务去处理请求，或者批处理。

`203 Non-Authoritative Information`

服务器已成功处理了请求，但返回的实体头部元信息不是在原始服务器上有效的确定集合，而是来自本地或者第三方的拷贝。当前的信息可能是原始版本的子集或者超集。例如，包含资源的元数据可能导致原始服务器知道元信息的超集。使用此状态码不是必须的，而且只有在响应不使用此状态码便会返回`200 OK`的情况下才是合适的。

`204 No Content`

对于该请求没有的内容可发送，但头部字段可能有用。用户代理可能会用此时请求头部信息来更新原来资源的头部缓存字段。

`205 Reset Content`

告诉用户代理重置发送此请求的文档。

`206 Partial Content`

当从客户端发送`Range`范围标头以只请求资源的一部分时，将使用此响应代码。

`207 Multi-Status` (en-US) (WebDAV)

对于多个状态代码都可能合适的情况，传输有关多个资源的信息。

`208 Already Reported` (en-US) (WebDAV)

在 DAV 里面使用 `<dav:propstat>` 响应元素以避免重复枚举多个绑定的内部成员到同一个集合。

`226 IM Used` (en-US) (HTTP Delta encoding)

服务器已经完成了对资源的`GET`请求，并且响应是对当前实例应用的一个或多个实例操作结果的表示。

重定向消息
-----

`300 Multiple Choice`

请求拥有多个可能的响应。用户代理或者用户应当从中选择一个。（没有标准化的方法来选择其中一个响应，但是建议使用指向可能性的 HTML 链接，以便用户可以选择。）

`301 Moved Permanently`

请求资源的 URL 已永久更改。在响应中给出了新的 URL。

`302 Found`

此响应代码表示所请求资源的 URI 已 *暂时* 更改。未来可能会对 URI 进行进一步的改变。因此，客户机应该在将来的请求中使用这个相同的 URI。

`303 See Other`

服务器发送此响应，以指示客户端通过一个 GET 请求在另一个 URI 中获取所请求的资源。

`304 Not Modified`

这是用于缓存的目的。它告诉客户端响应还没有被修改，因此客户端可以继续使用相同的缓存版本的响应。

`305 Use Proxy`
已弃用

在 HTTP 规范中定义，以指示请求的响应必须被代理访问。由于对代理的带内配置的安全考虑，它已被弃用。

`306 unused`

此响应代码不再使用；它只是保留。它曾在 HTTP/1.1 规范的早期版本中使用过。

`307 Temporary Redirect`

服务器发送此响应，以指示客户端使用在前一个请求中使用的相同方法在另一个 URI 上获取所请求的资源。这与 `302 Found` HTTP 响应代码具有相同的语义，但用户代理 *不能* 更改所使用的 HTTP 方法：如果在第一个请求中使用了 `POST`，则在第二个请求中必须使用 `POST`

`308 Permanent Redirect`

这意味着资源现在永久位于由`Location:` HTTP Response 标头指定的另一个 URI。这与 `301 Moved Permanently` HTTP 响应代码具有相同的语义，但用户代理不能更改所使用的 HTTP 方法：如果在第一个请求中使用 `POST`，则必须在第二个请求中使用 `POST`。

客户端错误响应
-------

`400 Bad Request`

由于被认为是客户端错误（例如，错误的请求语法、无效的请求消息帧或欺骗性的请求路由），服务器无法或不会处理请求。

`401 Unauthorized`

虽然 HTTP 标准指定了"unauthorized"，但从语义上来说，这个响应意味着"unauthenticated"。也就是说，客户端必须对自身进行身份验证才能获得请求的响应。

`402 Payment Required`
实验性

此响应代码保留供将来使用。创建此代码的最初目的是将其用于数字支付系统，但是此状态代码很少使用，并且不存在标准约定。

`403 Forbidden`

客户端没有访问内容的权限；也就是说，它是未经授权的，因此服务器拒绝提供请求的资源。与 `401 Unauthorized` 不同，服务器知道客户端的身份。

`404 Not Found`

服务器找不到请求的资源。在浏览器中，这意味着无法识别 URL。在 API 中，这也可能意味着端点有效，但资源本身不存在。服务器也可以发送此响应，而不是 `403 Forbidden`，以向未经授权的客户端隐藏资源的存在。这个响应代码可能是最广为人知的，因为它经常出现在网络上。

`405 Method Not Allowed`

服务器知道请求方法，但目标资源不支持该方法。例如，API 可能不允许调用`DELETE`来删除资源。

`406 Not Acceptable`

当 web 服务器在执行服务端驱动型内容协商机制后，没有发现任何符合用户代理给定标准的内容时，就会发送此响应。

`407 Proxy Authentication Required`

类似于 `401 Unauthorized` 但是认证需要由代理完成。

`408 Request Timeout`

此响应由一些服务器在空闲连接上发送，即使客户端之前没有任何请求。这意味着服务器想关闭这个未使用的连接。由于一些浏览器，如 Chrome、Firefox 27+ 或 IE9，使用 HTTP 预连接机制来加速冲浪，所以这种响应被使用得更多。还要注意的是，有些服务器只是关闭了连接而没有发送此消息。

`409 Conflict`

当请求与服务器的当前状态冲突时，将发送此响应。

`410 Gone`

当请求的内容已从服务器中永久删除且没有转发地址时，将发送此响应。客户端需要删除缓存和指向资源的链接。HTTP 规范打算将此状态代码用于“有限时间的促销服务”。API 不应被迫指出已使用此状态代码删除的资源。

`411 Length Required`

服务端拒绝该请求因为 `Content-Length` 头部字段未定义但是服务端需要它。

`412 Precondition Failed`

客户端在其头文件中指出了服务器不满足的先决条件。

`413 Payload Too Large`

请求实体大于服务器定义的限制。服务器可能会关闭连接，或在标头字段后返回重试 `Retry-After`。

`414 URI Too Long`

客户端请求的 URI 比服务器愿意接收的长度长。

`415 Unsupported Media Type`

服务器不支持请求数据的媒体格式，因此服务器拒绝请求。

`416 Range Not Satisfiable`

无法满足请求中 `Range` 标头字段指定的范围。该范围可能超出了目标 URI 数据的大小。

`417 Expectation Failed`

此响应代码表示服务器无法满足 `Expect` 请求标头字段所指示的期望。

`418 I'm a teapot`

服务端拒绝用茶壶煮咖啡。笑话，典故来源茶壶冲泡咖啡

`421 Misdirected Request`

请求被定向到无法生成响应的服务器。这可以由未配置为针对请求 URI 中包含的方案和权限组合生成响应的服务器发送。

`422 Unprocessable Entity` (WebDAV)

请求格式正确，但由于语义错误而无法遵循。

`423 Locked` (WebDAV)

正在访问的资源已锁定。

`424 Failed Dependency` (WebDAV)

由于前一个请求失败，请求失败。

`425 Too Early`
实验性

表示服务器不愿意冒险处理可能被重播的请求。

`426 Upgrade Required`

 服务器拒绝使用当前协议执行请求，但在客户端升级到其他协议后可能愿意这样做。
 服务端发送带有`Upgrade` (en-US) 字段的 426 响应 来表明它所需的协议（们）。

`428 Precondition Required`

源服务器要求请求是有条件的。此响应旨在防止'丢失更新'问题，即当第三方修改服务器上的状态时，客户端 `GET` 获取资源的状态，对其进行修改并将其 `PUT` 放回服务器，从而导致冲突。

`429 Too Many Requests`

用户在给定的时间内发送了太多请求（"限制请求速率"）

`431 Request Header Fields Too Large`

服务器不愿意处理请求，因为其头字段太大。在减小请求头字段的大小后，可以重新提交请求。

`451 Unavailable For Legal Reasons`

用户代理请求了无法合法提供的资源，例如政府审查的网页。

服务端错误响应
-------

`500 Internal Server Error`

服务器遇到了不知道如何处理的情况。

`501 Not Implemented`

服务器不支持请求方法，因此无法处理。服务器需要支持的唯二方法（因此不能返回此代码）是 `GET` and `HEAD`.

`502 Bad Gateway`

此错误响应表明服务器作为网关需要得到一个处理这个请求的响应，但是得到一个错误的响应。

`503 Service Unavailable`

服务器没有准备好处理请求。常见原因是服务器因维护或重载而停机。请注意，与此响应一起，应发送解释问题的用户友好页面。这个响应应该用于临时条件和如果可能的话，HTTP 标头 `Retry-After` 字段应该包含恢复服务之前的估计时间。网站管理员还必须注意与此响应一起发送的与缓存相关的标头，因为这些临时条件响应通常不应被缓存。

`504 Gateway Timeout`

当服务器充当网关且无法及时获得响应时，会给出此错误响应。

`505 HTTP Version Not Supported`

服务器不支持请求中使用的 HTTP 版本。

`506 Variant Also Negotiates`

服务器存在内部配置错误：所选的变体资源被配置为参与透明内容协商本身，因此不是协商过程中的适当终点。

`507 Insufficient Storage` (WebDAV)

无法在资源上执行该方法，因为服务器无法存储成功完成请求所需的表示。

`508 Loop Detected` (WebDAV)

服务器在处理请求时检测到无限循环。

`510 Not Extended`

服务器需要对请求进行进一步扩展才能完成请求。

`511 Network Authentication Required`

指示客户端需要进行身份验证才能获得网络访问权限。

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 维基百科上的 HTTP 状态码
* IANA official registry of HTTP status codes


内容协商 - HTTP

内容协商
====

在 HTTP 协议中，***内容协商***是一种机制，用于为同一 URI 提供资源不同的表示形式，以帮助用户代理指定最适合用户的表示形式（例如，哪种文档语言、哪种图片格式或者哪种内容编码）。

**备注：** 你可以在来自 WHATWG 的维基页面发现 HTTP 内容协商的一些缺点。HTML5 提供其他的选择来进行内容协商，例如 `<source>` 元素。

内容协商的基本原则
---------

一份特定的文件被称为一项*资源*。当客户端获取资源的时候，会使用其对应的 URL 发送请求。服务器通过这个 URL 来选择它指向的资源的某一可用的变体——每一个变体称为一种表示形式——然后将这个选定的表示形式返回给客户端。整个资源，以及它的各种表示形式，共享一个特定的 URL。当访问某项资源的时候，**内容协商**会决定如何选择一种指定的表示形式。客户端和服务器端之间存在多种协商方式。

![客户端请求一个 URL。服务器有多个由 URL 表示的资源，并根据请求返回适当的内容。](/zh-CN/docs/Web/HTTP/Content_negotiation/httpnego.png)

最佳表示形式的选取可以通过两种机制实现：

* 客户端设置特定的 HTTP 标头（又称为*服务端驱动型内容协商*或者*主动内容协商*），这是进行内容协商的标准方式。
* 服务器返回 `300`（Multiple Choices）或者 `406`（Not Acceptable）、`415`（Unsupported Media Type）HTTP 响应状态码 （又称为*代理驱动型协商*或者*响应式协商*），这种方式一般用作备选方案。

随着时间的推移，也有其他一些内容协商的提案被提出来，比如透明内容协商以及 `Alternates` 标头。但是它们都没有获得人们的认可从而被遗弃。

服务端驱动型内容协商机制
------------

在*服务端驱动型内容协商*或者主动内容协商中，浏览器（或者其他任何类型的用户代理）会随同 URL 发送一系列的 HTTP 标头。这些标头描述了用户倾向的选择。服务器则以此为线索，通过内部算法来选择最佳方案提供给客户端。如果它不能提供一个合适的资源，它可能使用 `406`（Not Acceptable）、`415`（Unsupported Media Type）进行响应并为其支持的媒体类型设置标头（例如，分别对 POST 和 PATCH 请求使用 `Accept-Post` (en-US) 或 `Accept-Patch` 标头）。相关算法与具体的服务器相关，并没有在规范中进行规定。例如这里有一份 Apache 服务器的内容协商算法。

![客户端请求一个 URL，其中标头表示对内容类型的偏好。服务器有多个由 URL 表示的资源并根据偏好的语言发回内容，然后根据客户端的请求标头压缩请求主体](/zh-CN/docs/Web/HTTP/Content_negotiation/httpnegoserver.png)

HTTP/1.1 规范指定了一系列的标准标头用于启动服务端驱动型内容协商（`Accept`、`Accept-Charset`、`Accept-Encoding`、`Accept-Language`）。尽管严格来说 `User-Agent` 并不在此列，有时候它还是会被用来确定给客户端发送的所请求资源的特定表示形式，不过这种做法不提倡使用。服务器会使用 `Vary` 标头来说明实际上哪些标头被用作内容协商的参考依据（确切来说是与之相关的响应标头），这样可以使缓存的运作更有效。

除此之外，有一个向可供选择的列表中增加更多标头的实验性提案，称为*客户端提示*（Client Hint）。客户端示意机制可以告知运行用户代理的设备类型（例如，是桌面计算机还是移动设备）。

即便服务端驱动型内容协商机制是最常用的对资源特定表示形式进行协商的方式，它也存在如下几个缺点：

* 服务器对浏览器并非全知全能。即便是有了客户端示意扩展，也依然无法获取关于浏览器能力的全部信息。与客户端进行选择的代理驱动型内容协商机制不同，服务器端的选择总是显得有点武断。
* 客户端提供的信息相当冗长（HTTP/2 协议的标头压缩机制缓解了这个问题），并且存在隐私风险（HTTP 指纹识别技术）。
* 因为给定的资源需要返回不同的表示形式，共享缓存的效率会降低，而服务器端的实现会越来越复杂。
### `Accept` 标头

`Accept` 标头列举了用户代理希望接收的媒体资源的 MIME 类型。其中不同的 MIME 类型之间用逗号分隔，同时每一种 MIME 类型会配有一个品质因数（quality factor），该参数明确了不同 MIME 类型之间的相对优先级。

`Accept` 标头的值由浏览器或其他类型的用户代理确定，并且会由于上下文环境的不同而不同。比如在获取 HTML 页面、图片文件、视频文件或者是脚本文件的时候，无论是通过在地址栏中输入资源地址来获取还是通过 `<img>`、`<video>` 或 `<audio>` 元素引用都是不一样的。浏览器可以自由使用它们认为最为合适的标头值；这里有一份常见浏览器 Accept 标头默认值的完整列表。

### `Accept-CH` 标头
实验性

**备注：** 这是被称为*客户端提示*的**实验性**技术方案的一部分，目前仅在 Chrome 46 及以后的版本中得到了实现。Device-Memory 值在 Chrome 61 或更高版本中。

该实验性标头 `Accept-CH` 列出了服务器可以用来选择合适响应的配置数据。合法值如下：

| 值 | 含义 |
| --- | --- |
| `Device-Memory` | 标明客户端设备的内存大小。该值是个估计值，设备的实际内存值会向 2 的次方取整，且除以 1024。比如 512MB 的内存对应的值为 `0.5`。 |
| `Viewport-Width` | 标明用 CSS 像素数值表示的布局视口（layout viewport）宽度。 |
| `Width` | 标明用物理像素值表示的资源宽度（换句话说就是一张图片的固有大小）。 |

### `Accept-CH-Lifetime` 标头

**备注：** 这是被称为*客户端提示*的**实验性**技术方案的一部分，目前仅在 Chrome 61 及以后的版本中得到了实现。

`Accept-CH-Lifetime` 标头与 `Accept-CH` 标头的 `Device-Memory` 值一起使用，并指示设备应与服务器共享设备内存的时间量。该值以毫秒为单位给出，并且是可选的。

### `Accept-Encoding` 标头

`Accept-Encoding` 标头明确说明了（接收端）可以接受的内容编码形式（所支持的压缩算法）。该标头的值是一个 Q 因子清单（例如 `br, gzip;q=0.8`），用来提示不同编码类型值的优先级顺序。默认值 `identity` 的优先级最低（除非声明为其他优先级）。

将 HTTP 消息进行压缩是一种最重要的提升 Web 站点性能的方法。该方法会减小所要传输的数据量的大小，节省可用带宽。浏览器总是会发送该标头，服务器则应该配置为接受它，并且采用一定的压缩方案。

### `Accept-Language` 标头

`Accept-Language` 标头用来提示用户期望获得的自然语言的优先顺序。该标头的值是一个 Q 因子清单（例如 `de, en;q=0.7`）。用户代理的图形界面上所采用的语言通常可以用来设置为默认值，但是大多数浏览器允许设置不同优先级的语言选项。

由于基于配置信息的信息熵的增加，修改后的值可以用作识别用户的指纹，所以不建议对其进行修改，不过这样的话 Web 站点也就不能依赖该值来揭示用户的真实期望。站点设计者不能过度热衷于通过这个标头来进行语言检测，因为它可能会导致糟糕的用户体验：

* 站点设计者应该总是提供一种方式来使用户能够覆盖由服务器端选择的语言，例如在页面上提供一个用于语言选择的按钮。大多数用户代理会为 `Accept-Language` 标头提供一个默认值，该值采用的是用户界面的显示的语言。通常终端用户不能对其进行修改，或者是不知道该怎么修改，或者在他们计算机的环境中无法进行修改。
* 一旦用户覆盖了服务器端选择的语言选项，站点就不应该再使用语言检测技术，而应该忠于明确选择的语言选项。换句话说，只有站点的入口页面应该使用这个标头来选择合适的语言。
### `User-Agent` 标头

**备注：** 尽管使用该标头来进行内容选择是合理的，但是依赖这个标头来确定用户代理都支持哪些功能特性通常被认为是一个糟糕的做法。

`User-Agent` 标头可以用来识别发送请求的浏览器。该字符串中包含有用空格间隔的*产品标记符*及*注释*的清单。

*产品标记符*由产品名称、后面紧跟的“`/`”以及产品版本号构成，例如 `Firefox/4.0.1`。用户代理可以随意添加多少产品标记符都可以。*注释*是一个用括号分隔的自由形式的字符串。显然括号本身不能用在该字符串中。规范没有规定注释的内部格式，不过一些浏览器会把一些标记符放置在里面，不同的标记符之间使用“`;`”分隔。

### `Vary` 响应标头

与前面列举的 `Accept-*` 形式的由客户端发送的标头相反，`Vary` 标头是由服务器在响应中发送的。它指示了服务器在服务端驱动型内容协商阶段所使用的标头清单。`Vary` 标头是必要的，它用于将决策的规范告知缓存，这样它就可以进行复现。这将使缓存发挥它的作用，同时确保缓存可以向用户提供正确的内容。

特殊值“`*`”意味着在服务端驱动型内容协商过程中同时采纳了未在标头中传递的信息来选择合适的内容。

`Vary` 标头是在 HTTP 协议的 1.1 版本中新添加的，它是为了使缓存恰当地工作。缓存为了能够与服务端驱动型内容协商机制协同工作，需要知道服务器选择传送内容的规范。这样的话，缓存服务器就可以重复该算法，直接提供恰当的内容，而不需要向服务器发送更多的请求。显然，通配符“`*`”阻碍了缓存机制发挥作用，因为缓存并不知道该通配符究竟指代哪些元素。有关更多信息，请参见 HTTP 缓存 > Vary 响应。

代理驱动型内容协商机制
-----------

服务端驱动型内容协商也有一些缺点：它不能很好的扩展。在协商机制中，每一个特性需要对应一个标头。如果想要使用屏幕大小、分辨率或者其他方面的特性，就需要创建一个新的 HTTP 标头。而且在每一次请求中都必须发送这些标头。在标头很少的时候，这并不是问题，但是随着数量的增多，消息的体积会导致性能的下降。带有精确信息的标头发送的越多，信息熵就会越大，也就准许了更多 HTTP 指纹识别行为，以及与此相关的隐私问题的发生。

从 HTTP 协议制定之初，该协议就准许另外一种协商机制：*代理驱动型内容协商*，或称为*响应式协商*。在这种协商机制中，当面临不明确的请求时，服务器会返回一个页面，其中包含了可供选择的资源的链接。资源呈现给用户，由用户做出选择。

![客户端请求一个 URL，其中标头表示对内容类型的偏好。服务器有多个由 URL 表示的资源并发回多个响应，因此客户端可以选择应用了首选压缩算法的主体。](/zh-CN/docs/Web/HTTP/Content_negotiation/httpnego3.png)

不幸的是，HTTP 标准没有明确指定提供可选资源链接的页面的格式，这阻碍了该过程的无痛自动化。除了退回至*服务端驱动型内容协商*外，这种自动化方法几乎无一例外都是通过脚本技术来完成的，尤其是 JavaScript 重定向技术：在检测了协商的条件之后，脚本会触发重定向动作。另外一个问题是，为了获得实际的资源，需要额外发送一次请求，减慢了将资源呈现给用户的速度。


HTTP Cookie - HTTP

HTTP Cookie
===========

HTTP Cookie（也叫 Web Cookie 或浏览器 Cookie）是服务器发送到用户浏览器并保存在本地的一小块数据。浏览器会存储 cookie 并在下次向同一服务器再发起请求时携带并发送到服务器上。通常，它用于告知服务端两个请求是否来自同一浏览器——如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。

Cookie 主要用于以下三个方面：

会话状态管理

如用户登录状态、购物车、游戏分数或其他需要记录的信息

个性化设置

如用户自定义设置、主题和其他设置

浏览器行为跟踪

如跟踪分析用户行为等

Cookie 曾一度用于客户端数据的存储，因当时并没有其他合适的存储办法而作为唯一的存储手段，但现在推荐使用现代存储 API。由于服务器指定 Cookie 后，浏览器的每次请求都会携带 Cookie 数据，会带来额外的性能开销（尤其是在移动环境下）。新的浏览器 API 已经允许开发者直接将数据存储到本地，如使用 Web storage API（`localStorage` 和 `sessionStorage`）或 IndexedDB 。

**备注：** 要查看 Cookie 存储（或网页上能够使用其他的存储方式），你可以在开发者工具中启用存储查看器（Storage Inspector）功能，并在存储树上选中 Cookie。

创建 Cookie
---------

服务器收到 HTTP 请求后，服务器可以在响应标头里面添加一个或多个 `Set-Cookie` 选项。浏览器收到响应后通常会保存下 Cookie，并将其放在 HTTP `Cookie` 标头内，向同一服务器发出请求时一起发送。你可以指定一个过期日期或者时间段之后，不能发送 cookie。你也可以对指定的域和路径设置额外的限制，以限制 cookie 发送的位置。关于下面提到的头部属性的详细信息，请参考 `Set-Cookie` 文章。

### `Set-Cookie` 和 `Cookie` 标头

服务器使用 `Set-Cookie` 响应头部向用户代理（一般是浏览器）发送 Cookie 信息。一个简单的 Cookie 可能像这样：

```http
Set-Cookie: <cookie-name>=<cookie-value>
```

这指示服务器发送标头告知客户端存储一对 cookie：

```http
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy\_cookie=choco
Set-Cookie: tasty\_cookie=strawberry

[页面内容]
```

现在，对该服务器发起的每一次新请求，浏览器都会将之前保存的 Cookie 信息通过 `Cookie` 请求头部再发送给服务器。

```http
GET /sample\_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy\_cookie=choco; tasty\_cookie=strawberry
```

**备注：** 如何在以下几种服务端程序中设置 `Set-Cookie` 响应头信息：

* PHP
* Node.JS
* Python
* Ruby on Rails

### 定义 Cookie 的生命周期

Cookie 的生命周期可以通过两种方式定义：

* *会话期* Cookie 会在当前的会话结束之后删除。浏览器定义了“当前会话”结束的时间，一些浏览器重启时会使用*会话恢复*。这可能导致会话 cookie 无限延长。
* *持久性* Cookie 在过期时间（`Expires`）指定的日期或有效期（`Max-Age`）指定的一段时间后被删除。

例如：

```http
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
```

**备注：** 当 Cookie 的过期时间（ `Expires`）被设定时，设定的日期和时间只与客户端相关，而不是服务端。

如果你的站点对用户进行身份验证，则每当用户进行身份验证时，它都应重新生成并重新发送会话 Cookie，甚至是已经存在的会话 Cookie。此技术有助于防止会话固定攻击（session fixation attacks） (en-US)，在该攻击中第三方可以重用用户的会话。

### 限制访问 Cookie

有两种方法可以确保 `Cookie` 被安全发送，并且不会被意外的参与者或脚本访问：`Secure` 属性和 `HttpOnly` 属性。

标记为 `Secure` 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端。它永远不会使用不安全的 HTTP 发送（本地主机除外），这意味着中间人攻击者无法轻松访问它。不安全的站点（在 URL 中带有 `http:`）无法使用 `Secure` 属性设置 cookie。但是，`Secure` 不会阻止对 cookie 中敏感信息的访问。例如，有权访问客户端硬盘（或，如果未设置 `HttpOnly` 属性，则为 JavaScript）的人可以读取和修改它。

JavaScript `Document.cookie` API 无法访问带有 `HttpOnly` 属性的 cookie；此类 Cookie 仅作用于服务器。例如，持久化服务器端会话的 Cookie 不需要对 JavaScript 可用，而应具有 `HttpOnly` 属性。此预防措施有助于缓解跨站点脚本（XSS） (en-US)攻击。

示例：

```http
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
```

### 定义 Cookie 发送的位置

`Domain` 和 `Path` 标识定义了 Cookie 的*作用域*：即允许 Cookie 应该发送给哪些 URL。

#### Domain 属性

`Domain` 指定了哪些主机可以接受 Cookie。如果不指定，该属性默认为同一 host 设置 cookie，*不包含子域名*。如果指定了 `Domain`，则一般包含子域名。因此，指定 `Domain` 比省略它的限制要少。但是，当子域需要共享有关用户的信息时，这可能会有所帮助。

例如，如果设置 `Domain=mozilla.org`，则 Cookie 也包含在子域名中（如 `developer.mozilla.org`）。

#### Path 属性

`Path` 属性指定了一个 URL 路径，该 URL 路径必须存在于请求的 URL 中，以便发送 `Cookie` 标头。以字符 `%x2F` (“/”) 作为路径分隔符，并且子路径也会被匹配。

例如，设置 `Path=/docs`，则以下地址都会匹配：

* `/docs`
* `/docs/`
* `/docs/Web/`
* `/docs/Web/HTTP`

但是这些请求路径不会匹配以下地址：

* `/`
* `/docsets`
* `/fr/docs`

#### SameSite 属性

`SameSite` 属性允许服务器指定是否/何时通过跨站点请求发送（其中站点由注册的域和*方案*定义：http 或 https）。这提供了一些针对跨站点请求伪造攻击（CSRF）的保护。它采用三个可能的值：`Strict`、`Lax` 和 `None`。

使用 `Strict`，cookie 仅发送到它来源的站点。`Lax` 与 Strict 相似，只是在用户*导航*到 cookie 的源站点时发送 cookie。例如，通过跟踪来自外部站点的链接。`None` 指定浏览器会在同站请求和跨站请求下继续发送 cookie，但*仅在安全的上下文中*（即，如果 `SameSite=None`，且还必须设置 `Secure` 属性）。如果没有设置 `SameSite` 属性，则将 cookie 视为 `Lax`。

下面是例子：

```http
Set-Cookie: mykey=myvalue; SameSite=Strict
```

**备注：** 与 `SameSite` 相关的标准最近发生了变化（MDN 记录了上面的新行为）。有关在特定浏览器版本中如何处理属性的信息，请参阅 cookie 浏览器兼容性表：

* 如果 `SameSite` 未指定，则 `SameSite=Lax` 时新的默认值。以前，默认情况下会为有请求发送 cookie。
* `SameSite=None` 的 cookie 还必须指定 `Secure` 属性（它们需要安全上下文）。
* cookie 使用不同的方案（`http:` 或 `https:`）发送来自同一域的 cookie，则不再视为来自同一站点。

#### Cookie 前缀

cookie 的机制使得服务器无法确认 cookie 是在安全来源上设置的，甚至无法确定 cookie 最初是在哪里设置的。

子域上的易受攻击的应用程序可以使用 `Domain` 属性设置 cookie，从而可以访问所有其他子域上的该 cookie。*会话劫持*攻击中可能会滥用此机制。有关主要缓解方法，请参阅会话劫持（session fixation） (en-US)。

但是，作为深度防御措施，可以使用 *cookie 前缀*来断言有关 cookie 的特定事实。有两个前缀可用：

`__Host-`

如果 cookie 名称具有此前缀，则仅当它也用 `Secure` 属性标记、从安全来源发送、不包括 `Domain` 属性，并将 `Path` 属性设置为 `/` 时，它才在 `Set-Cookie` 标头中接受。这样，这些 cookie 可以被视为“domain-locked”。

`__Secure-`

如果 cookie 名称具有此前缀，则仅当它也用 `Secure` 属性标记，是从安全来源发送的，它才在 `Set-Cookie` 标头中接受。该前缀限制要弱于 `__Host-` 前缀。

带有这些前缀的 Cookie，如果不符合其限制的会被浏览器拒绝。请注意，这确保了如果子域要创建带有前缀的 cookie，那么它将要么局限于该子域，要么被完全忽略。由于应用服务器仅在确定用户是否已通过身份验证或 CSRF 令牌正确时才检查特定的 cookie 名称，因此，这有效地充当了针对会话劫持的防御措施。

**备注：** 在应用程序服务器上，Web 应用程序**必须**检查完整的 cookie 名称。用户代理程序在将其发送到请求的 `Cookie` 标头之前，**不会**从 cookie 中剥离前缀。

有关 cookie 前缀和浏览器支持的当前状态的更多信息，请参阅Set-Cookie 参考文章的前缀部分。

#### JavaScript 通过 Document.cookie 访问 Cookie

通过 `Document.cookie` 属性可创建新的 Cookie。如果未设置 `HttpOnly` 标记，你也可以从 JavaScript 访问现有的 Cookie。

```js
document.cookie = "yummy\_cookie=choco";
document.cookie = "tasty\_cookie=strawberry";
console.log(document.cookie);
// logs "yummy\_cookie=choco; tasty\_cookie=strawberry"
```

通过 JavaScript 创建的 Cookie 不能包含 `HttpOnly` 标志。

请留意在安全章节提到的安全隐患问题，JavaScript 可以通过跨站脚本攻击（XSS）的方式来窃取 Cookie。

安全
--

**备注：** 当你存储信息到 Cookie 中时，需要明白 cookie 的值是可以被访问，且可以被终端用户所修改的。根据应用程序的不同，可能需要使用服务器查找的不透明标识符，或者研究诸如 JSON Web Tokens 之类的替代身份验证/机密机制。当机器处于不安全环境时，切记*不能*通过 HTTP Cookie 存储、传输敏感信息。

缓解涉及 Cookie 的攻击的方法：

* 使用 `HttpOnly` 属性可防止通过 JavaScript 访问 cookie 值。
* 用于敏感信息（例如指示身份验证）的 Cookie 的生存期应较短，并且 `SameSite` 属性设置为 `Strict` 或 `Lax`。（请参见上方的 SameSite 属性。）在支持 SameSite 的浏览器中，这样做的作用是确保不与跨站点请求一起发送身份验证 cookie。因此，这种请求实际上不会向应用服务器进行身份验证。
跟踪和隐私
-----

### 第三方 Cookie

Cookie 与特定域和方案（例如，`http` 或 `https`）相关联，如果设置了 `Set-Cookie` `Domain` 属性，也可能与子域相关联。如果该 cookie 域和方案匹配当前的页面，则认为该 cookie 和该页面来自同一站点，则称为*第一方 cookie（first-party cookie）*。

如果域和方案不同，则它不认为来自同一个站点，被称为*第三方 cookie（third-party cookie）*。虽然托管网页的服务器设置第一方 Cookie 时，但该页面可能包含存储在其他域中的服务器上的图像或其他组件（例如，广告横幅），这些图像或其他组件可能会设置第三方 Cookie。这些主要用于在网络上进行广告和跟踪。例如，谷歌使用的 cookie 类型。

第三方服务器可以基于同一浏览器在访问多个站点时发送给它的 cookie 来建立用户浏览历史和习惯的配置文件。Firefox 默认情况下会阻止已知包含跟踪器的第三方 cookie。第三方 cookie（或仅跟踪 cookie）也可能被其他浏览器设置或扩展程序阻止。阻止 Cookie 会导致某些第三方组件（例如社交媒体窗口小部件）无法正常运行。

**备注：** 服务器可以（并且应该）设置 cookie SameSite 属性以指定是否可以将 cookie 发送到第三方站点。

### Cookie 相关规定

涉及使用 Cookie 的法律或法规包括：

* 欧盟通用数据隐私法规（GDPR）
* 欧盟的电子隐私权指令
* 加州消费者隐私法

这些规定具有全球影响力。它们适用于这些司法管辖区的用户访问的万维网上的任何站点（欧盟和加利福尼亚，但请注意，加利福尼亚州的法律仅适用于总收入超过 2500 万美元的实体）。

这些法规包括以下要求：

* 向用户表明你的站点使用 cookie。
* 允许用户选择不接收某些或所有 cookie。
* 允许用户在不接收 Cookie 的情况下使用大部分服务。

可能还存在其他法规来管理你当地的 Cookie。你有责任了解并遵守这些规定。有些公司提供“cookie banner”代码，可帮助你遵守这些法规。

在浏览器中存储信息的其他方式
--------------

在浏览器中存储数据的另一种方法是 Web Storage API。window.sessionStorage 和 window.localStorage 属性与持续时间中的会话和永久 cookie 相对应，但是存储限制比 cookie 大，并且永远不会发送到服务器。可以使用 IndexedDB API 或基于它构建的库来存储更多结构化的数据。

有一些技术旨在在 cookie 被删除后重新创建它们。这些被称为“僵尸”cookie。这些技术违反了用户的隐私和用户控制原则，可能违反数据隐私法规，并可能使使用它们的网站承担法律责任。

参见
--

* `Set-Cookie`
* `Cookie`
* `Document.cookie`
* `Navigator.cookieEnabled`
* SameSite cookie
* 使用 Storage Inspector 检查 cookie
* Cookie 规范：RFC 6265
* HTTP cookie on Wikipedia
* Cookies, the GDPR, and the ePrivacy Directive


HTTP 标头（header） - HTTP

HTTP 标头（header）
===============

**HTTP 标头**（header）允许客户端和服务器通过 HTTP 请求（request）或者响应（response）传递附加信息。一个 HTTP 标头由它的名称（不区分大小写）后跟随一个冒号（`:`），冒号后跟随它具体的值。该值之前的空格会被忽略。

自定义专用的标头之前可以与 `X-` 前缀一起使用，但是这种用法被 IETF 在 2012 年 6 月发布的 RFC 6648 明确弃用，原因是其会在非标准字段成为标准时造成不便；其他的标头在 IANA 注册表中列出，其原始内容在 RFC 4229 中定义。此外，IANA 还维护着被提议的新的 HTTP 标头注册表。

根据不同的消息上下文，标头可以分为：

* 请求标头包含有关要获取的资源或客户端或请求资源的客户端的更多信息。
* 响应标头包含有关响应的额外信息，例如响应的位置或者提供响应的服务器。
* 表示标头包含资源主体的信息，例如主体的 MIME 类型或者应用的编码/压缩方案。
* 有效负荷标头包含有关有效载荷数据表示的单独信息，包括内容长度和用于传输的编码。

标头也可以根据代理处理它们的方式进行分组：

* `Connection`
* `Keep-Alive`
* `Proxy-Authenticate`
* `Proxy-Authorization`
* `TE`
* `Trailer`
* `Transfer-Encoding`
* `Upgrade` (en-US)（另见协议升级机制）。

端到端（End-to-end）标头

这类标头*必须*被传输到最终的消息接收者：请求的服务器或者响应的客户端。中间的代理必须重新转发这些未经修改的标头，并且必须缓存它们。

逐跳（Hop-by-hop）标头

这类标头仅对单次传输连接有意义，并且*不得由*代理重传或者缓存。注意，只能使用 `Connection` 标头来设置逐跳标头。

验证（Authentication）
------------------

`WWW-Authenticate`

定义应该用于访问资源的身份验证方法。

`Authorization`

包含用于向服务器验证用户代理身份的凭据。

`Proxy-Authenticate`

定义应用于访问代理服务器后面资源的身份验证方法。

`Proxy-Authorization`

包含用于使用代理服务器验证用户代理的凭据。

缓存（Cache）
---------

`Age`

对象在代理缓存中的时间（以秒为单位）。

`Cache-Control`

请求和响应中缓存机制的指令。

`Clear-Site-Data`

清除与请求网站相关联的浏览器数据（例如 cookie、storage、cache）。

`Expires`

响应被视为过时的日期/时间。

`Pragma`

特定于实现的标头可能会在请求—响应链（request-response chain）的任何地方产生各种影响。用于向后兼容 `Cache-Control` 标头尚不存在的 HTTP/1.0 缓存。

`Warning`
已弃用

关于可能出现的问题的一般警告信息。

客户端提示（Client hint）
------------------

HTTP 客户端提示是一组请求标头，可提供有关客户端的有用信息，例如设备类型和网络条件，并允许服务器优化针对这些条件提供的服务。

服务器使用 `Accept-CH` 主动向客户端请求它们感兴趣的客户端提示标头。客户端可以选择在后续的请求中包含请求的标头。

`Accept-CH`
实验性

服务器可以使用 `Accept-CH` 标头字段或具有 `http-equiv` 属性的等效 HTML `<meta>` 元素来宣告对客户端提示的支持。

`Accept-CH-Lifetime`
实验性

已弃用

服务器可以要求客户端记住服务器在指定时间段内支持的客户端提示集合，以便在后续请求到服务器源站时能够传送客户端提示。

`Critical-CH` (en-US)
实验性

服务器使用 `Critical-CH` 和 `Accept-CH` 来指定接受的客户端提示，也是重要的客户端提示。

下面列出了不同类别的客户端提示。

### 用户代理客户端提示

用户代理客户端提示是请求标头，其提供有关用户代理、它运行的平台/架构以及在用户代理或平台上设置的用户首选项信息：

`Sec-CH-Prefers-Reduced-Motion` (en-US)
实验性

用户代理的减少动画运动首选项设置。

`Sec-CH-UA`
实验性

用户代理的品牌（brand）和版本。

`Sec-CH-UA-Arch`
实验性

用户代理的底层平台架构。

`Sec-CH-UA-Bitness`
实验性

用户代理的底层 CPU 架构位数（例如“64”位）。

`Sec-CH-UA-Full-Version`
已弃用

用户代理的完整语义版本字符串。

`Sec-CH-UA-Full-Version-List`
实验性

用户代理品牌（brand）列表中每个品牌的完整版本。

`Sec-CH-UA-Mobile` (en-US)
实验性

用户代理是否在手机设备上运行，或者更一般地说，更偏好“手机”用户体验。

`Sec-CH-UA-Model` (en-US)
实验性

用户代理的设备模型。

`Sec-CH-UA-Platform` (en-US)
实验性

用户代理的底层操作系统/平台。

`Sec-CH-UA-Platform-Version` (en-US)
实验性

用户代理的底层操作系统版本。

### 设备客户端提示

`Content-DPR` (en-US)
已弃用

实验性

*响应标头*用于在使用 `DPR` 客户端提示选择图像资源的请求中，确认图像设备与像素的比率。

`Device-Memory`
已弃用

实验性

可用客户端 RAM 内存的近似值。这是设备内存 API (en-US) 的一部分。

`DPR`
已弃用

实验性

客户端设备像素比（DPR），即每个 CSS 像素对应的物理设备像素数。

`Viewport-Width` (en-US)
已弃用

实验性

一个数字，表示以 CSS 像素为单位的布局视口宽度。提供的像素值是四舍五入到最小整数（即上限值）的数字。

`Width` (en-US)
已弃用

实验性

一个数字，表示以物理像素为单位的所需资源宽度（即图像的固有尺寸）。

### 网络客户端提示

网络客户端提示允许服务器根据用户选择以及网络带宽和延迟来选择发送什么信息。

`Downlink` (en-US)

客户端连接到服务器的近似带宽，以 Mbps 为单位。这是网络信息 API 的一部分。

`ECT` (en-US)

最匹配连接延迟和带宽的有效连接类型 (en-US)（“网络配置文件”）。这是网络信息 API 的一部分。

`RTT` (en-US)

应用层往返时间（RTT），以毫秒为单位，其中包括服务器处理时间。这是网络信息 API 的一部分。

`Save-Data`
实验性

一个布尔值，指示用户代理对减少数据使用量的偏好。

条件（Conditional）
---------------

`Last-Modified`

资源的最后修改日期，用于比较同一个资源的多个版本。它不如 `ETag` 准确，但在某些环境中更容易计算。使用 `If-Modified-Since` 和 `If-Unmodified-Since` 的条件请求可以使用此值来更改请求的行为。

`ETag`

标识资源版本的唯一字符串。使用 `If-Match` 和 `If-None-Match` 的条件请求使用此值来更改请求的行为。

`If-Match`

使请求有条件，并且仅当存储的资源与给定的 ETag 之一匹配时才应用该方法。

`If-None-Match`

使请求有条件，并且仅当存储的资源与给定的 ETag 都*不*匹配时才应用该方法。这用于更新缓存（用于安全请求），或防止在资源已存在时上传新资源。

`If-Modified-Since`

使请求有条件，并期望只有在给定日期后修改资源时才请求传输资源。仅当缓存过期时才用于传输数据。

`If-Unmodified-Since`

使请求有条件，并期望只有在给定日期后资源未被修改时才请求传输资源。这确保了特定范围的新片段与先前片段的一致性，或者在修改现有文档时实现乐观的（optimistic）并发控制系统。

`Vary`

确定如何匹配请求标头以决定是否可以使用缓存的响应而不是从源服务器请求新的响应。

连接管理（Connection management）
---------------------------

`Connection`

控制当前事务完成后网络连接是否保持打开状态。

`Keep-Alive`

控制持久连接应保持打开状态的时间。

内容协商（Content negotiation）
-------------------------

内容协商（Content negotiation）标头。

`Accept`

通知服务器可以发回的数据类型。

`Accept-Encoding`

编码算法，通常是压缩算法，用于返回的资源。

`Accept-Language`

通知服务器有关服务器预期返回的人类语言。这是一个提示，不一定在用户的完全控制之下：服务器应该始终注意不要覆盖明确的用户选择（比如从下拉列表中选择一种语言）。

控制（Control）
-----------

`Expect`

表示服务器需要满足的期望才能正确处理请求。

`Max-Forwards`

使用 `TRACE` 时，指示请求在被反映到发送方之前可以执行的最大跃点数。

Cookie
------

`Cookie`

包含先前由服务器使用 `Set-Cookie` 标头发送存储的 HTTP cookie。

`Set-Cookie`

将 cookie 从服务器发送到用户代理。

CORS
----

*在此处了解更多关于 CORS。*

`Access-Control-Allow-Origin`

指示响应的资源是否可以被给定的来源共享。

`Access-Control-Allow-Credentials`

指示当请求的凭证标记为 true 时，是否可以公开对该请求响应。

`Access-Control-Allow-Headers`

用在对预检请求的响应中，指示实际的请求中可以使用哪些 HTTP 标头。

`Access-Control-Allow-Methods`

指定对预检请求的响应中，哪些 HTTP 方法允许访问请求的资源。

`Access-Control-Expose-Headers`

通过列出标头的名称，指示哪些标头可以作为响应的一部分公开。

`Access-Control-Max-Age`

指示预检请求的结果能被缓存多久。

`Access-Control-Request-Headers`

用于发起一个预检请求，告知服务器正式请求会使用哪些 HTTP 标头。

`Access-Control-Request-Method`

用于发起一个预检请求，告知服务器正式请求会使用哪一种 HTTP 请求方法。

`Origin`

指示获取资源的请求是从什么源发起的。

`Timing-Allow-Origin`

指定特定的源，以允许其访问 Resource Timing API 功能提供的属性值，否则由于跨源限制，这些值将被报告为零。

下载
--

`Content-Disposition`

指示传输的资源是否应内联显示（没有标题的默认行为），或者是否应像下载一样处理并且浏览器应显示“另存为”对话框。

消息主体信息
------

`Content-Length`

资源的大小，以十进制字节数表示。

`Content-Type`

指示资源的媒体类型。

`Content-Encoding`

用于指定压缩算法。

`Content-Language`

描述面向受众的人类语言，以便用户可以根据自己的首选语言进行区分。

`Content-Location`

指示返回数据的备用位置。

代理
--

`Forwarded`

包含来自代理服务器面向客户端的信息，当请求路径中涉及代理时，这些信息会被更改或丢失。

`X-Forwarded-For`
非标准

标识通过 HTTP 代理或负载均衡器（load balancer）连接到 Web 服务器的客户端的原始 IP 地址。

`X-Forwarded-Host`
非标准

标识请求客户端用于连接到你的代理或负载均衡器（load balancer）的原始主机。

`X-Forwarded-Proto`
非标准

标识客户端用于连接到你的代理或负载均衡器（load balancer）的协议（HTTP 或 HTTPS）。

`Via`

由代理添加，包括正向和反向代理，并且可以出现在请求标头和响应标头中。

重定向
---

`Location`

指示要将页面重定向到的 URL。

`Refresh`

指示浏览器重新加载页面或重定向到另一个页面。采用与带有 `http-equiv="refresh"` 的 `meta` 元素相同的值。

请求上下文
-----

`From`

包含一个电子邮箱地址，这个电子邮箱地址属于发送请求的用户代理的实际掌控者的人类用户。

`Host`

指定服务器的域名（用于虚拟主机）和（可选）服务器侦听的 TCP 端口号。

`Referer`

前一个网页的地址，表示从该网页链接（进入）到当前请求的页面。

`Referrer-Policy`

管理 `Referer` 标头中发送的哪些引用信息应包含在发出的请求中。

`User-Agent`

包含一个特征字符串，允许网络协议对端识别发起请求的用户代理软件的应用程序类型、操作系统、软件供应商或软件版本。另请参阅 Firefox 用户代理字符串参考。

响应上下文
-----

`Allow`

列出资源所支持的 HTTP 方法的集合。

`Server`

包含了处理请求的源头服务器所用到的软件相关信息。

范围请求
----

`Accept-Ranges`

指示服务器是否支持范围请求，如果支持，范围可以用哪个单位表示。

`Range`

指示服务器应返回的文档部分。

`If-Range`

创建一个条件范围请求，只有在给定的 etag 或日期与远程资源匹配时才会满足。用于防止从资源的不兼容版本下载两个范围。

`Content-Range`

指示部分消息在完整正文消息中的位置。

安全
--

`Cross-Origin-Embedder-Policy`（COEP）

允许服务器为给定文档声明嵌入器（embedder）策略。

`Cross-Origin-Opener-Policy` (en-US)（COOP）

防止其他域打开/控制窗口。

`Cross-Origin-Resource-Policy`（CORP）

防止从其他域读取应用了此标头的资源的响应。

`Content-Security-Policy`（CSP）

控制允许用户代理能够为给定页面加载哪些资源。

`Content-Security-Policy-Report-Only`

允许 Web 开发人员通过监控而不是强制执行其效果来试验策略。这些由 JSON 文档组成的违规报告包含通过 HTTP `POST` 请求发送到指定的 URI。

`Expect-CT`

允许网站选择报告和/或执行证书透明度要求，这可以让你注意到该网站使用了欺诈性证书。当站点启用 Expect-CT 标头时，他们将请求 Chrome 检查该站点的任何证书是否出现在公共证书透明度日志中。

`Origin-Isolation`
实验性

提供一种允许 Web 应用程序隔离其来源的机制。

`Permissions-Policy`

提供一种机制来允许和拒绝在网站自己的框架和它嵌入的 `<iframe>` 中使用浏览器特性。

`Strict-Transport-Security`（HSTS）

强制使用 HTTPS 而不是 HTTP 进行通信。

`Upgrade-Insecure-Requests`

向服务器发送一个信号，表达客户端对加密和身份验证响应的偏好，并且它可以成功处理 `upgrade-insecure-requests` 指令。

`X-Content-Type-Options`

禁用 MIME 嗅探并强制浏览器使用 `Content-Type` 中给出的类型。

`X-Download-Options`

`X-Download-Options` HTTP 标头指示浏览器（Internet Explorer）不应显示“打开”已从应用程序下载的文件的选项，以防止网络钓鱼攻击，否则该文件将获得在应用程序上下文中执行的权限。

`X-Frame-Options` (XFO)

指示是否应允许浏览器在 `<frame>`、`<iframe>`、`<embed>` 或 `<object>` 中呈现页面。

`X-Permitted-Cross-Domain-Policies`

指定是否允许跨域策略文件（`crossdomain.xml`)。该文件可能会定义一项策略，以授予客户端（例如 Adobe 的 Flash Player（现已过时）、Adobe Acrobat、Microsoft Silverlight（现已过时）或 Apache Flex）处理跨域数据的权限，否则这些客户端将因同源（Same-Origin）策略而受到限制。有关详细信息，请参阅跨域策略文件规范。

`X-Powered-By`

可能由托管环境或其他框架设置，并包含有关它们的信息，但不会为应用程序或其访问者提供任何用处。取消设置此标头以避免暴露潜在的漏洞。

`X-XSS-Protection`

启用跨站点脚本过滤。

### fetch 元数据请求标头

fetch 元数据请求标头提供有关发起请求的上下文的信息。这允许服务器根据请求的来源和资源将如何使用来决定是否允许请求。

`Sec-Fetch-Site`

它是一个请求标头，指示请求发起者的源与其目标源之间的关系。它是一个结构化标头（Structured Header），其值是一个标记，可能的值有 `cross-site`、`same-origin`、`same-site` 和 `none`。

`Sec-Fetch-Mode`

它是一个请求标头，向服务器指示请求的模式。它是一个结构化标头（Structured Header），其值是一个标记，可能的值有 `cors`、`navigate`、`no-cors`、`same-origin` 和 `websocket`。

`Sec-Fetch-User`

它是一个请求标头，指示导航请求是否由用户激活触发。它是一个结构化标头（Structured Header），其值为布尔值，因此可能的值为 `?0` 表示 false，`?1` 表示 true。

`Sec-Fetch-Dest`

它是一个请求标头，指示请求到服务器的目的地。它是一个结构化标头（Structured Header），其值为具有可能值的标记 `audio`、`audioworklet`、`document`、`embed`、`empty`、`font`、`image`、`manifest`、`object`、`paintworklet`、`report`、`script`、`serviceworker`、`sharedworker`、`style`、`track`、`video`、`worker` 和 `xslt`。

`Service-Worker-Navigation-Preload` (en-US)

在 service worker 启动期间以抢占式请求发送到 `fetch()` 资源的请求标头。该值由 `NavigationPreloadManager.setHeaderValue()` (en-US) 设置，可用于通知服务器应返回与正常 `fetch()` 操作不同的资源。

服务器发送的事件
--------

`Last-Event-ID`

待定

`NEL` (en-US)
实验性

定义一种机制，使开发人员能够声明网络错误报告策略。

`Ping-From`

待定

`Ping-To`

待定

`Report-To`

用于指定浏览器向其发送警告和错误报告的服务器端点。

传输编码
----

`Transfer-Encoding`

指定用于将资源安全地传输给用户的编码形式。

`TE`

指定用户代理愿意接受的传输编码。

`Trailer`

允许发送方在分块消息的末尾包含其他字段。

WebSocket
---------

`Sec-WebSocket-Key`

待定

`Sec-WebSocket-Extensions`

待定

`Sec-WebSocket-Accept`

待定

`Sec-WebSocket-Protocol`

待定

`Sec-WebSocket-Version`

待定

其他
--

`Accept-Push-Policy`
实验性

客户端可以通过在请求中发送 `Accept-Push-Policy` 标头字段来表达请求的所需推送策略。

`Accept-Signature`
实验性

客户端可以发送 `Accept-Signature` 标头字段以指示利用任何可用的签名并指示其支持哪些签名类型。

`Alt-Svc`

用于列出到达此服务的替代方法。

`Date`

包含消息发出的日期和时间。

`Early-Data`
实验性

指示请求已在 TLS 早期数据中传送。

`Large-Allocation`
已弃用

告诉浏览器正在加载的页面将要执行大量分配。

`Link`

`Link` 实体标头字段提供了一种在 HTTP 标头中序列化一个或多个链接的方法。它在语义上等同于 HTML 的 `<link>` 元素。

`Push-Policy`
实验性

`Push-Policy` 定义了处理请求时服务器有关推送的行为。

`Retry-After`

指示用户代理在发出后续请求之前应等待多长时间。

`Signature`
实验性

`Signature` 标头字段传达交换的签名列表，每个签名都附有有关如何确定签名的权限和刷新该签名的信息。

`Signed-Headers`
实验性

`Signed-Headers` 标头字段标识要包含在签名中的响应标头字段的有序列表。

`Server-Timing`

传达给定请求—响应周期的一个或多个指标和描述。

`Service-Worker-Allowed`

用于通过在 Service Worker 脚本的响应中包含此标头来删除路径限制。

`SourceMap`

将生成的代码链接到源映射。

`Upgrade` (en-US)

Upgrade 标头字段的相关 RFC 文档是 RFC 9110, section 7.8。该标准建立了当前客户端、服务器、传输协议连接上升级或更改为不同协议的规则。例如，此标头标准允许客户端从 HTTP 1.1 更改为 WebSocket，假设服务器决定确认并实现升级标头字段。任何一方都不需要接受升级标头字段中指定的条款。它可以在客户端和服务器标头中使用。如果指定了 Upgrade 标头字段，那么发送方也必须发送带有指定升级选项的 Connection 标头字段。有关 Connection 标头字段的详细信息，请参阅上述 RFC 的第 7.6.1 节。

`X-DNS-Prefetch-Control`

控制 DNS 预取，浏览器通过该功能主动对用户可能选择访问的链接以及文档（document）引用的项目（包括图片、CSS、JavaScript 等）的 URL 执行域名解析。

`X-Firefox-Spdy`
已弃用

非标准

待定

`X-Pingback`
非标准

待定

`X-Requested-With`

待定

`X-Robots-Tag`
非标准

`X-Robots-Tag` HTTP 标头用于指示网页如何在公共搜索引擎结果中编入索引。标头实际上等同于 `<meta name="robots" content="...">`。

`X-UA-Compatible`
非标准

由 Internet Explorer 用来指示要使用的文档模式。

贡献
--

你可以通过编写新条目或改进现有条目来提供帮助。

参见
--

* 维基百科中的 HTTP 标头列表
* IANA 注册表
* HTTP 工作组


协议升级机制 - HTTP

协议升级机制
======

HTTP/1.1 协议提供了一种使用 `Upgrade` (en-US) 标头字段的特殊机制，这一机制允许将一个已建立的连接升级成新的、不相容的协议。

这个机制是可选的；它并不能强制协议的更改（通常来说这一机制总是由客户端发起的）。如果它们支持新协议，实现甚至可以不利用 upgrade，在实践中，这种机制主要用于引导 WebSocket 连接。

注意：HTTP/2 明确禁止使用此机制；这个机制只属于 HTTP/1.1。

升级 HTTP/1.1 连接
--------------

客户端使用 `Upgrade` (en-US) 标头字段请求服务器，以降序优先的顺序切换到其中列出的一个协议。

因为 `Upgrade` 是一个逐跳（Hop-by-hop）标头，它还需要在 `Connection` 标头字段中列出。这意味着包含 Upgrade 的典型请求类似于：

```http
GET /index.html HTTP/1.1
Host: www.example.com
Connection: upgrade
Upgrade: example/1, foo/2
```

根据之前的请求的协议，可能需要其他标头信息，例如：从 HTTP/1.1 升级到 WebSocket 允许配置有关 WebSocket 连接的标头详细信息，以及在连接时提供一定程度的安全性。查看升级到 WebSocket 协议的连接获取更多信息。

如果服务器决定升级这次连接，就会返回一个 `101 Switching Protocols` 响应状态码，和一个要切换到的协议的标头字段 Upgrade。如果服务器没有（或者不能）升级这次连接，它会忽略客户端发送的 `Upgrade` 标头字段，返回一个常规的响应：例如一个 `200 OK`).

在发送 `101` 状态码之后，服务器可以使用新协议，并根据需要执行任何额外的特定于协议的握手。实际上，一旦这次升级完成了，连接就变成了双向管道。并且可以通过新协议完成启动升级的请求。

升级机制的常用场合
---------

此处将介绍最常用到 `Upgrade` (en-US) 标头的场合。

### 升级到 websocket 协议的连接

至今为止，最经常会需要升级一个 HTTP 连接的场合就是使用 WebSocket，它总是通过升级 HTTP 或 HTTPS 连接来实现。请记住，当你用 WebSocket API 以及其他大部分实现 WebSocket 的库去建立新的连接时，基本上都不用操心升级的过程，因为这些 API 已经实现了这一步。比如，用如下 API 打开一个 WebSocket 连接：

```js
webSocket = new WebSocket("ws://destination.server.ext", "optionalProtocol");
```

`WebSocket()` 构造函数已经自动完成了发送初始 HTTP/1.1 连接的所有工作，然后为你处理握手及升级过程。

**备注：** 你也可以用 `"wss://"` URL 方式来打开安全的 WebSocket 连接。

如果想要自己重头实现 WebSocket 连接，就必须要处理握手和升级过程。在创建初始 HTTP/1.1 会话之后，你需要发送另一个 HTTP 标准请求来请求升级，但在标头中要带上 `Upgrade` (en-US) 和 `Connection`，也就是：

```http
Connection: Upgrade
Upgrade: websocket
```

### WebSocket 专有的标头

以下标头是在 WebSocket 升级过程中会出现的。除了 `Upgrade` (en-US) 和 `Connection` 标头，其余的通常是可选的，或者由浏览器和服务器都会在交互过程中处理好。

#### `Sec-WebSocket-Extensions`

用于指定一个或多个请求服务器使用的协议级 WebSocket 扩展。允许在一个请求中使用多个 `Sec-WebSocket-Extension` 标头；结果跟在一个标头文件中包含了所有列出的扩展一样。

```http
Sec-WebSocket-Extensions: extensions
```

`extensions`

指需要（或支持）的扩展的逗号分隔列表。这些值来自 IANA WebSocket 扩展名注册表。带参数的扩展使用分号表示。

例如：

```http
Sec-WebSocket-Extensions: superspeed, colormode; depth=16
```

#### `Sec-WebSocket-Key`

该标头向服务器提供确认客户端有权请求升级到 WebSocket 的所需信息。当不安全（HTTP）客户端希望升级时，可以使用该标头，以提供一定程度防止滥用的保护。密钥的值是使用 WebSocket 规范中定义的算法计算的，因此*不提供安全性*。相反，它有助于防止非 WebSocket 客户端无意中或滥用请求 WebSocket 连接。那么，从本质上讲，这个密钥是为了确认“是的，我真的是要打开一个 WebSocket 连接。”

该标头由选择使用它的客户端自动添加；它不能使用 `XMLHttpRequest.setRequestHeader()` 方法添加。

```http
Sec-WebSocket-Key: key
```

`key`

此请求升级的密钥。如果客户端愿意，则添加它，服务器将在响应中包含一个自己的密钥，客户端将在向你发送升级响应之前验证该密钥。

服务器响应的 `Sec-WebSocket-Accept` 标头将基于指定的 `key` 计算的值。

#### `Sec-WebSocket-Protocol`

`Sec-WebSocket-Protocol` 标头按优先顺序指定你希望用的一个或者多个 WebSocket 协议。将服务器支持的第一个 WebSocket 协议，由服务器在响应中包含的 `Sec-WebSocket-Protocol` 标头中选择并返回它。你可以在标头中多次使用它；结果与在单个标头中使用逗号分隔的子协议标识符列表相同。

```http
Sec-WebSocket-Protocol: subprotocols
```

`subprotocols`

以逗号分隔的子协议名称列表，按优先顺序排列。子协议可以从 IANA WebSocket 子协议名称注册表中选择，也可以是客户端和服务器共同理解的自定义名称。

#### `Sec-WebSocket-Version`

##### 请求标头

指定客户端希望使用的 WebSocket 协议版本，以便服务器可以确认其是否支持该版本。

```http
Sec-WebSocket-Version: version
```

`version`

客户端在与服务器通信时希望使用的 WebSocket 协议版本。此编号应该是 IANA WebSocket 版本号注册表可能列出的最新版本。WebSocket 协议的最新最终版本是版本 13。

##### 响应标头

如果服务器无法使用指定版本的 Websocket 协议进行通信，它将响应一个错误（例如 426 Upgrade Required），该错误在它的标头中包含一个 `Sec-WebSocket-Version` 标头，其中包含支持的逗号分隔列表的协议版本。如果服务器确实支持请求的协议版本，则响应中不包含 `Sec-WebSocket-Version` 标头。

```http
Sec-WebSocket-Version: supportedVersions
```

`supportedVersions`

服务器支持的 WebSocket 协议版本的逗号分隔列表。

### 仅响应标头

来自服务器的响应可能包含这些。

#### `Sec-WebSocket-Accept`

当服务器愿意发起 WebSocket 连接时，其包含在打开握手过程中来自服务器的响应消息中。它只会在响应标头中出现一次。

```http
Sec-WebSocket-Accept: hash
```

`hash`

如果提供了 `Sec-WebSocket-Key` 标头，那么将通过以下流程计算此标头的值：首先取密钥的值，然后将该值与“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”进行拼接，再取拼接后的字符串的 SHA-1 哈希。最后对得出的 20 字节的值进行 base64 编码以获得该属性的值。

参见
--

* WebSocket API
* HTTP
* 规范和 RFC：
    + RFC 7230
    + RFC 6455
    + RFC 7540


Cross-Origin-Embedder-Policy - HTTP

Cross-Origin-Embedder-Policy
============================

HTTP **`Cross-Origin-Embedder-Policy`** (COEP) 响应标头可防止文档加载未明确授予文档权限 (通过 CORP (en-US) 或者 CORS) 的任何跨域资源。

| Header type | Response header |
| --- | --- |
| Forbidden header name | no |

语法
--
```

Cross-Origin-Embedder-Policy: unsafe-none | require-corp
```

### 指令

`unsafe-none`

这是默认值。允许文档获取跨源资源，而无需通过 CORS 协议或 `Cross-Origin-Resource-Policy` 头。

`require-corp`

 文档只能从相同的源加载资源，或显式标记为可从另一个源加载的资源。
 如果跨源资源支持 CORS，则 `crossorigin` 属性或 `Cross-Origin-Resource-Policy` 头必须使用它来加载资源，而不会被 COEP 阻止。

示例
--

### 某些功能取决于跨域隔离

为了节省时间你可以只接受类似于`SharedArrayBuffer` 或者 `Performance.now()` 对象，只要你的文档有一个值被设置为`require-corp` 的 COEP 头部。
```

Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
```

你可以看看这个头部 `Cross-Origin-Opener-Policy` (en-US) ，这样你设置起来会做的更好。

检查 cross origin isolation 是否成功，你可以再次测试`crossOriginIsolated` 这个属性 是否对窗口和工作的上下文有效：

```js
if (crossOriginIsolated) {
  // Post SharedArrayBuffer
} else {
  // Do something else
}
```

### 避免 CORS 阻塞 COEP

If you enable COEP using `require-corp` and have a cross origin resource that needs to be loaded, it needs to support CORS and you need to explicitly mark the resource as loadable from another origin to avoid blockage from COEP. For example, you can use the `crossorigin` attribute for this image from a third-party site:

```html
<img src="https://thirdparty.com/img.png" crossorigin />
```

规范
--

| Specification |
| --- |
| HTML Standard # coep |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参阅
--

* `Cross-Origin-Opener-Policy` (en-US)


If-None-Match - HTTP

If-None-Match
=============

**`If-None-Match`** 是一个条件式请求首部。对于 GET`GET` 和 `HEAD` 请求方法来说，当且仅当服务器上没有任何资源的 `ETag` 属性值与这个首部中列出的相匹配的时候，服务器端才会返回所请求的资源，响应码为 `200` 。对于其他方法来说，当且仅当最终确认没有已存在的资源的 `ETag` 属性值与这个首部中所列出的相匹配的时候，才会对请求进行相应的处理。

对于 `GET` 和 `HEAD` 方法来说，当验证失败的时候，服务器端必须返回响应码 304（Not Modified，未改变）。对于能够引发服务器状态改变的方法，则返回 412（Precondition Failed，前置条件失败）。需要注意的是，服务器端在生成状态码为 304 的响应的时候，必须同时生成以下会存在于对应的 200 响应中的首部：Cache-Control、Content-Location、Date、ETag、Expires 和 Vary。

`ETag` 属性之间的比较采用的是**弱比较算法**，即两个文件除了每个字节都相同外，内容一致也可以认为是相同的。例如，如果两个页面仅仅在页脚的生成时间有所不同，就可以认为二者是相同的。

当与 `If-Modified-Since` 一同使用的时候，If-None-Match 优先级更高（假如服务器支持的话）。

以下是两个常见的应用场景：

* 采用 `GET` 或 `HEAD` 方法，来更新拥有特定的`ETag` 属性值的缓存。
* 采用其他方法，尤其是 `PUT`，将 `If-None-Match` used 的值设置为 \* ，用来生成事先并不知道是否存在的文件，可以确保先前并没有进行过类似的上传操作，防止之前操作数据的丢失。这个问题属于更新丢失问题的一种。

| Header type | Request header |
| --- | --- |
| Forbidden header name | no |

语法
--
```

If-None-Match: <etag_value>
If-None-Match: <etag_value>, <etag_value>, …
If-None-Match: *
```

指令
--

<etag\_value>

唯一地表示所请求资源的实体标签。形式是采用双引号括起来的由 ASCII 字符串（如"675af34563dc-tr34"），有可能包含一个 W/ 前缀，来提示应该采用弱比较算法（这个是画蛇添足，因为 If-None-Match 用且仅用这一算法）。

`*`

星号是一个特殊值，可以代表任意资源。它只用在进行资源上传时，通常是采用 `PUT` 方法，来检测拥有相同识别 ID 的资源是否已经上传过了。

示例
--
```

If-None-Match: "bfc13a64729c4290ef5b2c2730249c88ca92d82d"

If-None-Match: W/"67ab43", "54ed21", "7892dd"

If-None-Match: *
```

规范
--

| Specification |
| --- |
| HTTP Semantics # field.if-none-match |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

相关内容
----

* `ETag`
* `If-Unmodified-Since`
* `If-Modified-Since`
* `If-Match`
* `304` `Not Modified`
* `412` `Precondition Failed`


Last-Modified - HTTP

Last-Modified
=============

**`Last-Modified`** 是一个响应首部，其中包含源头服务器认定的资源做出修改的日期及时间。它通常被用作一个验证器来判断接收到的或者存储的资源是否彼此一致。由于精确度比 `ETag` 要低，所以这是一个备用机制。包含有 `If-Modified-Since` 或 `If-Unmodified-Since` 首部的条件请求会使用这个字段。

| Header type | Response header |
| --- | --- |
| Forbidden header name | no |
| CORS-safelisted response-header | yes |

语法
--
```

Last-Modified: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
```

指令
--

<day-name>

"Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 或 "Sun" 之一（区分大小写）。

<day>

两位数字表示的天数，例如"04" or "23"。

<month>

"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 之一（区分大小写）。

<year>

4 位数字表示的年份，例如 "1990" 或者"2016"。

<hour>

两位数字表示的小时数，例如 "09" 或者 "23"。

<minute>

两位数字表示的分钟数，例如"04" 或者 "59"。

<second>

两位数字表示的秒数，例如 "04" 或者 "59"。

`GMT`

国际标准时间。HTTP 中的时间均用国际标准时间表示，从来不使用当地时间。

示例
--
```

Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
```

规范
--

| Specification |
| --- |
| HTTP Semantics # field.last-modified |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

相关内容
----

* `If-Modified-Since`
* `If-Unmodified-Since`
* `Etag`


Sec-CH-UA-Full-Version - HTTP

Sec-CH-UA-Full-Version
======================

**已弃用:** 不再推荐使用该特性。虽然一些浏览器仍然支持它，但也许已从相关的 web 标准中移除，也许正准备移除或出于兼容性而保留。请尽量不要使用该特性，并更新现有的代码；参见本页面底部的兼容性表格以指导你作出决定。请注意，该特性随时可能无法正常工作。

 **安全上下文:** 此项功能仅在一些支持的浏览器的安全上下文（HTTPS）中可用。

**备注：** 这个标头将被 `Sec-CH-UA-Full-Version-List` 替代。

**`Sec-CH-UA-Full-Version`** 用户代理客户端提示请求标头提供用户代理完整的版本字符串。

|  |  |
| --- | --- |
| 标头类型 | Request header、客户端提示 |
| Forbidden header name | 是 |

语法
--

```http
Sec-CH-UA-Full-Version: <version>
```

### 指令

`<version>`

包含完整版本号的字符串，如“96.0.4664.93”。

示例
--

服务器请求 `Sec-CH-UA-Full-Version` 标头的方法是在任何来自客户端的请求*响应*中添加 `Accept-CH`，使用所需标头的名称作为它的令牌（token）：

```http
HTTP/1.1 200 OK
Accept-CH: Sec-CH-UA-Full-Version
```

客户端可以选择提供提示（hint），并将 `Sec-CH-UA-Full-Version` 标头添加到后续的请求中。例如，客户端可能会添加标头，如下所示：

```http
GET /GET /my/page HTTP/1.1
Host: example.site

Sec-CH-UA: " Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"
Sec-CH-UA-Mobile: ?0
Sec-CH-UA-Full-Version: "96.0.4664.110"
Sec-CH-UA-Platform: "Windows"
```

规范
--

| Specification |
| --- |
| User-Agent Client Hints # sec-ch-ua-full-version |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* 客户端提示
* 用户代理客户端提示 API (en-US)
* 通过用户代理客户端提示改善用户隐私和开发人员体验（web.dev）
* `Accept-CH`
* HTTP 缓存 > Vary 响应和 `Vary`


Access-Control-Max-Age - HTTP

Access-Control-Max-Age
======================

The **`Access-Control-Max-Age`** 这个响应头表示 preflight request （预检请求）的返回结果（即 `Access-Control-Allow-Methods` 和`Access-Control-Allow-Headers` 提供的信息）可以被缓存多久。

| 报头类型 | Response header |
| --- | --- |
| Forbidden header name | no |

语法
--
```

Access-Control-Max-Age: <delta-seconds>
```

指令
--

<delta-seconds>

 返回结果可以被缓存的最长时间（秒）。
 在 Firefox 中，上限是 24 小时 （即 86400 秒）。
 在 Chromium v76 之前， 上限是 10 分钟（即 600 秒)。
 从 Chromium v76 开始，上限是 2 小时（即 7200 秒)。
 Chromium 同时规定了一个默认值 5 秒。
 如果值为 **-1**，表示禁用缓存，则每次请求前都需要使用 OPTIONS 预检请求。

示例
--

将预检请求的结果缓存 10 分钟：
```

Access-Control-Max-Age: 600
```

规范
--

| Specification |
| --- |
| Fetch Standard # http-access-control-max-age |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

相关内容
----

* `Access-Control-Allow-Headers`
* `Access-Control-Allow-Methods`


From - HTTP

From
====

请求首部 **`From`** 中包含一个电子邮箱地址，这个电子邮箱地址属于发送请求的用户代理的实际掌控者的人类用户。

如果你在运行一个机器人代理程序（比如爬虫），那么 Form 首部应该随请求一起发送，这样的话，在服务器遇到问题的时候，例如机器人代理发送了过量的、不希望收到的或者不合法的请求，站点管理员可以联系到你。

**警告：** 不可以将 From 首部用于访问控制或者身份验证。

| Header type | Request header |
| --- | --- |
| Forbidden header name | no |

语法
--
```

From: <email>
```

指令
--

<email>

一个机器可识别的电子邮箱地址。

示例
--
```

From: webmaster@example.org
```

规范
--

| Specification |
| --- |
| HTTP Semantics # field.from |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

相关内容
----

* `Host`


If-Modified-Since - HTTP

If-Modified-Since
=================

**`If-Modified-Since`** 是一个条件式请求首部，服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回，状态码为 `200` 。如果请求的资源从那时起未经修改，那么返回一个不带有消息主体的 `304` 响应，而在 `Last-Modified` 首部中会带有上次修改时间。不同于 `If-Unmodified-Since`, `If-Modified-Since` 只可以用在 `GET` 或 `HEAD` 请求中。

当与 `If-None-Match` 一同出现时，它（**`If-Modified-Since`**）会被忽略掉，除非服务器不支持 `If-None-Match`。

最常见的应用场景是来更新没有特定 `ETag` 标签的缓存实体。

| Header type | Request header |
| --- | --- |
| Forbidden header name | no |

语法
--
```

If-Modified-Since: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
```

指令
--

<day-name>

"Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 或 "Sun" 之一（区分大小写）。

<day>

两位数字表示的天数，例如"04" or "23"。

<month>

"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 之一（区分大小写）。

<year>

4 位数字表示的年份，例如 "1990" 或者"2016"。

<hour>

两位数字表示的小时数，例如 "09" 或者 "23"。

<minute>

两位数字表示的分钟数，例如"04" 或者 "59"。

<second>

两位数字表示的秒数，例如 "04" 或者 "59"。

`GMT`

国际标准时间。HTTP 中的时间均用国际标准时间表示，从来不使用当地时间。

示例
--
```

If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
```

规范
--

| Specification |
| --- |
| HTTP Semantics # field.if-modified-since |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

相关内容
----

* `ETag`
* `If-Unmodified-since`
* `If-Match`
* `If-None-Match`
* `304` `Not Modified`


Permissions-Policy - HTTP

Permissions-Policy
==================

**实验性:** **这是一项实验性技术**
在将其用于生产之前，请仔细检查浏览器兼容性表格。

**`Permissions-Policy`** 响应标头提供了一种可以在本页面或包含的 iframe 上启用或禁止浏览器特性的机制。

更多的信息，请查看Feature Policy

| Header type | Response header |
| --- | --- |
| Forbidden header name | yes |

语法
--
```

Permissions-Policy: <directive> <allowlist>
```

`<allowlist>`

一个来源列表，在括号中包含的以下一个或多个值，并用空格分隔：

* `*`: 允许在当前文档和所有包含的内容（比如 iframes）中使用本特性。
* `'self'`: 允许在当前文档中使用本特性，但在包含的内容（比如 iframes）仍使用原值。
* `'src'`: (只在 iframe 中允许) 只要在src 中的 URL 和加载 iframe 用的 URL 相同，则本特性在 iframe 中允许，
* `'none'`: 从最上层到包含的内容都禁止本特性。 <origin(s)>: 在特定的源中允许，源 URL 以空格分割。

`*` 或 `none` 值只允许单独使用，而 `self` 和 `src` 值可以与多个源一起使用。

所有的特性都有一个如下的默认的 allowlist

* `*`: 本特性默认在最上层和包含的内容中（iframes）允许。
* `'self'`: 本特性默认在最上层允许，而包含的内容中（iframes）使用源地址相同设定。也就是说本特性在 iframe 中不允许跨域访问。
* `'none'`: 本特性默认在最上层和包含的内容中（iframes）都禁止。
指令
--

`autoplay`
实验性

控制是否允许当前文档自动播放媒体。这种控制是通过接口 `HTMLMediaElement` 来实现。当这种规则被禁用，而且没有用户操作的时候，`HTMLMediaElement.play()` 返回的 `Promise` (en-US) 会拒绝并抛出一个 `DOMException` 异常。`<audio>` 和 `<video>` 上的 autoplay 属性会被忽略。

`camera`

控制是否允许当前文档使用视频输入设备。当这种规则被禁用时，`MediaDevices.getUserMedia()` 返回的 `Promise` 会拒绝并抛出 `NotAllowedError` `DOMException` 异常。

`document-domain` (en-US)
实验性

控制是否允许当前文档设置 `document.domain`。当这种规则被禁用时，尝试设置 `document.domain` 会失败并抛出 `SecurityError` `DOMException` 异常。

`encrypted-media` (en-US)
实验性

控制是否允许当前文档使用 Encrypted Media Extension (en-US) API（EME）。当这种规则被禁用时，`Navigator.requestMediaKeySystemAccess()` (en-US) 返回的 `Promise` (en-US) 会拒绝并抛出 `DOMException` 异常。

`fullscreen` (en-US)

控制是否允许当前文档使用 `Element.requestFullScreen()`。当这种规则被禁用时，返回的 `Promise` 会拒绝并抛出 `TypeError`。

`geolocation` (en-US)

控制是否允许当前文档使用 `Geolocation` 接口。当这种规则被禁用时，调用 `getCurrentPosition()` 和 `watchPosition()` 会返回包含 `PERMISSION_DENIED` 的 `PositionError`。

`microphone` (en-US)

控制是否允许当前文档使用音频输入设备。当这种规则被禁用时，`MediaDevices.getUserMedia()` 返回的 `Promise` 会拒绝并抛出错误 `NotAllowedError`。

`midi` (en-US)

控制是否允许当前文档使用 Web MIDI API (en-US)。当这种规则被禁用时，`Navigator.requestMIDIAccess()` (en-US) 返回的 `Promise` 会拒绝并抛出错误 `DOMException`。

`payment` (en-US)

控制是否允许当前文档使用 Payment Request API。当这种规则被启用时，构造函数 `PaymentRequest()` (en-US) 会抛出错误 `SecurityError`。

`vr` / `xr`

控制是否允许当前文档使用 WebVR API。当这种规则被禁用时，`Navigator.getVRDisplays()` (en-US) 返回的 `Promise` 会拒绝并抛出错误 `DOMException`。

示例
--

SecureCorp Inc. 公司想要在应用中禁用震动和定位 API，则可以在返回的 response 中传递以下定义权限策略的 HTTP 的标头信息：
```

Permissions-Policy: vibrate 'none'; geolocation 'none'
```

通过使用 `'none'` 关键词，不管原来如何设定，这些特性在所有浏览的上下文中都会被禁用。

规范
--

| Specification |
| --- |
| Permissions Policy # permissions-policy-http-header-field |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* Feature Policy
* Using Feature Policy
* Permissions-Policy Tester (Chrome Developer Tools extension)
* `Content-Security-Policy`
* `Referrer-Policy`


Via - HTTP

Via
===

**`Via`** 是一个通用首部，是由代理服务器添加的，适用于正向和反向代理，在请求和响应首部中均可出现。这个消息首部可以用来追踪消息转发情况，防止循环请求，以及识别在请求或响应传递链中消息发送者对于协议的支持能力。

| Header type | General header |
| --- | --- |
| Forbidden header name | yes |

语法
--
```

Via: [ <protocol-name> "/" ] <protocol-version> <host> [ ":" <port> ]
or
Via: [ <protocol-name> "/" ] <protocol-version> <pseudonym>
```

指令
--

<protocol-name>

可选。所使用的协议名称，如 "HTTP"。

<protocol-version>

所使用的协议版本号，例如 "1.1"。

<host> and <port>

公共代理的 URL 及端口号。

<pseudonym>

内部代理的名称或别名。

示例
--
```

Via: 1.1 vegur
Via: HTTP/1.1 GWA
Via: 1.0 fred, 1.1 p.example.net
```

规范
--

| Specification |
| --- |
| HTTP Semantics # field.via |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

相关内容
----

* `X-Forwarded-For`
* Heroku 的代理库 Vegur