送給前端 er 一份 HTTP 基礎知識大圖
前言
最近經過幾輪的面試之后,被面試官虐得慘慘的,其中許多原因是沒有仔細的學過 HTTP 的知識,我對這塊的知識相對來說比較薄弱,所以決定把數據結構和算法得先放一放了。所以把 HTTP 的知識學一遍,然后再補一下JS的基礎。
下面廢話不多說,我們開始吧!
HTTP 是什么
HTTP是一種超文本傳輸協議,用于完成客戶端和服務器端等等一系列的運作流程。而協議指的是規則的約定。可以說,Web 是建立在 HTTP 協議上進行通信的。
HTTP的誕生
我相信大家也是這樣,在學一門技術之前都會去了解一下它的歷史,下面來看看 HTTP 的發展史。
HTTP 誕生于 1989 年 3 月。是一個名叫蒂姆伯納斯-李的哥們兒提出的,最初設想的基本理念是:借助多文檔之間相互關聯形成的超文本,連成可相互參閱的 WWW(World Wide Web,萬維網)。簡稱 Web。
HTTP 0.9 在 1990 年問世。那時候的 HTTP 還沒有作為正式的標準被建立。
HTTP 1.0 在 1996 年 5 月 正式作為標準。該協議標準現在仍然被廣泛使用在服務器端。
HTTP 1.1 在 1997 年 1 月公布為當前主流的 HTTP 協議版本。
HTTP 2.0 在 2012 年 3 月 征集建議。
HTTP 2.0 在 同年的 9 月份 發布了第一個草案。
HTTP 2.0 在 2014 年 11 月實現了標準化。
了解 TCP/IP
在理解 HTTP 之前,我們先簡單的來了解一下 TCP/IP 協議族。一般使用的網絡都是在 TCP/IP 協議的基礎上運作的,而 HTTP 屬于它內部的一個子集。
TCP/IP 協議族
在計算機和網絡設備進行互相通信時,雙方都必須基于相同的方法。比如,如何探測到通信目標,是哪邊先發起通信、用什么語言進行通信、怎樣結束通信等等一些規則都是先要確定好的。不同的硬件、操作系統之間的通信,所有的這一切都需要一種規則。而這種規則稱為協議。
協議中包括:從電纜的規格到 IP 地址的選定方法、尋找異地用戶的方法、雙方建立通信的順序,以及 Web 頁面顯示要處理的步驟,等等。將這些相關聯的協議集合起來總稱為 TCP/IP。
TCP/IP 模型各層作用
TCP/IP 重要的點就是分層。有以下4層:應用層、傳輸層、網絡層和數據鏈路層。
TCP四層.png
下面來介紹各層的作用。
- 應用層:應用層決定了向用戶提供應用服務時通信的活動。比如,FTP(文件傳輸協議)和 DNS(域名解析系統)。HTTP 協議也在該層。
- 傳輸層:傳輸層對上層應用層,提供處于網絡連接中的兩臺計算機之間的數據傳送。該層有兩個不同的協議:TCP 傳輸控制協議和 UDP 用戶數據協議。
- 網絡層:網絡層用來處理在網絡上的數據包。數據包是網絡傳輸的最小數據單位。網絡層的作用就是在多條路線中選出一條傳輸路線進行數據傳輸。
- 鏈路層:用來處理連接網絡的硬件部分。包括什么操作系統、硬件的設備、什么路由器啊之類的等等,都屬于該層。
TCP/IP 層次化的好處是:如果互聯網由一個協議統一規劃,某個地方需要改變設計時,就必須將所有部分整體替換掉。而分層之后只需要把變動的層替換掉。把各層之間的接口部分規劃好之后,每層內部的設計就可以自由改動。比如,處于應用層上的應用可以只考慮分配給自己的任務,不用去考慮其他的問題。
TCP/IP 通信傳輸流
TCP/IP 協議進行通信時,會通過分層順序和對方進行通信。客戶端從應用層往下走,服務器端則從鏈路層往上走。看下面的圖。
1.3.1.jpg
- 首先客戶端在應用層發出一個 HTTP 請求。
- 接著,在傳輸層接收到應用層的數據后進行分割,給每個報文打上標記序號以及端口號轉發給網絡層。
- 在網絡層,添加通信目的地的 MAC 地址后轉發給鏈路層。
- 接收端(也叫服務器端)的服務器在鏈路層接收到數據,按次序向上層發送,一直到應用層。傳輸到應用層才算真正接收到客戶端發過來的 HTTP 請求。
和HTTP相關的協議
在HTTP客戶端向服務器端發送報文之前,需要用到 IP、TCP、DNS 這三個和 HTTP 密不可分的協議。
IP 網絡協議
IP(Internet Protocol)網絡協議處于網絡層。IP協議的作用是把各種數據包傳送給對方。但要保證正確的傳送給對方,其中兩個重要的條件是 IP 地址和 MAC 地址。可以把它想象成你家的地址,或者說你的電話號碼。
IP 地址指的是節點被分配到的地址,MAC 地址指的是網卡所屬的固定地址。IP 地址可以跟 MAC 地址進行配對。IP 地址是可變的,MAC 地址是不可變的。
IP 和 IP地址別搞混了,IP是一種協議。而IP地址是則是每臺計算機的標識
ARP 協議
IP 間的通信依賴 MAC 地址。在網絡上通信的雙方很少會在同一個局域網,一般都是經過多臺計算機或者網絡設備中專才能連接到對方。而在中轉的過程中,會利用下一站中轉設備的 MAC 地址進行搜索下一個中轉目標。而這時,會用到ARP協議。ARP協議是一種用來解析地址的協議,通過通信方的 IP 地址就能反查出對應的 MAC 地址。
在到達通信目標前的中轉過程中,計算機和路由器只能獲取粗略的傳輸路線,這種機制叫做路由選擇。
就跟你在淘寶上買東西是一樣的道理。比如,你在淘寶網買了件衣服,快遞公司會根據你的地址進行送貨,在送貨這個過程中,并不是直接送到你手里。而是經過各種什么杭州中轉站然后又到深圳中轉站,之后才送到你手里。
TCP 協議
TCP 協議處于傳輸層,主要的作用是提供可靠的字節流服務。字節流服務指的是,為了方便傳輸,將大塊的數據分割成以報文段為單位的數據包進行管理。而可靠性的傳輸服務指的是,能夠把數據準確可靠的傳給對方。
為了準確的將數據傳送給對方,三次握手就出現了。下圖展示這個過程。
1.4.1.jpg
- 第一次握手:客戶端先發送一個帶 SYN 標志的數據包給對方。
- 第二次握手:服務器端收到之后,回傳一個帶有 SYN/ACK 標志的數據包表示傳達確認信息。
- 第三次握手:最后,客戶端再傳回一個帶 ACK 標志的數據包,表示 “握手” 結束。
DNS 服務
DNS 服務和 HTTP協議一樣,處于應用層。它主要的作用是,將域名解析成 IP 地址。DNS 協議可以通過域名查找 IP 地址,也可以通過 IP 地址反查域名的服務。
下面展示每個協議和HTTP協議的關系。
什么是URL和URI?
- URL指的是統一資源定位符,是訪問Web網站需要輸入的網站地址。例如,http://www.tutu.com。
- URI指的是統一資源標識符,全稱為Uniform Resource Identifier,它的作用是區分互聯網中的不同資源。比如,HTML 文檔、圖像、視頻片段、程序等等。而 URL 是 URI 的一個子集。
URI格式
下圖展示了 URI 的格式。
URI.jpg
- 協議名:http:或https:表示協議名稱。不區分字母大小寫,最后加上//。
- 登錄信息:user:pass@ 表示獲取服務器資源的用戶和密碼。但不推薦使用,因為不安全。
- 服務器地址:服務器地址有三種:
- 以域名的形式www.tutu.com;
- 以 IPv4 192.168.0.1地址名;
- 以[0:0:0:0:0:0:1]這種方括號括起來的 IPv6 地址;
- 服務器端口號::8080表示端口號。
- 文件路徑:/html/index.html表示服務器文件路徑,資源的訪問位置。
- 查詢字符串:?userId=1表示文件路徑中的參數。?后面以key=value的形式。如果后面還需要加參數,用&拼接。
- 片段標識符:#cn1表示文件中的某個位置。就是平時的網頁錨點定位。
HTTP基礎
HTTP 是一種無狀態的協議,對發送過的請求/響應都不做持久化處理。
持久化連接
HTTP 1.1 中的所有連接都是默認開啟的(keep-alive)。通過請求/響應頭部的Connection字段可以查看是否開啟持久化連接(后面會介紹該字段的值),而在 HTTP1.0中是默認關閉的(close)。
它的特點是,不管是客戶端還是服務器端,只要其中的一端沒有提出斷開連接,那么就會保持 TCP 連接。好處是,減少 TCP 連接的重復建立和斷開連接造成的額外開銷,減輕服務器壓力。這樣使得 HTTP 請求和響應速度更快結束,也提高頁面的顯示速度。
管線化
管線化是不用等待響應就可以發送下一個請求,也就是并行處理。不用一個接一個的等待響應,管線化比持久化連接還要更快。
HTTP 報文
HTTP 一共有兩種報文:請求報文、響應報文。報文又分為報文頭部和報文主體,報文主體是可選的。報文包含了以下三個部分。
- 起始行(start line)有以下兩種類型。
- 請求行:請求的方法、請求的 URL、HTTP的版本
- 響應行:HTTP 版本、狀態碼
- 頭部字段(header):一些頭部信息,以key: value的形式。
- 主體(body):被發送的數據。
報文主體.jpg
這張圖是以請求報文為例。
HTTP 的請求方法
- GET:獲取服務器資源。
- POST:提交信息給服務器。
- PUT:傳輸文件。
- HEAD:和 GET 方法一樣。但是只返回響應頭部。作用是確定 URL 的有效性和資源更新的時間。
- DELETE:刪除指定的資源。
- OPTIONS:查詢請求服務器指定的資源所支持的方法。
- TRACE:用來確認連接過程中發生的一些操作。
- CONNECT:建立連接渠道,用于代理服務器。
HTTP 狀態碼
1xx
1XX表示接收的請求正在處理。
2xx 成功
- 200 OK:表示客戶端發送的請求在服務器端被正常處理了。
- 204 No Content:表示請求被處理成功,但沒有資源可返回。
- 206 Partial Content:表示客戶端只獲取文件的一部分內容,而服務器成功執行了這部分的GET請求。響應報文中含Content-Range指定部分的實體內容。
3xx 重定向
- 301 Moved Permanenty:永久重定向。表示請求的資源已經被分配了新的 URL,以后就使用資源現在所指的 URL。
- 302 Found:臨時重定向。表示請求的資源被分配了新的 URL。
- 303 See Other:表示請求的資源存著另一個 URL,應該用GET方法獲取請求的資源。
- 304 Not Modified:表示請求已經找到,但不符合條件請求。協商緩存就會返回這個狀態碼。
- 307 Temporary Redirect:臨時重定向,和302類似。但是補鞥呢改變請求方法。
當301、302、303響應狀態碼返回時,幾乎所有瀏覽器都會將POST改為GET,并刪除請求報文中的主體,之后請求會自動再次發送。301、302標準是禁止把POST改成GET的,但實際使用的時候大家都會這么做。
4xx 客戶端錯誤
- 400 Bad Request:表示請求報文中存在語法錯誤。
- 401 Unauthorized:表示發送的請求要通過 HTTP 認證的認證消息。如果之前請求過一次,就表示用戶認證失敗。
- 403 Forbidden:表示對請求資源的訪問被服務器拒絕。
- 404 Not Found:表示服務器上無法找到請求的資源。
5xx 服務器錯誤
- 500 Internal Serve Error:表示服務器端在執行請求時發生錯誤。
- 503 Service Unavailable:表示服務器暫處于超負荷或者正在進行停機維護。
和HTTP相關Web服務器
HTTP進行通信時,除了客戶端和服務器端這兩個之外,還有一些用于通信數據轉發的應用程序。例如代理、網關、隧道和緩存。
代理
代理是一種具有轉發功能的應用程序,它存在于客戶端和服務器端之間,相當于一個中間人。它將客戶端發送過來的請求并轉發給服務器端。當然,它也會將服務器端返回的響應轉發給客戶端。
代理服務器.jpg
每次通過代理服務器轉發請求或響應時,頭部都會出現Via這個字段。
網關
網關是一種特殊的服務器,作為其他服務器的中間實體使用。用于將 HTTP 請求轉化成其他協議通信。網關接收請求時就好像自己的資源的源服務器一樣對請求做處理。
網關.jpg
隧道
隧道是可按要求建立一條和其他服務器的通信線路,到時候使用 SSL 加密進行通信。隧道的目的是保證客戶端和服務器進行安全的通信。
隧道.jpg
緩存
緩存是指代理服務器或客戶端本地磁盤中保存的資源副本。利用緩存可以減少向源服務器的訪問,主要目的是減少網絡帶寬的流量和通信時間。
緩存服務器是代理服務器的一種,當代理轉發從服務器返回的響應時,會保存一份資源的副本。緩存服務器的優點在于通過緩存可以避免多次從源服務器轉發資源。因此客戶端可就近從緩存服務器上獲取資源,而源服務器也不必多次處理相同的請求。
緩存的有效期
每當源服務器上的資源更新時,如果還是用不變的緩存,那就會變成返回更新前的舊資源。
即使存在緩存,也會因為客戶端的要求、緩存的有效期等等一些因素,向源服務器確認資源的有效性。如果緩存的資源已過期,緩存服務器會向源服務器上獲取新的資源。
客戶端緩存
這里的客戶端緩存指的是瀏覽器中的緩存。瀏覽器緩存如果未過期,就不用向源服務器請求相同的資源,直接獲取緩存在本地磁盤中的資源。當資源過期時,會向源服務器確認資源的有效性。如果緩存的資源過期,就會再次向源服務器發起資源請求。
內容協商
內容協商機制是指客戶端和服務器端就響應的資源內容進行互相協商,然后提供客戶端最合適的資源。內容協商會以語言、字符集、編碼方式等。
主要使用的請求頭有:
- Accept
- Accept-Charset
- Accept-Language
- Content-Language
內容協商技術有下面三種類型。
- 服務器驅動協商(Server-driven Negotiation)
- 由服務器進行內容協商。
- 客戶端啟動協商(Agent-driven Negotiation)
- 由客戶端進行內容協商。
- 透明協商 服務器驅動和客戶端驅動的結合體,由服務器和客戶端進行內容協商的一種方法。
End-to-end頭部和Hop-by-hop頭部
HTTP 頭字段定義成緩存代理和非緩存代理。分為兩種類型。
- 端到端頭部End-to-end
分在這個類別中的頭部會轉發給請求或響應對應的最終接收目標,而且必須保存在緩存生成的響應中,另外規定它必須被轉發。
- 逐跳頭部Hop-by-hop
分在這個類別中的頭部只對單次轉發有效,會因通過緩存或代理而不轉發。在 HTTP 1.1 和之后的版本中,如果使用Hop-by-hop頭,就要提供 Connection 頭字段。
除了下面 8 個頭字段外,其他所有字段都屬于端到端頭部。
- Connection
- Keep-Alive
- Proxy-Authenticate
- Proxy-Authorization
- Trailer
- TE
- Transfer-Encoding
- Upgrade
HTTP通用頭部字段
下面列出請求/響應頭都會出現的字段,這些字段都含有重要的信息。
Cache-Control
Cache-Control表示資源的緩存操作,參數是可選的,如果存在多個參數的話,以,分隔開。
請求頭
在請求頭中使用Cache-Control字段時,它的值如下:
- no-cache:不使用強緩存,強制向源服務器再次驗證緩存的資源是否過期(走協商緩存)。
- no-store:不使用任何緩存,每次都向源服務器獲取最新的資源。
- max-age:以秒為單位。表示緩存的資源沒有超過指定的時間,客戶端就從緩存中獲取資源。
- min-fresh:以秒為單位。要求代理服務器返回至少還沒過指定時間的緩存資源。
- max-stale:即使是過期的資源,也照樣接收。
- only-if-cached:告訴代理服務器,從緩存中獲取資源(如果有)。
- no-transform:不能對資源進行轉換,可以防止緩存或代理壓縮圖片等類似操作。
響應頭
在響應頭中使用Cache-Control字段時,它的值如下:
- public:資源可被瀏覽器和代理服務器進行緩存。
- private:資源只可以被瀏覽器進行緩存。其他都不可以。
- no-cache:可以緩存,但每次使用前要向源服務器驗證緩存資源是否過期。
- s-maxage:只提供給代理服務器,表示代理服務器中的資源過期時長,用s-maxage后,會忽略max-age和Expires字段。
- max-age:以秒為單位。設置緩存時間,如果沒超過該時間,不用向服務器請求資源。如果超過,就證明資源已過期。如果響應頭出現Expires字段,在 HTTP 1.1 中會有限處理max-age,而 HTTP1.0 中則相反。
- must-revalidate:可以緩存,但必須向源服務器再次驗證。如果請求失敗,則返回504狀態碼。該字段會忽略max-stale。
- proxy-revalidate:要求緩存服務器對緩存的響應有效性進行確認。
- no-transform:不能對資源進行轉換,可以防止緩存或代理壓縮圖片等類似操作。
Connection
Connection字段決定當前 TCP連接 完成后,是否關閉連接。有以下兩種。
- keep-Alive:持久化連接。
- close:TCP 連接完成后,立馬關閉連接。
Date
Date字段的值為GMT時間日期格式,表示 HTTP 報文創建的時間和日期。
- Date: Tue, 13 Apr 2021 12:35:41 GMT
Pragma
Pragma是用來向后兼容只支持 HTTP1.0 協議的緩存服務器。它的效果和Cache-Control一樣。
- Pragma: no-cache
Upgrade
Upgrade用于查看 HTTP 協議或者其他協議是否可以使用更高的版本進行通信。
- Upgrade: HTTP/2.0
- Connection: Upgrade <!-- 表示刪除了Upgrade后再轉發 -->
Via
Via用于跟蹤客戶端和服務器端之間的請求和響應報文的傳輸路徑,還可以避免請求循環的發生。
- <!-- 經過單個代理服務器 -->
- Via: 1.0 gw.hackr.jp(Squid/3.1)
- <!-- 經過多個代理服務器 -->
- Via: 1.0 gw.hackr.jp(Squid/3.1), 1.1 al.example.com(Squid/2.7)
經過代理服務器 A 時,Via頭部附加了 “1.0 gw.hackr.jp(Squid/3.1)” 這樣的字符串值。行頭 1.0 指的是接收請求的服務器上應用的 HTTP 協議版本。如果經過多個代理服務器的話,這些信息會后面追加。
Warning
Warning字段告訴用戶一些與緩存有關的警告。
請求頭字段
Accept
Accept請求頭用來告訴服務器,客戶端能處理的內容類型。下面列出幾種媒體類型。
- 文本文件: text/html、text/plain、text/css、application/xhtml+xml、application/xml等等。
- 圖片文件: image/jpeg、image/gif、image/png;
- 視頻文件: video/mpeg、video/quicktime;
- 二進制文件:application/octet-stream、application/zip;
當值為*/*,表示客戶端可以是任意內容類型。當值為image/*,用來代表任何其他圖片類型。
如果想給顯示的媒體類型增加優先級,通過用q=表示權重值,用分號(;)分隔開。權重值的范圍是0~1,可以精確到小數點后 3 位,1是最大值。沒有指定權重值時,默認權重是q=1.0。
- Accept: text/html, appliaction/json;q=0.9
Accept-Charset
Accept-Charset請求頭用來告訴服務器,客戶端可處理的字符集類型。另外,可以一次性指定多種字符集。和Accept一樣,通過q值來表示優先級。該頭部應用于內容協商機制的服務器驅動協商。
- Accept-Charset: iso-8859-1
- Accept-Charset: iso-8859-1;q=0.5
Accept-Encoding
Accept-Encoding請求頭用來告訴服務器,客戶端能理解的內容編碼方式。可以一次性指定多種內容編碼,有以下幾種編碼。
- gzip:由文件壓縮程序 gzip 生成的編碼格式,使用 Lempel-Ziv 算法以及 32 位循環冗余驗證。
- compress:由 UNIX 文件壓縮程序compress生成的編碼方式,采用Lempel-Ziv-Welch算法。
- deflate:組合使用 zlib 格式以及由deflate壓縮算法生成的編碼方式。
- indentity:不執行壓縮或不會變化的默認編碼格式。
- Accept-Encoding: gzip, deflate
和Accept一樣,用q值來設置優先級。還有使用星號(*),表示指定任意的編碼格式。
Accept-Language
Accept-Language請求頭用來告訴服務器,客戶端可以理解的自然語言集(指中文和英文集),以及自然語言集的優先級。和Accept一樣的,可指定多個自然語言集。使用q值設置優先級。
- Accept-Language: zh-CN,zh;q=0.9;q=0.8
客戶端在服務器有中文版的情況下,會請求返回中文版的響應,如果沒有,則返回英文版。
Authorization
Authorization用來告訴服務器,用戶代理的認證信息(證書值)。通常會在服務器返回401狀態碼響應后,把頭部字段Authorization添加到請求中。
- Authorization: Basic dWVub3NlbjpwYXNzd29yZA==
Expect
Expect用來告訴服務器,只有在滿足這個條件的情況下才會處理請求。如果服務器不能滿足客戶端的要求,會返回417狀態碼。目前只規定了100-continue這個條件。
- Expect: 100-continue
From
From字段表示用戶代理的用戶的電子郵箱地址,目的是為了顯示搜索引擎用戶代理的負責人的電子郵箱聯系方式。
- From: info@hackr.jp
Host
Host請求頭指明了請求的資源所在的服務器主機名和端口號。如果服務器沒設置主機名,會發送一個空值。
- Host: www.tutu.com
If-Match
像這種If-xxxx開頭的請求頭字段,都是條件請求。服務器接收到附帶條件的請求后,只判斷條件是真時才會執行請求。
If-Match字段用于和服務器資源的ETag值做對比時,ETag值和If-Match的值相等時才會處理該請求。否則返回412狀態碼。通過用星號(*)的方式,表示只要資源存在就處理請求,但服務器會忽略掉ETag的值。
- If-Match: "123456"
If-Modified-Since
If-Modified-Since用于確定代理服務器或客戶端的資源有效性。在指定的時間之后,請求的資源發生改變時,就處理請求。如果資源都沒有改變,則返回304狀態碼。
- If-Modified-Since: Tue, 13 Apr 2021 12:35:41 GMT
If-None-Match
If-None-Match和If-Match相反,只有服務器資源的ETag的值和If-None-Match的值不一樣時,才會處理該請求。在GET和HEAD請求方法中加入該字段可以獲取最新的資源。
If-Range
If-Range用于告訴服務器,如果If-Range字段的值和請求資源的ETag值或時間一樣時,就會作為范圍請求處理(Range字段規定請求多少字節的數據)。否則,忽略范圍請求,返回全部資源。
- If-Range: "123456"
- Range: bytes=5001-10000
If-Unmodified-Since
If-Unmodified-Since字段用來告訴服務器,只有當請求資源在指定的時間之后沒有修改的情況下,才會處理請求。如果在指定的時間后發生了修改,則返回412狀態碼。
- If-Unmodified-Since: Tue, 13 Apr 2021 12:35:41 GMT
Proxy-Authorization
Proxy-Authorization字段包含了用戶代理提供給代理服務器用于身份驗證的憑證。
- Proxy-Authorization: Basic dGlwOjkpNLAGfFY5
Range
Range請求頭字段表示獲取資源的哪一部分。服務器收到帶有Range字段的請求后,會在處理請求之后返回206狀態碼。如果無法處理,則返回200狀態碼并把全部資源返回。
- Range: bytes=5001-10000 <!-- bytes=5001-10000為資源的字節 -->
Referer
Referer字段表示請求的URL是從哪個 Web 頁面發起的。服務器通過Referer字段來標識訪問來源,進行統計分析、日志記錄和緩存優化。
- Referer: www.tutu.com <!-- 請求是由www.tutu.com發起的 -->
TE
TE表示客戶端能夠處理響應的傳輸編碼方式以及優先級。
- TE: gzip, deflate;q=0.5
User-Agent
User-Agent字段用于把請求的瀏覽器和用戶代理名稱等信息傳給服務器。
- User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36
響應頭字段
Accept-Ranges
Accept-Ranges表示服務器端能處理指定范圍內的資源。有兩個值:bytes和none。none表示不能處理指定范圍內的請求。
Age
Age表示源服務器再多久前返回過資源。單位為秒。
- Age: 3600
ETag
ETag表示資源的特定標識符,服務器會給每份資源都分配對應的ETag值。當資源發生變化時,ETag值也會改變。例如,訪問同一個 URL 的網站同時具備有中英文版,當切換中文時,會返回中文的資源(ETag: user-chi),而切換到英文時,返回的是英文的資源(ETag: user-us)。
強弱ETag
- 強ETag: 不管資源發生了什么變化都會改變其值。
- ETag: "user-123456"
- 弱ETag: 用于資源是否相同,只有資源發生了根本改變,產生差異時才會改變ETag值。字段值最開頭會添加W/的字符
- ETag: W/"user-123456"
Location
Location字段表示需要將頁面重定向到某個地址,一般都是響應碼為3xx的響應才有用。
- Location: www.baidu.com <!-- 重定向到www.baidu.com -->
Proxy-Authenticate
Proxy-Authenticate字段表示獲取代理服務器的資源要通過身份驗證的方式。
Retry-After
Retry-After字段表示客戶端應該在多久之后再次請求。配合503和3xx狀態碼響應一起使用。
- Retry-After: 120
Server
Server字段表示處理請求的服務器所用到的軟件相關信息。
- Server: Apache/2.2.17
Vary
Vary字段可以對緩存進行控制。從代理服務器收到源服務器返回含有Vary指定項的響應后,如果要進行緩存,那么會對請求中含有Vary指定頭部字段的請求返回緩存。即使對相同資源發起請求,如果Vary指定的頭部字段不相同,就必須從源服務器重新獲取資源。
- Vary: Accept-Language
實體頭字段
Allow
Allow用于告訴客戶端,資源所支持的 HTTP 方法,如果服務器收到不支持的 HTTP 方法時,會返回405狀態碼作為響應。
- Allow: GET, DELETE
Content-Encoding
Content-Encoding字段表示服務器對實體的主體部分用的內容編碼方式。內容編碼在Accept-Encoding中已經介紹過,一共有4種。
- Content-Encoding: gzip
Content-Language
Content-Language字段表示實體主體使用的自然語言。
- Content-Language: zh-CN
Content-Length
Content-Length表示實體主體部分的大小(以字節為單位)。
- Content-Length: 1500
Content-Location
Content-Location字段表示要返回數據的地址。
- Content-Location: https://www.tutu.com/index.html
Content-Range
Content-Range字段表示的是一個數據片段在整個文件中的位置。
- Content-Range: bytes 5001-10000/10000
Content-type
Content-type字段表示實體主體中對象的媒體類型。
- Content-type: text/html; charset=UTF-8
Expires
Expires字段會把資源失效的日期告訴客戶端。在這個日期后,資源就會過期。也就是說,在指定的日期內可以從瀏覽器緩存中獲取資源。如果超過了這個日期,就必須向服務器發起資源請求。如果頭部存在Cache-Control: max-age時,會優先處理max-age指令。
- Expires: Tue, 13 Apr 2021 12:35:41 GMT
Last-Modified
Last-Modified字段表示資源最后修改的時間。
- Last-Modified: Tue, 13 Apr 2021 12:35:41 GMT
HTTP 緩存
HTTP 緩存可分為強緩存和協商緩存,主要用于加快資源的獲取速度,提高用戶體驗,減少網絡連接,緩解服務器壓力。
強緩存
對強緩存來說,瀏覽器會判斷請求的資源是否在有效期內。如果是在有效期內,就直接從緩存中讀取資源,不用向服務器發送資源請求。強緩存通過Expires、Cache-Control和Pragma這三個頭部字段設置。
Cache-Control
Cache-Control頭部字段在上面也詳細介紹過在各端所具備的屬性值。下面來列出最常見的幾個值。
- public: 該資源可以被瀏覽器和代理服務器進行緩存。
- private: 該資源只可以被瀏覽器緩存,其他都不可以。
- no-cache: 不使用強緩存,強制向源服務器再次驗證緩存的有效性。這個值表示走協商緩存。
- no-store: 不使用任何緩存,每次都向源服務器獲取最新資源。
- max-age: 如果緩存的資源沒有超過規定的時間,客戶端就從緩存中獲取資源。以秒為單位。
- s-maxage: 只適用于代理服務器,表示代理服務器中的資源過期時長,用了s-maxage后,會忽略max-age和Expires字段。
Expires
Expires字段的值是一個GMT格式的時間日期,將資源失效的日期告訴客戶端,客戶端收到帶有該字段的響應體后進行緩存。后續客戶端發起相同的資源請求,會用Expires的值和本地時間做對比,如果該請求的本地時間小于Expires的值,就直接用緩存中的資源,不用向服務器發起請求。
Expires的值會產生一個問題。如果修改了本地時間,就會導致客戶端和服務器端的時間不一致,那么對于緩存過期的判斷就無法和預期一樣了。
Expires在三者中優先級最低。
Pragma
Pragma可以看上面的介紹,這里不過多講解。
強緩存在 Chrome 中會返回200狀態碼并且有兩種情況。
- memory cache: 只要頁面不關閉,就會從瀏覽器內存中獲取資源。
- disk cache: 從磁盤中讀取緩存資源。
強緩存.jpg
用了強緩存后,如果服務器端的資源更新了,客戶端是不知道的,而且在過期之前都會用緩存中的資源。可以通過Ctrl + F5強制刷新。
協商緩存
協商緩存是在用本地緩存之前,會向服務器發起一次GET請求,驗證瀏覽器保存在本地的資源是否過期。
last-modified和if-unmodified-sine
一般情況下是用請求資源的最近一次修改時間戳來判斷。來舉個例子:假設客戶端向服務器端請求一個文件,為了讓資源被再次請求時能通過協商緩存機制使用本地緩存。首次返回該資源的響應頭中會包含一個last-modified的字段,字段的值表示資源最后修改的時間。當刷新頁面時,該資源使用的是協商緩存,瀏覽器無法確認本地緩存是否過期,然后向服務器發起一次GET請求,進行緩存有效性的協商,本次請求的請求頭中包含一個if-unmodified-since字段,字段的值是上次響應頭中的last-modified字段的值。
last-modified的不足之處
last-modified存在兩個缺陷:
- 它只是根據資源最后的修改時間進行判斷,雖然請求的文件資源進行了編輯,但內容并沒有改變,時間也會更新。這就導致了協商緩存時關于有效性的判斷驗證失效,就要重新進行資源的請求。
- 由于文件資源修改的時間單位是秒,如果文件修改很頻繁。比如,幾百毫秒內改一次,就無法識別出該文件資源的更新。
ETag和if-none-match
為了彌補通過時間判斷的不足,HTTP 1.1 加入了ETag(實體標簽)的頭信息。
ETag表示資源的特定標識符,類似文件指紋。作用上面也有提到,這里不過多講解。
當響應頭同時存在last-modified和ETag這兩個字段時,會以ETag為準。再次對該資源發起請求時,會將之前的響應頭中ETag的值當作這次請求中if-none-match字段的值,發送給服務器進行緩存有效性驗證。如果驗證緩存有效,就返回304狀態碼響應重定向到本地緩存。
ETag的不足之處
ETag的出現并不是last-modified的代替品,而是一種補充方案,它還是存在弊端的。
- 如果資源比較大,數量多而且修改頻繁的話,那么生成ETag的過程會影響到服務器的性能。
- 上面也講了ETag還分強ETag和弱ETag。
- 強ETag值是根據資源內容進行生成,保證每個字節都相同。
- 弱ETag值是根據資源的部分屬性值來生成,生成速度快但是無法保證每個字節都相同。
如果瀏覽器走的是協商緩存,并且資源沒發生改變,服務器端會返回304狀態響應碼告訴瀏覽器獲取本地緩存的資源即可。
1617632172962.jpg
HTTP 的缺點
HTTP協議主要的不足之處有以下幾點。
- 通信使用明文,內容會被竊聽
- 不驗證通信方的身份,可能遭遇偽裝
- 無法證明報文的完整性,可能已遭到篡改
通信使用明文,內容會被竊聽
HTTP 協議本身沒有加密功能,所以無法做到對通信請求和響應內容進行加密。
TCP/IP 是會被竊聽的網絡
由于 TCP/IP 協議的工作機制,通信內容在所有通信線路上都有可能遭到窺視。不管是哪個角落的服務器在跟客戶端進行通信,通信的線路上的一些設備都不可能是個人物品。所以不排除在某個環節上遭到惡意窺視的行為。即使進行加密處理,也會被窺視到通信的內容。竊聽相同端上的通信并不是難事,只要收集在網絡上流動的數據包就行。可以通過抓包和嗅探器工具來收集數據包。
解決方案: 加密處理防止竊聽
最常見的兩種加密方式是通信加密和內容加密
通信加密
HTTP 協議中本身沒有加密機制,但可以通過 SSL(Secure Socket Layer 安全套階層) 或 TLS(Transport Layer Security 安全傳輸層協議) 的組合使用,加密HTTP的通信內容。用 SSL 建立安全通信線路后,就可以在這條線路上進行 HTTP 通信。和 SSL 組合使用的 HTTP 叫做 HTTPS (HTTP Secure 超文本傳輸安全協議) 或 HTTP over SSL。
內容加密
由于 HTTP 協議中沒有加密機制,那么可以對傳輸的內容本身進行加密。也就是把 HTTP 報文中包含的內容進行加密處理。在這種情況下,客戶端需要對 HTTP 報文主體(body)進行加密處理后再發送請求。要做到內容的加密,前提是客戶端和服務器端同時具有加密和解密的機制。主要應用在 Web 服務器中。該方式不同于 SSL 和 TLS 把整個通信線路加密處理,所以內容還是會有被篡改的可能。
不驗證通信放的身份可能遭遇偽裝
HTTP 協議的請求和響應都不會對通信方進行確認。
任何人都可以發起請求
在 HTTP 協議通信時,由于不存在確定通信方的處理步驟,任何人都可以發起請求。服務器只要收到請求,不管是誰都會返回一個響應(僅限發送端的 IP 地址和端口號沒被 Web 服務器設置限制訪問的前提下)。也就是來者不拒。
- 有可能是偽裝的服務器。
- 有可能是偽裝的客戶端。
- 無法確定正在通信的對方是否具備訪問權限。因為某些 Web 服務器上保存有重要的信息,只想發給特定用戶通信的權限。
- 無法判斷請求是從哪來、出自誰手。
- 即使是無意義的請求也照樣接收。無法阻止大量請求下的 Dos 攻擊(Denial of Service,拒絕服務器攻擊)。
解決方案:查明對方的證書
雖然使用 HTTP 協議無法確定通信方,但使用 SSL 可以。SSL 除了加密處理外,還用了一種證書的手段,用于確認通信方。證書是由值得信任的第三方機構頒發,用來證明服務器和客戶端是真實存在的。
通過證書,以證明通信方就是意料中的服務器,對個人來說,減少了個人信息泄露的危險。另外,客戶端持有證書即可完成個人身份的確認,也可用于對 Web 網站的認證環節。
無法證明報文完整性,可能已篡改
收到的內容不完整
沒有任何辦法確認,發出去的請求或響應和接收到的請求或響應是前后相同的。有可能在中途被篡改成其他的內容,即使內容是真的被改了,接收方也不會知道。
解決方案:MD5 和 SHA-1
可以使用 MD5 和 SHA-1 等散列值校驗方法,以及用來確認文件的數字簽名方法(PGP簽名)對內容進行加密。但是用這些方法也無法保證正確,因為 MD5 和 PGP 本身被修改的話,用戶也不會知道。
HTTPS
HTTP加上加密處理和認證以及完整性保護機制就是HTTPS
HTTPS 是身披 SSL 外殼的 HTTP
HTTPS 不是應用層的一種新協議。只是 HTTP 通信接口部分用 SSL 和 TLS 協議代替而已。之前是 HTTP 和 TCP 進行通信,用了 SSL 之后,就變成了 HTTP 先和 SSL 通信,之后 SSL 和 TCP 通信。
HTTPS.png
使用 SSL 之后,HTTP 就有了 HTTPS 的加密、證書和完整保護性這些功能。
加密方式
SSL 用的是公開密鑰加密的處理方式。加密方法中的加密算法是公開的,密鑰則是保密的。通過這種方式可以保持加密方法的安全性。
加密和解密都會用到密鑰。沒有密鑰就沒辦法對密碼解密,任何人只要有密鑰就可以進行解密,如果密鑰被攻擊者獲得,那么加密也就沒有意義了。
對稱加密
加密和解密同用一個密鑰的方式叫做共享密鑰加密,也稱為對稱密鑰加密。也就是說,客戶端和服務器端共用一個密鑰對消息進行加密。客戶端在發送請求時,會用密鑰對消息加密。服務器收到后,再用密鑰對消息進行解密。
缺點
對稱加密雖然保證了消息保密性,但客戶端和服務器端用的都是同一個密鑰,如果說在傳輸的過程中出現了中間人或攻擊者。密鑰就有可能落到攻擊者手中,這樣就對消息加密就沒有什么意義了。
非對稱加密
非對稱加密解決了對稱加密的缺點。非對稱加密用的是一對非對稱的密鑰。一把叫做私有密鑰,另一把叫做公開密鑰。私有密鑰只能是自己所擁有,而公開密鑰則是任何人都可以拿到。
當客戶端發送消息前,使用公共密鑰進行加密,而服務器收到消息后,使用私有密鑰進行解密。
缺點
非對稱加密需要在發送端在發送消息時,用公鑰加密。但公鑰是任何人都可以拿到,中間人也可以。中間人雖然不知道接收方的私鑰是什么,但可以截獲發送端的公鑰,自己另外生成一把公鑰或者篡改公鑰,把公鑰發給接收端。而且非對稱加密處理起來比對稱加密的方式更加復雜,這樣就導致了效率變低。
混合加密機制
HTTPS 用的就是對稱加密和非對稱加密兩者的混合加密。使用對稱加密的好處是解密效率快,使用非對稱加密的好處是在傳輸消息的過程中不會被破解。即使截獲了數據,沒有對應的私鑰,也無法對消息進行破解。
摘要算法
數字摘要是采用 Hash 函數將需要加密的明文 “摘要” 成一串固定長度(128位)的密文,這串密文又稱為數字指紋,它有固定長度,而且不同的明文摘要成密文,其結果總是不同,而同樣的明文摘要必須一致。數字摘要是 HTTPS 能確保數據完整性和防篡改的根本原因。
數字簽名
數字簽名是非對稱加密和數字摘要兩項技術的應用,它將摘要信息用發送者的私鑰加密,和原文一起發給接收者。接收者只有用發送者的公鑰才能解密被加密的摘要信息,然后用 Hash 函數對收到的原文產生一個摘要信息,與解密的摘要信息對比。如果一樣,那就說明收到的信息是完整的。否則說明信息被修改過,因此數字簽名能夠驗證信息的完整性。
數字簽名是附加在報文上的特殊加密校驗碼。使用數字簽名有以下兩點好處。
- 簽名能確定消息是由發送方簽名并發過來的,因為別人假冒不了發送方的簽名。
- 簽名能確定消息的完整性,證明數據沒有被篡改過。
數字簽名的過程如下:明文 \-> hash運算 \-> 摘要 \-> 私鑰加密 \-> 數字簽名
數字證書
數字證書(CA)就像我們的身份證一樣,信息都是唯一性的。它是屬于可信任的一些第三方機構所有。證書包含了以下的信息。
- 證書的發布機構 CA
- 證書的有效期
- 公鑰
- 證書所有者
- 簽名
數字證書還包括對象的公鑰,對象和所用簽名算法的描述信息。所有人都可以創建一個數字證書,但并不是所有人都能獲得簽發權,從而為證書信息擔保,并用它私有密鑰簽發證書。
HTTPS 的工作流程
- 首先是客戶端向服務器端發起一個 HTTPS 請求。
- 服務器端返回公鑰證書給客戶端。
- 客戶端收到公鑰證書后,用證書的公鑰驗證數字簽名,以確認服務器的公鑰的真實性。
- 客戶端用隨機數生成器生成臨時的會話密鑰,然后用服務器的公鑰對該會話密鑰進行加密,發送給服務器端。
- 服務器收到后,用自己的密鑰對會話密鑰解密。
- 之后客戶端和服務器端就開始了 HTTPS 通信。
SSL 和 TSL
HTTPS 用的是 SSL(Secure Socket Layer 安全套階層) 和 TLS(Transport Layer Security 安全傳輸層協議)這兩個協議。SSL 最開始是網景先倡導,后來網景涼了,就轉移給了 IETF 的手里。IETF 以 SSL 3.0 為準,之后又定制了 TLS1.0、TLS1.1和 TLS1.2。TLS 是以 SSL 為原型開發的協議。有時候統一稱該協議為 SSL。
為什么不一直用 HTTPS
凡事都具有兩面性,不是說 HTTPS 安全就沒有問題了。其實它還是存在一些問題的。在使用 SSL 時,它的處理速度會變慢。其原因有兩種,一是通信慢,二是每次都進行加密通信,就導致消耗大量的 CPU 和內存資源,導致處理速度變慢。
- 除了和 TCP 連接、發送請求和響應之外,還要和 SSL 進行通信。
- 另外 SSL 要進行加密處理,在服務器和客戶端都要進行加密和解密的運算處理。
- 要用 HTTPS 通信,購買證書是必不可少的。
當然可以用 SSL 加速(專用服務器)硬件來改善效率的問題。可以提高 SSL 的計算速度,分擔負載。但只有在 SSL 處理時才發揮 SSL 加速器的效果。像一些非敏感的信息就用 HTTP 進行通信,對于敏感信息采用 HTTPS 通信,以節約資源。
HTTP 和 HTTPS 的區別
- HTTP 是以明文的方式進行傳輸,HTTPS 則是具有安全性的 SSL 加密傳輸協議。
- HTTP 和 HTTPS 用的是兩種不同的方式進行連接,端口號也不一樣。前者是 80,后者是 443。
- 想要用 HTTPS 就得購買證書(CA),而免費的整數一般都很少,所以需要支付一定的費用。
- HTTPS 對搜索引擎更友好,有利于 SEO ,優先索引 HTTPS 的網頁。
- HTTP 的連接簡單,并且是無狀態的。HTTPS 是由 SSL + HTTP 協議構建的可進行加密傳輸、身份認證的網絡協議,比 HTTP 要安全。
解決 HTTP 1.x 瓶頸的 SPDY
HTTP 1.x的缺點
HTTP 1.x主要有以下幾個缺點:
1. HTTP 1.0 只允許在一個 TCP 連接上發送一個請求,HTTP 1.1中默認允許多個 TCP 連接。但是同一個 TCP 連接中,所有的數據通信都是按次序進行的,服務器一般是處理完一個響應之后,再繼續處理下一個。這就造成了隊首阻塞問題。
2. 請求只能從客戶端開始,客戶端不可以接收除了響應之外的指令。
3. 請求/響應頭部不進行壓縮就發送。頭部信息越多延遲就越大。
4. 發送冗長的頭部,每次互相發送相同的頭部導致資源浪費。
5. 可隨意選擇數據壓縮格式,非強制壓縮發送。
SPDY
SPDY 是由谷歌開發的基于 TCP 協議的應用層協議。目標是為了優化 HTTP 協議的性能,通過壓縮、多路復用和優先級技術,縮短網頁的加載時間并提高安全性。SPDY 協議的核心思想是盡量減少 TCP 的連接數。SPDY 并不是一種代替 HTTP 的協議,而是對 HTTP 協議的增強。
SPDY 沒有改寫 HTTP 協議,而是在 TCP/IP的應用層和傳輸層之間通過新加會話層的形式運作。同時,考慮到安全問題,SPDY 規定通信中使用 SSL。
SPDY.jpg
SPDY 以會話層的形式加入,控制對數據的流動,但還是采用 HTTP 建立通信。因此,可以照常使用 HTTP 的請求方法、Cookie 以及 HTTP 報文等等。
HTTP 2.0
HTTP 2.0 可以說是 SPDY 的升級版(其實是基于 SPDY 設計的),但HTTP 2.0和 SPDY 還是存在一些不同的。主要有以下兩點:
- HTTP 2.0 支持明文傳輸,而 SPDY 強制使用 HTTP。
- HTTP 2.0 消息頭的壓縮算法用的是 HPACK,而 SPDY 用的是 DEFLATE。
下面就簡單的介紹一下 HTTP 2.0 新增的功能。由于HTTP 2.0 設計到的東西太多了,之后我會一篇文章單獨講講 HTTP 2.0。
- 二進制分幀層:HTTP 2.0 性能增強的核心就是新增的二進制分幀層,HTTP 1.x是以換行符作為純文本的分隔符,而 HTTP 2.0 把所有傳輸的信息分割成更小的消息和幀,并對它們采用二進制的格式編碼。
- 多向請求和響應:HTTP 2.0 中心的二進制分幀層,將 HTTP 消息分解成獨立的幀,交錯發送。然后在另一個端根據流標識符和頭部把它們重新組裝。解決了 HTTP 1.x的隊首阻塞問題。
- 請求優先級:把 HTTP 消息分解成多個獨立的幀后,就可以通過優化這些幀的交錯和傳輸順序進一步性能優化。
- 服務器推送:服務器可以對一個客戶端請求發送多個響應。服務器還可以向客戶端推送資源而且無需客戶端明確的請求。
- 頭部壓縮:在 HTTP 2.0 中,使用了 HPACK(HTTP2頭部壓縮算法)壓縮格式對傳輸的頭部進行編碼,減少了頭部的大小。并在兩端維護了索引表,用于記錄出現過的頭部,之后在傳輸過程中就可以傳輸已經記錄過的頭部健名,對端收到數據后就可以通過鍵名找到對應的值。
如果想要了解更多 HTTP2.0 的知識可以看看《Web性能權威指南》這本書,里面講得挺詳細的。
補充
OSI模型
網絡架構模型除了TCP/IP模型之外,還有OSI模型。OSI模型實際上是多了三層。
OSI模型.png
也就是上面所說的多加了 SSL 和 SPDY 這兩個協議(都處于應用層)。
而數據鏈路層細分的話有兩層:
- 數據鏈路層: 在不可靠的物理鏈路上,提供可靠的數據傳輸服務。包括組幀、物理編址、流量控制、差錯控制、接入控制等等。
- 物理層: 主要功能就是連接網絡設備。
Cookie
前面也說到,HTTP 是無狀態的協議,它不會對之前發送過的請求和響應的狀態進行管理。假設,客戶端的用戶發送一個請求,服務器端收到請求后想知道這個請求是哪個家伙發過來的,那么就要有一個狀態進行管理。而Cookie正是解決這類問題而出現。
從服務器端返回的響應頭信息中有一個Set-Cookie的字段信息,告訴客戶端保存Cookie。在下次客戶端向服務器端發送請求時,客戶端會自動在請求頭信息中加入Cookie的值發送出去。
服務器收到客戶端發送過來的Cookie之后,會檢查到底是從哪個客戶端發送過的請求,然后對比服務器上的記錄,最后得到了之前的狀態信息。
Set-Cookie
Set-Cookie是屬于響應頭中的一個字段,它包含以下的值。
- NAME=VALUE: Cookie的名稱和值。
- expires=DATE: Cookie的有效期。
- path=PATH: 把服務器上的文件目錄作為Cookie的使用對象,如果沒有設置,默認是文檔所在的文件目錄。
- domain=域名: 作為Cookie適用對象的域名,如果沒有設置,默認是創建Cookie的服務器的域名。
- Secure: 只有在 HTTPS 時才會發送Cookie。
- HttpOnly: JavaScript 不能訪問Cookie。主要是為了防止跨站腳本攻擊時Cookie的信息竊取。
請求頭中的Cookie字段
Cookie是請求頭中的一個字段,它包含服務器通過Set-Cookie頭部設置并存到客戶端的值。如果接收多個Cookie時,可以以多個Cookie形式發送回去。
cookie.jpg
最后
以上是我一個多月在學 HTTP 相關書籍做的一些筆記。哪里有不對的地方,請各位大佬多多指點!