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從緩存中讀取