HTTP报文
请求报文/响应报文
结构: 报文首部 + (可选)报文主体(两者通过空行CR + LF
来划分)
使用首部字段是为了给浏览器和服务器提供报文主体大小、所使用的语言、认证信息等内容
HTTP首部字段重复,这种情况在规范内尚未明确,根据浏览器内部处理逻辑的而不同,结果可能并不一致。
报文首部
- 请求行/状态行
- 请求行: 请求方法 + 请求
URI
(资源/CGI
通用网关接口) +HTTP
版本 - 状态行:
HTTP
版本 + 状态码 + 原因短语 - 首部字段(请求/响应首部字段 + 通用首部字段 + 实体首部字段)
- 其他(在
HTTP
协议通信交互中使用到的首部字段,不限于RFC2616中定义的47种首部字段。包括Cookie
、Set-Cookie
和Content-Disposition
等在其他RFC
中定义的首部字段)
报文主体
HTTP
传输数据可按照数据原貌直接传输,也可以在传输过程中通过编码提升传输速率。
通常,报文主体等于实体主体。只有当传输中进行编码操作时,实体主体的内容发生变化,才导致它和报文主体产生差异。
报文主体和实体主体的差异
-
报文(
message
)是HTTP通信中的基本单位,由8位组字节流组成,通过HTTP通信传输
-
实体(
entity
)作为请求或响应的有效何在数据(补充项)被传输,其内容由实体首部和实体主体组成
内容编码
内容编码指明应用在实体内容上的编码格式,并保持实体信息原样压缩。内容编码后的实体由客户端接收并负责解码
内容编码的方式
-
gzip(GNU zip)
由文件压缩程序 gzip(GNU zip)生成的编码格式(RFC1952),采用 Lempel-Ziv 算法(LZ77)及 32 位循环冗余校验(Cyclic Redundancy Check,通称 CRC)。
nodejs中
var zlib = require('zlib')
vra gunzip = zlib.createGunzip() -
compress(UNIX系统的标准压缩)
由 UNIX 文件压缩程序 compress 生成的编码格式,采用 Lempel-Ziv-Welch 算法(LZW)。
-
deflate(zlib)
组合使用 zlib 格式(RFC1950)及由 deflate 压缩算法(RFC1951)生成的编码格式。
-
identity(不进行编码)
不执行压缩或不会变化的默认编码格式
分块传输编码
HTTP
通信过程中请求的编码实体资源尚未全部传输完成之前,浏览器无法显示请求页面。在传输大容量数据时,通过把数据分割成多块,能够让浏览器逐步显示页面。
简而言之,分块传输编码就是起到把实体主体分块的功能(Chunked Transfer Coding
); 区别于更微观层面,位于传输层的TCP
协议会将HTTP
请求报文分割成报文段,将每个报文段可靠地传输给对方。
发送多种数据的多部分对象合集
发送邮件时,可以在邮件里写入文字并添加多份附件。这是因为采用了MIME
(Multipurpose Internet Mail Extensions
, 多用途因特网邮件扩展)机制,它允许邮件处理文本、图片、视频等多个不同类型的数据。
例如,图片等二进制数据以ASCII码字符串编码的方式来指明,就是利用MIME来描述标记数据类型。而在MIME扩展中会使用一种称为多部分对象集合(Multipart
)的方法,来容纳多份不同类型的数据。
相应地,HTTP协议也采纳了多部分对象集合,发送的一份报文主体内可包含多类型实体。通常在图片或文本上传时使用。
多部分对象集合包含的对象如下
-
multipart/form-date
表单上传
-
multipart/byteranges
状态码206(
Partial Content
,部分内容)响应报文包含了多个范围的内容时使用(对于多重范围的范围请求的响应)
在HTTP报文中使用多部分对象集合时,需要在首部字段里加上特定的Content-type
范围请求
为了实现网络中断,从中断处恢复下载,我们需要指定下载的实体范围
指定范围发送的请求叫做范围请求(Range Request
)
对一份10,000字节大小的资源,如果使用功能范围请求,那么可以只请求5,001~10,000
例如,请求二分之一图片
GET /tip.jpg HTTP/1.1
Host: xxx
Range: bytes = 5001-10000
HTTP/1.1 206 Partial Content
Date: ...
Content-Range: bytes 5001-10000/10000
Content-Length: 5000
Content-Type: image/jpeg
针对范围请求,响应会返回状态码为206Partial Content
的响应报文。
多重范围的范围请求
Range: bytes=-3000, 5000-7000
对于多重范围的范围请求,响应会在 首部字段Content-Type
表明multipart/byteranges
后返回响应报文
如果服务端无法响应范围请求,则会返回状态码200 OK和完整的实体内容
内容协商
用于返回合适语言版本的web页面
包含在请求报文中的某些首部字段,可用于内容协商的判断基准
- Accept
- Accept-Charset
- Accept-Encoding
- Accept-Language
- Content-Language
内容协商的技术有
- 服务器驱动协商
- 客户端驱动协商
- 透明协商
请求首部字段(Request Header Fields)
从客户端向服务器端发送请求报文时使用的首部。补充了请求的附加内容、客户端信息、响应内容相关优先级等信息。
Accept
用户代理可处理的媒体类型
Accept: text/html,application/xhtml+xml,application/xml;q=
当服务器提供多种内容时,将会首先返回权重值最高的媒体类型。
Accept-Charset
该首部字段可用来通知服务器用户代理支持的字符集及字符集的相对优先顺序。(应用于内容协商机制的服务器驱动协商。)
Accept-Charset: iso-8859-5, unicode-1-1;q=0.8
Accept-Encoding
优先的内容编码
- gzip
- compress
- deflate
- identity(不执行压缩或不会变化的默认编码格式)
- *
Accept-Language
优先的语言(自然语言)
上述请求字段用于内容协商的判断基准
Accept-Language: zh-cn,zh;q=0.7,en-us,en;q=0.3
客户端在服务器有中文版资源的情况下,会请求其返回中文版对应的响应,没有中文版时,则请求返回英文版响应。
Authorization
Web认证信息
请求资源 => 服务端返回401 Unauthorized => 客户端发送带有认证信息的请求
GET /index.html
Authorization: Basic dWVub3NlbjpwYXNzd29yZA==
通常,想要通过服务器认证的用户代理会在接收到返回的401 状态码响应后,把首部字段 Authorization 加入请求中。
Host
请求资源所在服务器
虚拟主机运行在同一个IP
上,可使用首部字段Host
加以区分
Host: www.hackr.jp
// 同一服务器上有多个虚拟主机(多个域名)
虚拟主机一:www.hackr.jp
虚拟主机二:www.usagidesign.jp
首部字段 Host
和以单台服务器分配多个域名的虚拟主机的工作机制有很密切的关联,这是首部字段 Host
必须存在的意义。
请求被发送至服务器时,请求中的主机名会用 IP 地址直接替换解决。但如果这时,相同的 IP 地址下部署运行着多个域名,那么服务器就会无法理解究竟是哪个域名对应的请求。因此,就需要使用首部字段 Host
来明确指出请求的主机名。若服务器未设定主机名,那直接发送一个空值即可
If-Match
比较实体标记(ETag)
If-xxx 这种形式的请求首部字段,都可成为条件请求。服务器接收到附带条件的请求后,只有判断指定条件为真时,才会执行请求
// 客户端
GET /index.html HTTP/1.1
If-Match: '123456'
...
// 服务端
index.html 的实体标记(ETag):123456
对比请求的If-Match与服务器端资源的ETag,发现匹配,所以执行请求并返回 200 OK
// 客户端
GET /index.html HTTP/1.1
If-Match: '123456'
...
// 服务端
index.html 的实体标记(ETag):567890
对比请求的If-Match与服务器端资源的ETag,发现不匹配,则返回状态码 412 Precondition Failed响应
还可以使用星号(*)指定 If-Match
的字段值。针对这种情况,服务器将会忽略 ETag
的值,只要资源存在就处理请求。
If-None-Match
比较实体标记(与 If-Match 相反)
只有在If-None-Match
的字段值与ETag
值不一致时,可处理该请求
如果资源不存在,那么对于的ETag
也就不存在了,这样也可以处理该请求
在 GET 或 HEAD 方法中使用首部字段 If-None-Match
可获取最新的资源。因此,这与使用首部字段 If-Modified-Since
时有些类似。
Etag
主要是为了解决last-modified
(仅依据时间来判断是否过期)无法解决的问题
1、一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET; // 304 Not Modified
2、某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since
能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒)
3、某些服务器不能精确的得到文件的最后修改时间;
If-Modified-Since
比较资源的更新时间
// 客户端
GET /index.html HTTP/1.1
If-Modified-Since: Thu, 15 Apr 2004 00:00:00 GMT
// 服务端
发现资源在2004年4月15日之后更新过,所以接受并执行请求;并返回最新资源,及资源的标识信息
HTTP/1.1 200 OK
Last-Modified: Sun, 29 Aug 2004 14:03:05 GMT
// 客户端
GET /index.html HTTP/1.1
If-Modified-Since: Thu, 15 Apr 2004 00:00:00 GMT
// 服务端
发现资源在2004年4月15日之后未更新过,所以直接返回304 Not Modified
HTTP/1.1 304 Not Modified
If-Unmodified-Since
比较资源的更新时间(与If-Modified-Since相反)
If-Range
资源未更新时发送实体 Byte 的范围请求(中断后恢复下载需要用到,判断资源是否更新,未更新则继续下载,如果更新了那么重头重新下载)
HTTP 请求头字段用来使得 Range 头字段在一定条件下起作用:当字段值中的条件得到满足时,Range 头字段才会起作用
If-Range
字段值中既可以用 Last-Modified
时间值用作验证,也可以用ETag
标记作为验证,但不能将两者同时使用。
匹配一致
// 客户端
GET /index.html HTTP/1.1
If-Range: '123456'
Range: bytes=5001-10000
...
// 服务端
比较请求首部字段If-Range与服务端资源的Etag,发现两者匹配,则执行请求并返回范围请求的响应内容
HTTP/1.1 206 Partial Content
Content-Range: bytes 5001-10000/10000
Content-Length: 5000
...
匹配不一致
// 客户端
GET /index.html HTTP/1.1
If-Range: '123456'
Range: bytes=5001-10000
...
// 服务端
比较请求首部字段If-Range与服务端资源的Etag,发现两者不匹配,则返回全体资源及资源标识(客户端会保存标识便于下次请求带上该标识)
HTTP 200 OK
ETag:"567890"
...
Max-Forwards
最大传输逐跳数
HTTP
使用TRACE
方法(目的是让Web服务器将之前的请求通信环回给客户端的方法)发送请求时,在Max-Forwords
首部字段中填入数值,每经过一个服务端就将该数字减1,当数值刚好减到0时,就停止继续传输,最后接收到请求的服务器端则返回状态码200OK的响应
客户端通过TRACE
方法可以查询发送出去的请求是怎样被加工修改/篡改的。这是因为,请求想要连接到源目标服务器可能会通过代理中转
但是,TRACE
方法本来就不常用。再加上它容易引发XST(Cross-Site Tracing, 跨站追踪)
攻击,通常就更不会用到了。
另外,由于代理服务器由于某些原因导致请求转发失败,客户端就等不到服务器返回的响应了;或者由于其他原因导致请求陷入循环(代理服务器A <=> 代理服务器B),客户端也就等不到服务器返回的响应了
Proxy-Authorization
代理服务器要求客户端的认证信息(客户端与代理服务器之间);客户端与服务器之间的认证,使用首部字段 Authorization 可起到相同作用
Range
实体的字节范围请求
Range: bytes=5001-10000
对于只需获取部分资源的范围请求,包含首部字段 Range 即可告知服务器资源的指定范围
配合If-Range
一起使用,服务端对此的响应,若满足条件,则处理请求并返回206 Partial Content;若不满足条件,则返回状态码200 OK
Referer
对请求中 URI 的原始获取方法;首部字段 Referer 会告知服务器请求的原始资源的 URI。
Referer:http://www.hackr.jp/index.html
客户端一般都会发送 Referer 首部字段给服务器。但当直接在浏览器的地址栏输入 URI,或出于安全性的考虑时,也可以不发送该首部字段。
因为原始资源的 URI 中的查询字符串可能含有 ID 和密码等保密信息,要是写进 Referer 转发给其他服务器,则有可能导致保密信息的泄露。
TE
传输编码的优先级
User-Agent
HTTP 客户端程序的信息;通常用于传达浏览器的种类
web端和移动端
由网络爬虫发起请求时,有可能会在字段内添加爬虫作者的电子邮件地址。此外,如果请求经过代理,那么中间也很可能被添加上代理服务器的名称。
响应首部字段(Response Header Fields)
从服务器端向客户端返回响应报文时使用的首部。补充了响应的附加内容,也会要求客户端附加额外的内容信息。
Accept-Ranges
是否接受字节范围请求(是否能处理范围请求,以指定获取服务器端资源的某个部分。)
字段值有bytes与none
Age
推算资源创建经过时间
ETag
资源的匹配信息
首部字段 ETag
能告知客户端实体标识。它是一种可将资源以字符串形式做唯一性标识的方式。服务器会为每份资源分配对应的 ETag
值。
另外,当资源更新时,ETag
值也需要更新。生成 Etag
值时,并没有统一的算法规则,而仅仅是由服务器来分配。
应用:比如中文版/英文版对应的资源。两者的URI
是相同的,所以仅凭 URI
指定缓存的资源是相当困难的。若在下载过程中出现连接中断、再连接的情况,都会依照 ETag
值来指定资源。(范围请求,查看资源是否更新时,对比ETag
)
强ETag
值和弱ETag
值
强 ETag
值,不论实体发生多么细微的变化都会改变其值。
ETag: "usagi-1234"
弱 ETag
值只用于提示资源是否相同。只有资源发生了根本改变,产生差异时才会改变 ETag
值。这时,会在字段值最开始处附加 W/。
ETag: W/"usagi-1234"
Location
令客户端重定向至指定URI
使用首部字段 Location 可以将响应接收方引导至某个与请求 URI 位置不同的资源。
配合3xx:Redirection 的响应,提供重定向的URI
几乎所有的浏览器在接收到包含首部字段 Location 的响应后,都会强制性地尝试对已提示的重定向资源的访问。
Proxy-Authenticate
代理服务器对客户端的认证信息
Retry-After
对再次发起请求的时机要求
首部字段Retry-After
告知客户端应该在多久之后再次发送请求。主要配合状态码 503 Service Unavailable 响应,或 3xx Redirect 响应一起使用。
字段值可以指定为具体的日期时间(Wed, 04 Jul 2012 06:34:24GMT 等格式),也可以是创建响应后的秒数。
Server
Server
首部包含了处理请求的源头服务器所用到的软件相关信息。
应该避免使用过长或者过于详细的描述作为 Server 的值,因为这有可能泄露服务器的内部实现细节,有利于攻击者找到或者探测已知的安全漏洞。
Vary
代理服务器缓存的管理信息
Vray
是一个HTTP响应头部信息,它决定了对于未来的一个请求头,应该用一个缓存的回复(response)还是向源服务器请求一个新的回复。它被服务器用来表明在 content negotiation algorithm(内容协商算法)中选择一个资源代表的时候应该使用哪些头部信息(headers)
当代理服务器接收到带有 Vary 首部字段指定获取资源的请求时,如果使用的 Vary 首部字段的值相同,那么就直接从缓存返回响应。反之,则需要先从源服务器端获取资源后才能作为响应返回。
WWW-Authenticate
服务器对客户端的认证信息
通用首部字段(General Headers Fields)
请求报文和响应报文两方都会使用的首部。
Cache-Control
控制缓存的行为
缓存请求指令:
no-cache
:强制向源服务器再次验证
no-store
:不缓存请求或响应的任何内容
max-age = [秒]
:响应的最大Age值
no-transform
:代理不可更改媒体类型
缓存响应指令:
public
:可向任意方提供响应的缓存
private
:仅向特定用户返回响应
缓存服务器会对该特定用户提供资源缓存的服务,对于其他用户发送过来的请求,代理服务器则不会返回缓存。
no-cache
:缓存前必须先确认其有效性
使用 no-cache 指令的目的是为了防止从缓存中返回过期的资源。
客户端发送的请求中如果包含 no-cache 指令,则表示客户端将不会接收缓存过的响应。于是,“中间”的缓存服务器必须把客户端请求转发给源服务器。
如果服务器返回的响应中包含 no-cache 指令,那么缓存服务器不能对资源进行缓存。源服务器以后也将不再对缓存服务器请求中提出的资源有效性进行确认(must-revalidate
),且禁止其对响应资源进行缓存操作。(此时缓存服务器仅转发请求)
Cache-Control: no-cache=Location
若报文首部字段 Cache-Control 中对 no-cache字段名具体指定参数值,那么客户端在接收到这个被指定参数值的首部字段对应的响应报文后,就不能使用缓存。换言之,无参数值的首部字段可以使用缓存。只能在响应指令中指定该参数。
no-store
:不缓存请求或响应的任何内容
从字面意思上很容易把 no-cache
误解成为不缓存,但事实上 no-cache
代表不缓
存过期的资源,缓存会向源服务器进行有效期确认后处理资源,也许称为do-notserve-
from-cache-without-revalidation
更合适。no-store
才是真正地不进行缓存
该指令规定缓存不能在本地存储请求或响应的任一部分
no-transform
:代理不可更改媒体类型
使用 no-transform 指令规定无论是在请求(客户端向代理发请求,代理响应)还是响应(源服务器向代理发响应,代理缓存)中,缓存都不能改变实体主体的媒体类型。
这样做可防止缓存或代理压缩图片等类似操作。
must-revalidate
:可缓存但必须再向源服务器进行确认
使用 must-revalidate
指令,代理会向源服务器再次验证即将返回的响
应缓存目前是否仍然有效。
若代理无法连通源服务器再次获取有效资源的话,缓存必须给客户端一条 504(Gateway Timeout)状态码。
另外,使用 must-revalidate
指令会忽略请求的 max-stale
指令(即使已经在首部使用了 max-stale
,也不会再有效果)。
proxy-revalidate
:要求中间缓存服务器对缓存的响应有效性再
进行确认
max-age = [ 秒]
:响应的最大Age值
当客户端发送的请求中包含 max-age
指令时,如果判定缓存资源的缓存时间数值比指定时间的数值更小,那么客户端就接收缓存的资源。
另外,当指定 max-age
值为 0,那么缓存服务器通常需要将请求转发给源服务器。
当服务器返回的响应中包含 max-age
指令时,缓存服务器将不对资源的有效性再作确认(自己直接支配该缓存),而 max-age
数值代表资源保存为缓存的最长时间。
HTTP/1.1版本缓存服务器遇到同时存在max-age
和Expires
首部字段的情况时,会优先处理max-age
指令,而忽略Expires
首部字段。但HTTP/1.0版本的缓存服务器情况相反
s-maxage = [ 秒]
:公共缓存服务器响应的最大Age值
Cache-Control: s-maxage=604800(单位 :秒)
上述表示缓存7天;7天后会去请求服务器,确认资源的有效性
s-maxage
指令的功能和 max-age
指令的相同,它们的不同点是 s-maxage
指令只适用于供多位用户使用的公共缓存服务器(一般指代理)。也就是说,对于向同一用户重复返回响应的服务器来说,这个指令没有任何作用。
另外,当使用 s-maxage 指令后,则直接忽略对 Expires 首部字段及max-age 指令的处理。(Expires时刻/Cache-Control: max-age时间段,两者都是用来控制强缓存的,Cache-Control优先级高)
min-fresh
要求缓存服务器返回至少还未过指定时间的缓存资源
比如,当指定 min-fresh 为 60 秒后,过了 60 秒的资源都无法作为响应返回了。
max-stale
使用 max-stale 可指示缓存资源,即使过期也照常接收。
如果指令未指定参数值,那么无论经过多久,客户端都会接收响应;如果指令中指定了具体数值,那么即使过期,只要仍处于 max-stale指定的时间内,仍旧会被客户端接收。
only-if-cached
使用 only-if-cached 指令表示客户端仅在缓存服务器本地缓存目标资源的情况下才会要求其返回。换言之,该指令要求缓存服务器不重新加载响应,也不会再次请求源服务器确认资源有效性。若发生请求缓存服务器的本地缓存无响应,则返回状态码 504 Gateway Timeout。
Connection
1.控制不再转发给代理的首部字段;逐跳首部(HTTP/1.1 和之后版本中,如果要使用 hop-by-hop 首部,需提
供 Connection 首部字段。)
只对单次转发有效,会因通过缓存或代理而不再转发(下面Upgrade首部字段产生作用的Upgrade对象仅限于客户端和邻接服务器之间)
第一次收到
GET / HTTP/1.1
Upgrade: HTTP/1.1
Connection: Upgrade
通过缓存或代理后,不再转发逐跳首部
GET / HTTP/1.1
2.管理持久连接
HTTP/1.1默认连接都是持久连接。当服务器端想明确断开连接时,则指定Connection 首部字段的值为 Close。
keep-alive(1.1/部分1.0)
Keep-Alive
是一个通用消息头,允许消息发送者暗示连接的状态,还可以用来设置超时时长和最大请求数
持久连接。特点是只要任意一端没有明确提出断开连接,则保持TCP连接状态;建立1次TCP连接后进行多次请求和响应的交互(解决了由于TCP连接的重复建立和断开而造成的通信量额外开销问题,使得HTTP请求和响应能够更早的结束,从而提高了web页面显示速度;也减轻了服务器端的负载)
注: 之前,当服务器将请求资源返回后,会立刻断开与浏览器的连接
...
Connection: Keep-Alive
...
Keep-Alive: timeout=5, max=1000
HTTP/1.1中,所有的连接默认都是持久连接。
持久连接需要服务端和客户端同时支持。
parameters(指令)
一系列用逗号隔开的参数,每一个参数由一个标识符和一个值构成,并使用等号 ('='
) 隔开。下述标识符是可用的:
timeout
:指定了一个空闲连接需要保持打开状态的最小时长(以秒为单位)。需要注意的是,如果没有在传输层设置 keep-alive TCP message 的话,大于 TCP 层面的超时设置会被忽略。max
:在连接关闭之前,在此连接可以同时发送的请求的最大值。在非管道连接中,除了 0 以外,这个值是被忽略的,因为需要在紧跟着的响应中发送新一次的请求。HTTP 管道连接则可以用它来限制管道的使用。
管线化
持久连接使得多数请求以管线化(pipelining)方式发送成为可能。以前发送请求后需要等待并收到响应,才能发送下一个请求。管线化技术使得不用等待响应亦可直接发送下一个请求(并行发送多个请求)。
持久连接与最大并发连接数(持久连接也不是越多越好的,因为会导致服务端负载增高) // 开多个TCP持久连接来实现并发性
一个TCP连接可以同时发送几个请求?
HTTP/1.1
中,单个TCP连接,在同一时间只能处理一个http请求,虽然存在Pipelining技术支持多个请求同时发送,但由于实践中存在很多问题无法解决,所以浏览器默认是关闭,所以可以认为是不支持同时多个请求。
HTTP2
提供了多路传输功能,多个http请求,可以同时在同一个TCP连接中进行传输。
浏览器http请求的并发性是如何体现的?并发请求的数量有没有限制?
页面资源请求时,浏览器会同时和服务器建立多个TCP连接,在同一个TCP连接上顺序处理多个HTTP请求。所以浏览器的并发性就体现在可以建立多个TCP连接,来支持多个http同时请求。
Chrome浏览器最多允许对同一个域名Host建立6个TCP连接,不同的浏览器有所区别。
Content-Type
这个字段用来表示报文主题的对象类型;
text/plain
:文本类型
text/html
:html文档类型
application/json
:json数据类型
Transfer-Encoding
规定了传输报文主体时采用的编码方式。
HTTP/1.1 的传输编码方式仅对分块传输编码有效。
Pragma
报文指令
Pragma 是 HTTP/1.1 之前版本的历史遗留字段,仅作为与 HTTP/1.0的向后兼容而定义。
所有的中间服务器如果都能以 HTTP/1.1 为基准,那直接采用 Cache-Control: no-cache 指定缓存的处理方式是最为理想的。但要整体掌握全部中间服务器使用的 HTTP 协议版本却是不现实的。因此,发送的请求会同时含有下面两个首部字段。
Cache-Control: no-cache
Pragma: no-cache
Via
使用首部字段 Via 是为了追踪客户端与服务器之间的请求和响应报文的传输路径。
报文经过代理或网关时,会先在首部字段 Via 中附加该服务器的信息,然后再进行转发。这个做法和 traceroute 及电子邮件的 Received首部的工作机制很类似。
首部字段 Via 不仅用于追踪报文的转发,还可避免请求回环的发生。所以必须在经过代理时附加该首部字段内容。
GET / HTTP/1.1
=>
GET / HTTP/1.1
Via: 1.0 gw.hack.jp(Squid/3.1) // 经过代理服务器A
=>
GET / HTTP/1.1
Via: 1.0 gw.hack.jp(Squid/3.1), // 经过代理服务器A
1.1 a1.example.com(Squid/2,7) // 经过代理服务器B
...
各个代理服务器会往Via首部添加自身服务器的信息
Via首部是为了追踪传输路径,所以经常会和 HTTP方法的TRACE 方法一起使用。
Warning
该首部通常会告知用户一些与缓存相关的问题的警告。
实体首部字段(Entity Header Fields)
针对请求报文和响应报文的实体部分使用的首部。补充了资源内容更新时间等与实体有关的信息。
Allow
用于通知客户端能够支持 Request-URI 指定资源的所有 HTTP 方法
当服务器接收到不支持的 HTTP
方法时,会以状态码405 Method Not Allowed 作为响应返回。与此同时,还会把所有能支持的 HTTP
方法写入首部字段 Allow
后返回。
Content-Encoding
实体主体适用的编码方式。
首部字段 Content-Encoding
会告知客户端服务器对实体的主体部分选用的内容编码方式。
采用的内容编码方式与请求首部字段 Accept-Encoding
所采用的4中编码方式一致
Content-Language
实体主体的自然语言;告知客户端,实体主体使用的自然语言(指中文或英文等语言)。
Content-Length
实体主体的大小(单位:字节); 首部字段 Content-Length 表明了实体主体部分的大小(单位是字
节)。对实体主体进行内容编码传输时,不能再使用 Content-Length首部字段。(实体主体大小的计算方法略微复杂,若想一探究竟,可参考 RFC2616 的 4.4)
Content-Location
替代对应资源的URI。
首部字段 Content-Location 给出与报文主体部分相对应的 URI。和首部字段 Location 不同,Content-Location 表示的是报文主体返回资源对应的 URI。
比如,对于使用首部字段 Accept-Language 的服务器驱动型请求,当返回的页面内容与实际请求的对象不同时,首部字段 Content-Location内会写明 URI。(访问 http://www.hackr.jp/ 返回的对象却是http://www.hackr.jp/index-ja.html 等类似情况)
Content-MD5
实体主体的报文摘要
// 服务端
报文主体
=>
MD5算法
=>
Base64编码
=>
Content-MD5: OGFkZDUwNGVhNGY3N2MxMDIwZmQ4NTBmY2IyTY== // 发给客户端
客户端会对接收的报文主体执行相同的MD5算法,然后与首部字段Content-MD5的字段值比较
首部字段 Content-MD5 是一串由 MD5 算法生成的值,其目的在于检查报文主体在传输过程中是否保持完整,以及确认传输到达。
对报文主体执行 MD5 算法获得的 128 位二进制数,再通过 Base64 编码后将结果写入 Content-MD5 字段值。由于 HTTP 首部无法记录二进制值,所以要通过 Base64 编码处理。为确保报文的有效性,作为接收方的客户端会对报文主体再执行一次相同的 MD5 算法。计算出的值与字段值作比较后,即可判断出报文主体的准确性。
采用这种方法,对内容上的偶发性改变是无从查证的,也无法检测出恶意篡改。其中一个原因在于,内容如果能够被篡改,那么同时意味着 Content-MD5 也可重新计算然后被篡改。所以处在接收阶段的客户端是无法意识到报文主体以及首部字段 Content-MD5 是已经被篡改过的。
Content-Range
实体主体的位置范围。请求中断后,再次请求,发送范围请求
HTTP/1.1 206 Partial Content
Date: xxx
Content-Range: bytes 5001-10000/10000
Content-Length: 5000
Content-Type: image/jpeg
Content-Type
实体主体的媒体类型。
首部字段 Content-Type
说明了实体主体内对象的媒体类型。和首部字段 Accept
一样,字段值用 type/subtype
形式赋值。
Expires
实体主体过期的日期时间
首部字段 Expires
会将资源失效的日期告知客户端。缓存服务器在接收到含有首部字段 Expires
的响应后,会以缓存来应答请求,在Expires
字段值指定的时间之前,响应的副本会一直被保存。当超过指定的时间后,缓存服务器在请求发送过来时,会转向源服务器请求资源。源服务器不希望缓存服务器对资源缓存时,最好在 Expires
字段内写入与首部字段 Date
相同的时间值。
但是,当首部字段 Cache-Control
有指定 max-age
指令时,比起首部字段 Expires
,会优先处理max-age
Last-Modified
资源的最后修改日期时间
首部字段Last-Modified
指明资源最终修改的时间。一般来说,这个值就是 Request-URI
指定资源被修改的时间。但类似使用 CGI
脚本进行动态数据处理时,该值有可能会变成数据最终修改时的时间。
非标准化但常用
Cookie(RFC里未定义字段)
Cookie的工作机制是用户识别及状态管理。
Web 网站为了管理用户的状态会通过 Web 浏览器,把一些数据临时写入用户的计算机内。接着当用户访问该Web网站时,可通过通信方式取回之前发放的Cookie。
首部字段 Cookie 会告知服务器,当客户端想获得 HTTP 状态管理支持时,就会在请求中包含从服务器接收到的 Cookie。接收到多个Cookie 时,同样可以以多个 Cookie 形式发送。
调用 Cookie 时,由于可校验 Cookie 的有效期,以及发送方的域、路径、协议等信息,所以正规发布的 Cookie 内的数据不会因来自其他Web 站点和攻击者的攻击而泄露。
服务器端发来Set-Cookie
首部字段信息,客户端根据规则会保存Cookie
,下次再向服务端发请求时,客户端会自动在请求报文中加入Cookie
值后发送出去。
Set-Cookie
开始状态管理所使用的Cookie信息
当服务器准备开始管理客户端的状态时,会事先告知各种信息
属性 | 说明 |
---|---|
NAME=VALUE | 赋予 Cookie 的名称和其值(必需项) |
expires=DATE | Cookie 的有效期(若不明确指定则默认为浏览器关闭前为止) |
path=PATH | 将服务器上的文件目录作为Cookie的适用对象(若不指定则默认为文档所在的文件目录) |
domain=域名 | 作为 Cookie 适用对象的域名 (若不指定则默认为创建 Cookie的服务器的域名) |
Secure | 仅在HTTPS安全通信时才会发送 Cookie |
HttpOnly | 加以限制,使Cookie不能被javascript脚本访问 |
domain属性
通过 Cookie 的 domain 属性指定的域名可做到与结尾匹配一致。比如,当指定 example.com 后,除 example.com 以外,www.example.com或 www2.example.com 等都可以发送 Cookie。
因此,除了针对具体指定的多个域名发送Cookie之外,不指定domain属性显得更安全
HttpOnly属性
Cookie 的 HttpOnly 属性是 Cookie 的扩展功能,它使 JavaScript 脚本无法获得 Cookie。其主要目的为防止跨站脚本攻击(Cross-sitescripting,XSS
)对 Cookie 的信息窃取
Set-Cookie: name=value; HttpOnly
通过上述设置,通常从 Web 页面内还可以对 Cookie 进行读取操作。但使用 JavaScript 的 document.cookie 就无法读取附加 HttpOnly 属性后的 Cookie 的内容了。因此,也就无法在 XSS 中利用 JavaScript 劫持Cookie 了。
X-Frame-Option
X-Frame-Options: DENY
The X-Frame-Options HTTP 响应头是用来给浏览器 指示允许一个页面 可否在<frame>
,<iframe>
,<embed>
或者<object>
中展现的标记。站点可以通过确保网站没有被嵌入到别人的站点里面,从而避免 clickjacking 攻击。
有三个可指定的字段值
X-Frame-Options: deny // 全部不允许,即使是同源的
X-Frame-Options: sameorigin // 该页面可以同源域名的页面frame中嵌套
X-Frame-Options: allow-from https://example.com/ // 表示该页面可以在指定来源的 frame 中展示。
对 apache2.conf 的配置实例
<IfModule mod_headers.c>
Header append X-FRAME-OPTIONS "SAMEORIGIN"
<IfModule>
X-XSS-Protection
首部字段 X-XSS-Protection 属于 HTTP 响应首部,它是针对跨站脚本攻击(XSS)的一种对策,用于控制浏览器 XSS 防护机制的开关。
X-XSS-Protection: 1
可指定的字段值如下
- 0:将XSS过滤设置成无效状态
- 1:将XSS过滤设置成有效状态
DNT
首部字段 DNT 属于 HTTP 请求首部,其中 DNT 是 Do Not Track 的简称,意为拒绝个人信息被收集,是表示拒绝被精准广告追踪的一种方法。
可指定字段值如下
- 0:同意被追踪
- 1:拒绝被追踪
协议中对X-前缀的废除
在 HTTP 等多种协议中,通过给非标准参数加上前缀 X-,来区别于标准参数,并使那些非标准的参数作为扩展变成可能。但是这种简单粗暴的做法有百害而无一益,因此在“RFC 6648 - Deprecating
the "X-" Prefix and Similar Constructs in Application Protocols”中提议停止该做法。
HTTP 首部字段,与缓存相关的部分
Expires(时间点)/cache-control(时间长度) // 响应首部字段
cache-control: max-age=31536000 // 一年
expires 是以前用来控制缓存的http头,Cache-Control是新版的API。现在首选 Cache-Control。因为过期标准的时间用的是本地时间,所以不靠谱,所以要优先使用Cache-Control代替Expires
Etag/cache-control
etag有请求也有响应, 只不过如果MD5一样,就不下载响应体,直接返回304给客户端
cache-control,直接使用缓存,不发请求
响应/请求
Last-Modified / If-Modified-Since(如果资源更新了则返回新资源及相关信息,否则返回304)
Etag / If-None-Match(如果资源更新了则返回新资源及相关信息,否则返回304)
Last-Modified响应中实体首部字段标记着文件在服务器端最后被修改的时间;
再次请求url,客户端会添加一个If-Modified-Since的标记,用来询问服务器该时间之后文件是否被修改过。如果服务器资源未改变,则返回304使用浏览器缓存,200 OK(from disk cache)
ETag主要是为了解决lLst-Modified(仅依据时间来判断是否过期)无法解决的问题
1、一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
2、某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒)
3、某些服务器不能精确的得到文件的最后修改时间;
上一次请求响应头有Expires或Cache-control,下一次请求看本地有无可用的缓存,
未过期,则使用缓存 from memory cache/from disk cache,请求头显示 Provisional headers are shown,无其他请求首部字段(正常请求有完整请求首部字段)
如果过期,那么请求头使用If-Modified-Since(Last-Modified)或If-None-Match(ETag)向服务器问询资源是否更新;服务器决策,200请求响应/304从缓存中读取