完整的Headers列表在IANA 注册表中维护,该文档内包含每个Headers对应的RFC文档,不过这些文档过于详尽且不易理解,所以只适合需要深入学习http协议的开发者阅读。
对http和https的原理感兴趣的,可以参考我之前的博客:http与https原理分析。在该博客内,我们讲到过http的消息结构:
一次http请求包含请求
和响应
两个阶段,它们的消息结构是一致的。
请求阶段指客户端向服务端发送请求的阶段。所发送的消息包含请求行
、请求头部
、一个回车加换行符
以及请求数据
四个部分,如上图所示。
请求行包含本次请求的资源路径、请求方法、协议等;请求头部(Request Headers)携带了关于本次请求的描述信息,如可接受的压缩格式、编码类型、期望的数据响应格式、cookie等参数,服务端将根据这些参数来决定如何封装响应体;回车和换行是隔离消息头部和消息体的标志;请求数据是请求所携带的实体数据,在POST、PUT一类方法中较为常用。
响应阶段是服务端向客户端返回响应数据的阶段。消息结构大致同上,不过服务端会在请求行添加更多参数,如Status Code(状态码)、Remote Address(服务器地址)以及Referrer Policy(来源策略)等;在请求头部则会添加一个响应头(Response Headers),用以描述关于本次响应的参数,如数据长度、缓存策略、编码类型等。客户端会根据响应头参数决定如何解析和缓存响应数据。以下是请求百度首页时的响应信息:
展开Response Headers和Request Headers即可查看本次请求的相关参数,常用于在请求发生异常时定位异常原因。这里的Query String Parameters是浏览器提供的快捷查看查询参数的字段,实际发送的消息头部不包含该字段。
http Headers按照使用的上下文可分为四类:
General Headers
(通用头部),请求和响应阶段都会用到的头部信息Request Headers
(请求头部),只在请求阶段使用的头部信息Response Headers
(响应头部),只在响应阶段使用的头部信息Entity Headers
(实体头部),用于描述数据实体的头部信息,如内容长度(Content-Length)注意,以上只是按照用途进行的概念性分类,实际上General Headers
和Entity Headers
不会单独存在,它们可能存在于请求头内,也可能存在于响应头内。因此对于单次请求,最终的头信息只有两类:请求头和响应头。
下面我们按照字母顺序,依次介绍常用的headers(这里只列举标准消息头,不包含自定义或非标准消息头):
用户代理期望的MIME(Multipurpose Internet Mail Extensions,多用途互联网邮件扩展类型)类型列表,定义于http/1.1。
该字段描述了客户端希望收到的内容类型,如text/plain(纯文本)、text/html(html文档)、image/png(png图片)、application/json(json类型)等。一个MIME类型由一个/
分隔,左侧表示独立类型
,表明文件的分类;右侧是该文件的subtype
,即子类型。如text/html表示该资源的独立类型是text(文本),子类型是html(html类的文本);而image/png表示该资源属于图片类,子类型是png,也就是png格式的图片。以下是五种独立类型:
类型 | 描述 | 示例 |
---|---|---|
text | 人类可读的普通文本 | text/plain, text/html, text/css, text/javascript |
image | 图像类资源 | image/gif, image/png, image/jpeg, image/bmp, image/webp, image/x-icon, image/vnd.microsoft.icon |
audio | 音频文件 | audio/midi, audio/mpeg, audio/webm, audio/ogg, audio/wav |
video | 视频文件 | video/webm, video/ogg |
application | 二进制数据 | application/octet-stream, application/pkcs12, application/vnd.mspowerpoint, application/xhtml+xml, application/xml, application/pdf |
如果不需要限制子类型,可以用*
代替,这将匹配所有该独立类型的资源,如image/*
可以匹配任意类型的图像文件。同样的,*/*
表示不限制任何内容类型。
除了以上独立类型,还有一种复合类型:Multipart
,它表示其内部包含了不同种类的数据内容,包括multipart/form-data
(复合表单)和multipart/byteranges
(文件的某个子集)两种。一般来说这类内容类型不会出现在Accept
字段里,因此这里不作介绍。
通常来说,Accept
字段是由逗号隔开的多个值,表示这些类型都可以接收,如:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
我们注意到,在该例子中,application/xml
和*/*
的后面分别用分号隔开了一个q=0.9
和q=0.8
,这里的q
参数表示该类型的权重,当内容类型与多个类型匹配时,以权重较大的类型优先。q
值的区间为0.0 - 1.0
,0表示不允许该内容类型,值越大,则优先级越高,默认为1。
举个例子,假如服务端返回了一个html类型的文件,我们知道,html文件显然属于text/html
,但它同时也符合类型application/xml
(因为html本身就是xml的子集),当然也符合通配类型*/*
。
那么浏览器该以哪种内容类型来标识该文件呢?这时就需要按权重决定了。由于text/html
没有规定权重值,因此默认为1
;而application/xml
的权重为0.9
;*/*
权重为0.8
。所以浏览器最终将该文件的MIME类型标记为text/html
。
客户端支持的字符集。
目前常用的字符集包括ASCII
、GBK
(中文字符集的国家标准,GB表示“国标”,K表示“扩展”)、UTF-8
、ISO-8859-1
等,对于大多数浏览器,在不指定字符集的情况下,默认使用UTF-8
字符集,由于UTF-8
字符集几乎囊括了世界上所有的字符,因为它具有很好的国际化能力。某些国内浏览器可能默认采用GBK
字符集,当服务端以UTF-8
编码,而浏览器以GBK
解码时,网页就会出现乱码。
Accept-Charset
也支持设置多个字符集,字符集之间以逗号隔开,同时也可以设置权重,如:
Accept-Charset: utf-8, iso-8859-1;q=0.5
客户端支持的压缩方法。
为了减少网络传输的数据量,服务端通常会对响应数据进行压缩,常见的压缩方法包括:gzip
、compress
、deflate
、br
等。客户端接收到数据后,必须根据服务端使用的压缩方法反向解压才能得到完整的数据。因此,客户端需要通过Accept-Encoding
字段告知服务端自身支持哪些压缩类型。
除了以上几个值,该字段还可以设置identity
,表示未经压缩的内容,设置为该值时客户端将不对接收到的数据进行处理。而*
可以匹配未被列出的其他压缩方法,这表示客户端对压缩方法没有任何偏好。
客户端可以列举自身支持的所有压缩方法,以逗号隔开,也可以设置权重,表示各个压缩方法的优先级,如:
Accept-Encoding: gzip;q=1.0, deflate;q=0.8, *;q=0.1
客户端期望的页面语言。
一般来说,中文网页设置的Accept-Language
为zh
或zh-CN
,即“中文”,zh-CN
指的是标准简体中文。中文除了简体中文外,还包括zh-HK
:中文(香港)、zh-TW
:中文(台湾),即繁体字、zh-MO
:中文(澳门)等。中文网页的该字段的常见值为:
Accept-Language: zh-CN,zh;q=0.9
即优先使用简体中文,否则只要是中文均可。
如果是英文网页,则该字段的值一般为en
。实际上英文也分很多方言,完整的语言代码请参考
语言代码表。
服务端通过响应头的Accept-Ranges
字段标识自身支持范围请求,即可以从资源的特定位置开始上传或下载,常用于断点续传。
Accept-Ranges
支持两个值:bytes
和none
。bytes
表示服务端支持范围请求。如客户端正在下载一个很大的资源文件,但由于某些原因发生了中断;下次继续下载时,如果客户端检测到了Accept-Ranges
的值为bytes
,则客户端可以从上次中断的位置继续下载。而如果该字段的值为none
,那么客户端就必须从头下载该资源。该字段的默认值就是none
,一般不需要设置,但在IE9中,设置为none
可以禁用或移除下载管理器的暂停按钮。
注意,该字段只用于标识服务端是否支持范围请求,具体需要请求哪些范围的数据,需要通过另一个头字段:Ranges
来确定。
以Access-Control开头的消息头是由CORS(Cross-Origin Resource Sharing,跨域资源共享策略)所定义的,用于规定一个接口的跨域共享机制。主要包括以下几个值:
GET, POST, PUT
。GET
、POST
或DELETE
等。预检请求本身所用的方法总是OPTIONS。这里我们着重讲解一下Access-Control-Allow-Credentials
和Access-Control-Allow-Origin
。
Access-Control-Allow-Credentials: 用于约定在进行跨域请求时客户端是否可以携带凭证(一般指Cookie),该字段由服务端来设置。
一般来说,客户端在发起跨域请求时默认不会携带Cookie信息。而当Access-Control-Allow-Credentials
的值为true
时,客户端就可以携带Cookie,此时客户端在发送ajax请求时,需要把withCredentials
设置为true
,如:
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
当服务端不允许发送凭证,而客户端想要通过上述代码传输cookie时,请求就会报错。
Access-Control-Allow-Origin:约定服务端可以响应哪些域的跨域请求。
根据浏览器的跨域限制,不同域之间不能发起ajax请求,但这对日常开发带来了很大的限制。为此,W3C制定了跨域共享策略,该策略允许服务端指定哪些域可以跨域请求该接口,Access-Control-Allow-Origin
是该策略中必须要设置的字段,否则当前接口将不能支持任何跨域请求。
比如服务端设置了以下值:
'Access-Control-Allow-Origin: http://10.10.10.10'
那么http://10.10.10.10
这个域就是受信任的,可以发起跨域请求。
如果该字段的值被设置为*
,则表示该接口接受来自任意域的跨域请求。
表示当前资源在客户端已被缓存的时长,单位秒。
该值为0,表示该资源刚刚从服务端获取到。该值通常会被用来与Max-Age
字段作比较,如果Age
的值大于Max-Age
,则表示该资源已失效,客户端将重新从服务端请求该资源。
当服务端返回状态码405(Methods Not Allowed)
时,需要通过该字段告知客户端所支持的请求方法。
如某个接口服务端只支持POST
方法访问,而客户端是通过GET
方法请求的,那么服务端除了返回状态码405
告知客户端当前方法不被支持外,还需要通过设置Allow: POST
列举该接口支持的访问方式。注意,一个接口可能允许多种访问方法。
客户端发起请求时携带的凭证信息。
通常来说,该字段是当服务端返回状态码401(Unauthorized)
时,客户端额外添加的。服务端返回401
意味着当前用户未授权,客户端收到该状态码后,需要通过该字段上传自己的凭证信息,重新发起请求。
该字段的语法为:
Authorization: <type> <credentials>
type是凭证类型,一般是Basic
,即基本类型。此外还有IANA
或AWS
等专用的凭证类型。当type为Basic
时,凭证值一般是Base64.encode(username + ':' + password)
,即将用户名和密码用冒号隔开,再用Base64编码后的结果。服务端可通过该值验证当前用户。如:
Authorization: Basic emhhbmdzYW46MTIz
// Base64.decode('emhhbmdzYW46MTIz') === 'zhangsan:123'
资源缓存策略。
可用于请求头中的值包括:
no-cache
:数据内容可以被任意类型的缓存存储,但是每次使用前必须向源服务器进行验证。no-store
:数据内容不能被任何缓存存储,相当于Max-Age=0
。no-transform
:禁止代理缓存对数据进行处理,包括Content-Encoding, Content-Range或Content-Type等,这可以禁止浏览器的某些特性,如Chrome中的Web Light
为减小图片的存储体积而压缩图片的行为。max-age
:最大生存时间,单位是秒。不同于Expires,max-age是相对于该数据被缓存的初始时间计算的,这可以抵御客户端和服务端时间不同步。max-stale
:表明客户端可以接受过期多久的数据,如果被缓存的数据超过max-age的时间在max-stale的范围之内,那么客户端将认为它仍然可用。min-fresh
:表明客户端希望所请求的数据在多少秒内必须是最新的,例如min-fresh=60
,表示客户端希望服务端的该资源在60秒内不应该变化。可用于响应头的值包括:
must-revalidate
:它表示一旦一个资源过期,那么客户端不能在未经向服务端验证的情况下使用它。no-cache
:同上。no-store
:同上。no-transform
:同上。public
:响应体可以被任何类型的缓存存储。private
:响应体只能被浏览器缓存存储,不接受其他缓存类型。proxy-revalidate
:类似于must-revalidate
,但是只作用于共享缓存,对私有缓存无效。max-age
:同上。s-maxage
:在共享缓存(如proxies)中可以覆盖max-age和expires的值。扩展值包括:
immutable
:服务端向客户端返回该值表示该资源永远不会变化,因此客户端不需要向服务端发送类似If-Modified-Since
的信息来验证资源的有效性。stale-while-revalidate
:它表示客户端可以接受过期时间在一定范围内的缓存资源,不同于max-stale
,它会异步地验证该资源的有效性。换句话说,当过期时间在某个范围内时,客户端会先临时使用该资源,再去验证它的有效性。stale-if-error
:如果某个资源过期的时间没有超过这里设置的值,并且向服务端验证有效性的请求出错了,那么客户端将使用这个缓存资源。设置当请求完成之后,连接是否需要关闭。
它接受两个值:keep-alive
和close
。当值为keep-alive
时,请求完成时连接暂时不会关闭,而是等待后续的其他请求继续进行。当值为close
时,一旦请求完成,连接就会关闭,这是http/1.1的默认行为。此时如果需要发送其他请求,必须重新建立连接。保持http连接会占用服务端的连接池资源,因此如果不是连续的多个请求,应该及时关闭连接。
数据实体的媒体类型,它指示客户端该以何种格式来解码数据实体。
该字段与客户端请求头中携带的Accept-Encoding
是对应的,比如客户端发送请求时带了如下请求头:
Accept-Encoding: gzip, deflate
这表示客户端支持gzip和deflate两种数据压缩格式,此时服务端给出如下响应头:
Content-Encoding: gzip
它表示服务端当前是用gzip压缩数据实体的,因此客户端需要用gzip来解压数据。它的取值与Accept-Encoding
是对应的。
指明当前页面的语言类型。
它与客户端设置的Accept-Language
也是对应的,但它主要是为了指明当前文档的目标用户,如中文用户,并不能保证文档的所有内容均为中文内容。
以字节计数的实体数据的长度。
较常见于响应头,不过请求头中也可以携带该参数,如果服务端强制要求客户端发送某个请求时携带该参数,而客户端没有携带,客户端就会收到一个411(Length Required)
的报错。
指示当前请求的目标资源的位置。
比如当前请求的url为https://example.com/documents/foo
,并且Accept
类型为text/html
,那么这实际上是在请求一个静态页面。所以服务端就可以在响应头中设置:
Content-Location: /documents/foo.html
客户端就可以根据这个url直接请求该静态页面。
一般来说,静态资源是由web容器直接负责响应的,而web容器是根据url所请求的资源后缀(如.html
)来识别静态资源的,所以它无法拦截/documents/foo
这样的url。这时就需要服务端返回一个新的url,来指示该静态资源的位置。
该字段指明了当前内容在完整数据响应中的范围,通常以字节表示。
比如客户端需要从服务端分段下载一个很大的资源文件,假设资源总字节数为67589个字节,当前下载的是200 - 1000这个范围的数据,那么该字段的值为:
Content-Range: bytes 200-1000/67589
bytes(字节)是计量单位。如果不需要指明某部分的值,可以用星号代替,如:
Content-Range: bytes 200-1000/67589
Content-Range: bytes 200-1000/*
Content-Range: bytes */67589
内容安全策略字段,服务端通过该字段控制用户代理可以加载哪些资源,主要用于防止跨站点脚本攻击(XSS)。
该字段可以包含多个策略,各个策略由分号隔开,用于控制不同类型资源的加载策略。比如script-src
可以限制静态脚本的来源,而img-src
则可以限制页面内图片的来源,下面是该字段支持的策略类型:
child-src
,设置内嵌页面(iframe)和web workers的合法源地址。connect-src
,设置页面可以通过脚本接口访问的url,如ajax请求。default-src
,为其他策略提供一个默认值,当某个策略没有设置时,将自动启用这里设置的值。font-src
,设置@font-face加载的字体文件的合法源地址。frame-src
,设置内嵌页面,如iframe的合法源地址。img-src
,设置图片的合法源地址。manifest-src
,设置应用描述文件的合法源地址。object-src
,设置像object、embed、applet等内嵌标签的合法源地址。prefetch-src
,预加载或预渲染的资源的合法源地址。script-src
,脚本文件的合法源地址。style-src
,样式文件的合法源地址。worker-src
,限制各类worker的合法源地址。base-uri
,限制文档的plugin-types
,限制文档可以支持的插件类型。sandbox
,为请求的资源启用沙盒。form-action
,设置表单的action属性的合法地址。frame-ancestors
,指定了当前页面作为内嵌页面时,其父页面的合法地址。比如设置该值为Content-Security-Policy: frame-ancestors https://example.com;,那么就只有http://example.com这个域下的页面才可以把当前页面作为iframe或其他标签嵌入,否则会受到内容安全策略的限制而无法加载。block-all-mixed-content
,阻止在https页面内加载http类资源。require-sri-for
,设置文档的脚本或样式必须符合SRI策略,该策略用于验证资源文件的完整性和安全性。upgrade-insecure-requests
,将页面内所有不安全的http请求自动替换为安全的https请求。比如我们正在将站点从http迁移到https,但页面内包含了大量以http开头的请求,那么当设置了该策略后,页面内的请求在发送前会自动替换为https,这可以用于有大量请求地址需要修改的旧版网站。比如我们需要限制当前页面内的请求必须全部来自https站点,并且脚本必须来自于当前域或https://example.com域,可以这样写:
Content-Security-Policy:
connect-src https:;
script-src 'self' https://example.com/
'self’关键字指代当前站点,而设置为’none’则可以禁止某类资源的加载。
指示服务端返回的MIME类型,帮助客户端正确解析资源。
它所支持的类型与Accept字段是一致的,Accept描述客户端可以接受的资源类型,而Content-Type描述服务端实际返回的资源类型。
站点的Cookie,包含了一些简要的身份验证信息或其他重要信息。
各个Cookie由分号加一个空格隔开,是一个字符串,每个cookie可以单独设置有效期,但查看cookie时不会输出有效期,如:
Cookie: PHPSESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1
消息被发出去的日期,可用于请求头或响应头。该日期不一定是完整的,可以只携带年月日时分秒的部分信息。
资源的特定版本在服务端的标识符,可以理解为资源的版本id。
该字段服务于缓存策略。服务端的资源只要被编辑,服务器就会为该版本生成一个新的ETag值,这时与客户端旧资源的ETag就会不匹配,服务端需要返回最新的资源给客户端。相反,如果ETag是匹配的,那么就说明该资源未被编辑过,因此服务端可以返回状态码304,告知客户端使用缓存资源。
ETag的问题是,无论服务端资源发生任何改变,它的值都会变,因此所有客户端都需要重新下载该资源。设想一下,假如我们给cdn上的某个文件添加了几行注释,这几行注释并不会影响该文件的使用,却会导致所有引入该资源的缓存失效,这也是ETag使用频率有所下降的一个原因。
该字段用于请求头,包含了客户端的一个期待条件,表示只有服务端满足此期待条件时才会继续请求。
目前该字段只定义了一个值,就是100-continue
。比如客户端现在需要发送一个体积很大的文件到服务端,可以这样设置请求头:
PUT /somewhere/fun HTTP/1.1
Host: origin.example.com
Content-Type: video/h264
Content-Length: 1234567890987
Expect: 100-continue
这里只是一个预检请求,Content-Length描述了文件的字节数,Expect表示期望收到状态码100,以继续发送该文件。,服务端可以有以下两种应答:
100 continue
,即返回状态码100,表示同意客户端发送该文件。417 Expectation Failed
,服务端不允许发送该文件,此时请求失败。资源的有效日期,超过该日期,则资源被视为已失效。如:
Expires: Sat Nov 07 2020 23:00:00 GMT+0800 (中国标准时间)
表示该资源将在2020年11月7日晚十一点整失效。不过当客户端与服务端时间不同步时,使用该字段就会出现问题,因此现在主要是使用Max-Age来设置有效时间,由于使用的是相对值,所以它可以有效抵御时间不同步问题。
一个请求发起人的电子邮箱地址。
假如你在运行一个机器人代理程序(如爬虫程序),你可以在请求中携带From字段,这样当服务器收到一些异常的请求时,服务器的管理员可以通过这个邮箱地址联系到你。如:
From: [email protected]
指明了请求将要发送到的服务器的主机名和端口号,如果使用了默认的端口号,则端口号可以省略。如:
Host: developer.cdn.mozilla.net
该字段会携带一个或多个ETag,只有当服务端的资源与列表中的ETag匹配,才会执行操作。
对于GET和HEAD这类的安全操作,只有当服务端该资源的ETag与该字段的ETag匹配时,服务端才会返回该资源。而对于PUT这类的非安全操作,只有当ETag匹配时,才会对资源进行修改,否则服务端将返回 412 (Precondition Failed,先决条件失败)
响应。
该字段常有两类应用场景。
416 (Range Not Satisfiable,范围请求无法满足)
响应。412 (Precondition Failed,先决条件失败)
响应。如果目标资源在该字段指定的时间之后发生过修改,则返回该资源,否则返回304。例:
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
该字段用于缓存机制,它的值一般是缓存中的该资源上次被修改的时间。客户端通过该字段询问服务端该资源在该时间后有没有发生过修改,如果发生了修改,说明缓存中的资源已经不是最新的,服务端需要返回最新的资源;否则返回304,这时客户端会从缓存中取该资源使用。
该字段只能用于GET和HEAD,其他类型的请求不支持,如果同时还设置了 If-None-Match
,则If-Modified-Since
会被忽略。
与If-Match相反,只有服务端资源的ETag与该字段不匹配,才会执行请求。
该字段的两种常用场景为:
设置范围下载的先决条件,即在什么情况下才可以启用范围下载,它可以决定Range字段在何种条件下起作用。
当该字段的条件得到满足时,服务端就可以返回状态码206(Partial Content),并按照Range字段所规定的范围,返回该范围内的数据。如果该字段未得到满足,则服务端会返回状态码200,并返回完整的资源,此时相当于范围下载失效。
If-Range可以携带两类值,一类是资源上次被修改的时间(Last-Modified),一类是资源的ETag,但不能同时携带两个值。携带修改时间时,只有该时间之后资源没有发生变化,才算满足该条件;携带ETag时,需要与服务端该资源的ETag匹配,才算满足条件。本质上都是为了保证断点续传的每一段数据都来自同一个资源。
与If-Modified-Since正好相反,只有资源在该字段指定的时间之后未修改,才算满足条件。
该字段有两种常见场景:
用于响应头,服务端通过该字段告诉客户端该资源上次被修改的时间,从而为If-Modified-Since和If-Unmodified-Since这两个字段提供依据。
相当于文档内的link标签,但它是http级别的,一般用于设置预加载项或预链接。
我们在HTML页面内可能会编写如下代码来预加载一个脚本文件:
<link rel="preload" href="./main.js" as="script">
它可以在http头中以以下的方式定义:
Link: <./main.js>; rel="preload"; as="script"
如果需要定义多个link,用逗号隔开即可。HTML中的link标签可以定义很多预加载、预链接、预获取等行为,但相当于是静态定义,而http中的Link头则属于动态定义。
指定重定向地址,一般用于响应头状态码为3xx时的重定向,另外,状态码为201时也会带Location字段,它指示创建的资源的地址。
Location
与Content-Location
不同,Location
携带的是一个重定向的url地址(或新创建资源的地址),理论上不能保证还会不会发生新的重定向;而Content-Location
携带的是经过内容协商后的实际资源地址,不会再进行进一步的内容协商。前者相当于把请求交给了其他处理者,而后者直接返回了数据实体的地址。
以十进制表示的请求可转发的最大服务器数目,每台服务器在进行转发时将该值减一,当值变为0时,该服务器将不能再向其他服务器转发。以下示意图来自网络:
当设置Max-Forwards为0,则该请求必须由接收到该请求的第一台服务器处理,不允许向其他服务器转发;当值为1时,只允许被转发一次,以此类推。
通常来说我们不会设置这个值,但是设想如下场景:假如我们的服务经过了多级服务器做代理,现在某个请求出现了异常,而我们很难判定到底是哪一级服务出了错,我们就可以依次把Max-Forwords设置为0,1,2 …,由于未出现异常的服务器不会抛出错误,所以我们可以定位出抛出异常的那一级服务器。
该字段用于约定内容协商策略。
所谓内容协商,指的是当服务端的一个资源可能有多种表现形式(如文档的不同语言类型,图片的不同格式等)时,如何根据客户端需求得到指定的形式。比如现在服务端有一个HTML文件,它提供三种语言版本,我们希望这三个语言版本共享同一个URL,于是当用户请求这个URL时,我们可能需要根据请求中所携带的Accept-Language字段来决定最终返回哪个语言版本的文档。这个过程就是透明的内容协商,即客户端不需要知道具体的协商过程。不过客户端可以通过Negotiate
字段设定内容协商策略,它包含以下几个值:
trans
,即transparent,客户端支持任何透明的协商策略,此时内容协商过程由服务端决定。vlist
,客户端可以接受服务端返回的响应头中的列出的任何一种形式,协商策略暗指trans
。guess-small
,客户端允许源服务器使用可能得到该资源最佳变体的常规算法,暗指使用trans
和vlist
。rvsa-version
,客户端允许源服务器运行指定版本(或同一主版本的更高级子版本)的rvsa(remote variant selection algorithm,远程变体选择算法),并返回一个最佳变体。*
,客户端允许服务端运行任何版本的rvsa算法,甚至包括非标准化算法,并返回一个最佳变体。该字段携带的参数如果无法被服务端理解,它将被忽略。
发起跨域请求的页面所在的源服务器,对于任何有访问权限限制的请求,Origin字段都是必需的。
比如我们在http://example.com/index.html
这个页面发起了一个跨域请求,那么这个请求里必须携带以下字段:
Origin: http://example.com
它指示当前页面所在的域,服务端根据这个字段判断当前用户代理是否有对该接口的访问权限。
http/1.0中定义的控制缓存行为的字段。在请求头中,Pragma: no-cache
与Cache-Controll: no-cache
的含义是一致的,而后者是在http/1.1中定义的。在响应头中,Pragma字段没有明确的行为,因此不建议使用。
在仅支持http/1.0的站点中,可以使用Pragma: no-cache
来代替Cache-Controll: no-cache
,并且只能用在请求头中。其他情况下,应该使用http/1.1中定义的Cache-Controll
。
指定了获取代理服务器上资源访问权限所需采用的身份验证方式,用于响应头中。
Proxy-Authenticate
首部需要与407 Proxy Authentication Required
响应一起发送,即当代理服务器需要进行权限认证,而客户端未携带凭证时,代理服务器通过该字段告知客户端所需的凭证类型。语法一般如下:
Proxy-Authenticate: Basic realm="Access to the internal site"
该字段第一个参数为凭证类型,多为’Basic’,第二个参数是对被保护域(也叫安全域,即需要权限才能访问的域)的描述。
指定范围响应的字节范围。
当客户端需要请求服务端一个很大的资源文件时,可以每次只请求其中一部分,这样可以实现断点续传,也可以实现并行下载。Range字段就是指定要下载的字节范围。
范围下载一般需要进行先决条件判断,如If-Match
、If-Range
等,满足判断条件后,服务端会返回响应码207(Partial Content)
,并携带指定范围内的数据。先决条件验证失败的情况下,如果是范围无效导致的,则会返回416(Range Not Satisfiable)
;如果是因为资源发生了修改,或服务端不支持范围请求,则可以返回200(OK)
,并携带完整的资源文件。
该字段包含三个关键值:unit(单位)、range-start(起始字节)、range-end(结束字节)。unit一般是’bytes’,结束字节可省略,此时表示一直到文件末尾。Range字段可以携带由逗号隔开的多个值,表示一次请求多个范围的内容,每部分内容是multipart类型。如:
Range: bytes=200-1000, 2000-6500, 8000-
当前页面被链接的前一页面的绝对路径或相对路径。
比如某个页面是从百度首页跳转过来的,那么它的Referer就是百度首页的地址。Referer实际上应该写作Referrer
,这是http/0.9在制定规范时的拼写错误导致的,出于向后兼容的需要,这个错误被保留了下来。
在两种情况下Referer字段不会被保留:
由于Referer字段可能被用来监控用户浏览历史,所以可以通过给链接设置rel="noreferer"
来禁止跳转页面时携带当前页面的地址,另外还可以通过meta标签或设置请求头来设定该行为。
Referer只会包含url中的路径部分(页面的完整路径),不会包含URL fragments(如hash地址或查询参数等),如当前页面由https://www.baidu.com/?title=1
跳转而来,则referer的值为:
Referer: https://www.baidu.com/
用于响应头中,服务端通过该字段告知客户端需等待多久后才能重新访问服务器。
该字段经常与状态码503 (Service Unavailable,服务不可用)
或重定向类响应联合使用。503一般是在服务进行升级或维修时,临时给出的响应码。通过设置Retry-After
字段,管理员可以告知客户端服务大概多久可以恢复。当遇到重定向类响应时,该字段可以指定客户端至少多久后才能访问重定向的url。
该字段支持两类值,一类是Date类型,一类是延迟的毫数。如:
Retry-After: Mon Nov 09 2020 23:00:00 GMT+0800 (中国标准时间)
Retry-After: 120
描述了服务器所使用的web容器的类型,如Server: Nginx
或Server: Apache/2.4.1
。
一般来说,不应该将Server描述得过于详细,否则攻击者会更容易根据该Server的已知漏洞对站点发起攻击。
服务端通过该字段向客户端设置Cookie。
该字段可以一次设置多个cookie,每个cookie用逗号隔开;单个cookie可以设置多个参数,每个参数之间用分号隔开。如:
Set-Cookie: id=a3fWa, sessionid=38afes7a8; HttpOnly; Path=/
这里共设置两个cookie:id和sessionid。Set-Cookie支持的所有参数如下:
Expires=
,cookie的失效日期,类似于消息头中的Expires。Max-Age=
,cookie的最大生效时长,单位秒,优先级高于Expires。将该参数设置为0或-1,会导致该cookie立即失效。Domain=
,指定cookie可以送达的主机名。如果没有指定,默认为当前页所在的主域(不包含子域)。Path=
,指定一个URL路径,只有这个路径出现在要请求的资源路径中时,才会发送该cookie。如设置Set-Cookie: id=111; Path=/docs
,那么当访问/docs/WEB/index.html
时,这个cookie会被携带,而访问/main/index.html
时则不会发送该cookie。Secure
,当设置了该属性时,当前cookie只能在使用了SSL和https协议时才可以被发送。HttpOnly
,设置了该属性时,该cookie只能用于http传输,不能被JavaScript直接修改。SameSite=Strict
,限定某一个cookie不能随跨域请求一起发送,这可以在一定程度上防范跨站请求伪造攻击。严格限定客户端只能使用https来访问服务器。客户端使用http访问服务器会被忽略。
当客户端第一次以https请求服务器时,服务器就可以设置该值,随后客户端会根据这个参数,把所发起的任何http请求都默认设置成https。
请求方希望使用的传输编码类型。
不同于Accept-Encoding
,TE
字段描述的是单次数据传输时期望的编码类型,而Accept-Encoding描述的是最终送达客户端时的编码类型,因此TE
也可以非正式地称为Accept-Transfer-Encoding
。
TE
字段支持的指令包括:compress
、deflate
、gzip
、trailers
,当存在多个值时,可以使用q参数定义权重。前三个都是常规的数据压缩方法,这里不再详述;trailers
表示客户端期望在采用分块传输编码的响应中添加一些额外的接收挂载字段。
列出将在消息正文之后在尾部块中添加的额外元信息,通常是一些临时参数,如消息的完整性校验、消息的数字签名等。
如果需要在响应中使用Trailer字段,则请求中的TE
字段必须设置为TE: trailer
。如:
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
Trailer: Expires
... // 数据实体
Expires: Wed, 21 Oct 2015 07:28:00 GMT\r\n
\r\n
指明了消息实体所使用的编码格式,不同于Content-Encoding,Transfer-Encoding
面向的是两个节点之间的消息传递。
例如请求在转发的过程中可能经过了很多的代理服务器,一般客户端只会限制最终返回数据的编码格式,而数据在各个服务器之间以何种格式编码并不重要,所以客户端收到的响应头中常用的是Content-Encoding
。而对于处在中间的代理服务器来说,它只需要关注前一级服务器期望的编码类型,并用Transfer-Encoding
响应头告知前一级服务器当前使用的编码格式即可。
如果以TCP/IP协议的层级作类比,那么Content-Encoding相当于TCP级别的,面向的是最终用户;而Transfer-Encoding相当于是IP级别的,面向的是各个中间设备。这种设计使得系统中的异构设备可以共同工作,对系统的扩展是极为有利的。
用于检测是否有其他版本的协议可用于通信,一般用来请求切换协议。
比如客户端发起了如下请求:
Upgrade: TLS/1.0; Connection: Upgrade
这表示客户端请求切换到TLS/1.0协议。由于该请求默认只作用于客户端和邻接服务器之间,所以需要设置Connection: Upgrade
来作用到更高级的服务器。如果服务端可以切换到指定的协议,就会返回状态码101(Switching Protocols)
,并携带如下响应头:
Upgrade: TLS/1.0, HTTP/1.1; Connection:Upgrade
这表示服务端正在切换到协议TLS/1.0, HTTP/1.1
。
用户代理字符串,用来指明客户端所使用的用户代理(一般指浏览器)的描述参数。
比如以下是编写该文档时所使用的的谷歌浏览器的用户代理字符串:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36
可以看到,这里包含了非常多的版本描述,出现这种情况的原因还要追溯到第一次浏览器大战。
最初添加用户代理字符串的原因是,当时Netscape
公司的网景浏览器与IE
相比,功能更为强大,在Netscape上能显示的很多效果在IE中不能显示。所以开发者需要根据用户所用的浏览器类型,来决定是否启用某些功能,甚至直接使用另一组页面。于是浏览器开发商就添加了用户代理字符串,来提供这一信息。
但是后来IE项目组发现,尽管他们对IE进行了功能增强,但是很多线上网站仍然没有启用这些功能。原因是开发者是根据用户代理字符串中是否包含Mozilla
(Netscape的浏览器项目组叫Mozilla,当时该关键字是网景浏览器的标志,如今可以用来作为火狐浏览器的标志)关键字来决定功能启用与否的。为了摆脱这个困境,IE私自在自己的User-Agent中加入了Mozilla字样,以表明自身已经具备了Netscape某个版本的所有功能。这种传统后来被延续了下来,所以用户代理字符串被描述得越来越复杂。
如何根据User-Agent来判断浏览器类型,网上有成熟的判断方法,这里不再详述。一般来说,开发者不应该对浏览器做类型检查,更推荐的做法是进行功能性检查,它比浏览器类型检查更可靠。比如对addEventListener
的检查:
if (window.addEventListener) {
... }
else {
... }
它列举了服务端在进行内容协商时用到的头字段清单,以此来通知缓存服务器自身的决策依据,多个字段用逗号隔开。这可以帮助缓存服务器复现决策过程,以免将错误的缓存数据返回给客户端。
举个例子,下面是某个页面在请求js文件时,响应头中携带的Vary字段:
Vary: Accept-Encoding
这意味着,服务端在进行内容协商时只用到了请求头中的Accept-Encoding
字段。这样当缓存服务器对缓存的数据进行有效性检查时,只需要通过这个字段的值,就可以复现决策过程,从而验证当前缓存是否有效,因此避免了向服务端发起验证。为了使缓存服务器准确高效地工作,这个字段是必要的。
Vary的值设置为'*'
,意味着服务端使用了未在首部中传递的信息来进行决策,这样缓存服务器就不得不重新向服务端发请求才能验证缓存的有效性,这对缓存的效率影响很大,因此一般不建议设置为该值。
该字段是由代理服务器添加的一个通用头部,适用于正向代理和反向代理,可以用于追踪消息转发情况、防止循环请求、识别消息发送者对协议的支持能力等。
该字段的语法如下:
Via: [ "/" ] [ ":" ]
或
Via: [ "/" ]
protocol-name
是协议类型,默认是HTTP;protocol-version
是协议版本,如1.0或1.1等;host
是主机名,也可以是一个url;port
是端口号,可省略;pseudonym
是内部代理的名称或别名。如下面的三个例子:
Via: 1.1 vegur
Via: HTTP/1.1 GWA
Via: 1.0 fred, 1.1 p.example.net
第一个例子表示该请求刚从名为vegur
的内部代理服务器转发而来,使用的协议是HTTP/1.1
;第二个类似;第三个表示它先经由名为fred
,使用HTTP/1.0的代理服务器转发之后,再由地址为p.example.net
,使用HTTP/1.1的代理服务器转发而来。
给出一组警告信息,每个警告信息包含:warn-code
、warn-agent
、warn-text
和可选的warn-date
。
Warning: 110 anderson/1.3.37 "Response is stale"
Warning: 112 - "cache down" "Wed, 21 Oct 2020 07:28:00 GMT"
在http headers列表中还有很多以X-
开头的字段,这些字段一般是试验性字段或自定义字段。试验性字段可能会在后续版本中去除X-
前缀,变成正式字段。
本文罗列的是MDN中绝大部分的headers字段,并结合网上的一些资料对一些讲解不够清楚的字段加以阐释。由于headers中的字段较多,如果本文有遗漏的,可以参考IANA 注册表,里面可以查到所有headers的官方RFC文档,可以作深入学习用。