首先了解下瀏覽器輸入url後http請求返回的過程是什麼,看下圖javascript
(一) 首先一開始要作 redirect
重定向,那麼爲何要 redirect 呢,由於瀏覽器可能記錄了你這個地址已經永久跳轉成一個新的地址,因此一開始瀏覽器須要判斷需不須要 redirect 以及 redirect 到哪裏。css
(二) 看緩存,請求的資源可能已經緩存過,在 App cache 裏看是否有緩存,若是沒有緩存,就會去服務器請求資源。html
(三) 輸入域名,域名會對應ip以後才能真正訪問到服務器,因此這時候會先去查找域名對應的IP地址,這就叫DNS解析前端
(四) 有了IP以後,就會建立tcp鏈接,該過程要通過tcp的三次握手以後才能真正建立鏈接。同時若是這個請求是https的,就會建立https的連接,這跟tcp的三次握手不同,中間會有一個保證安全的數據傳輸的過程。java
(五) 鏈接建立好以後,纔會真正發起http請求的數據包,數據包發送完成以後,服務器接收到這個數據並進行處理以後會返回這個請求響應的內容數據,返回數據以後這個http請求才真正完成node
咱們先來看下經典五層模型圖例:nginx
這個五層模型中,分爲應用層、傳輸層、網絡層、數據鏈路層、物理層,每個服務器上都會有這樣的層級關係存在來維護整個網絡數據傳輸的過程。web
本文主要內容是http相關內容,因此主要是在應用層展開。http協議基於傳輸層裏的 TCP/IP 協議,該協議會涉及到一些http請求的性能,以及請求過程的消耗,會面也會有一些 TCP/IP 協議的介紹。ajax
低三層:算法
傳輸層主要有兩個協議,一個是 TCP/IP,一個是 UDP。更多狀況下都使用的是 TCP/IP 協議,由於它更可靠的傳輸數據。
向用戶提供可靠的端到端(e2e)服務
我的電腦到網絡服務器創建鏈接以後,若是傳輸數據很大,一次性沒法完成傳輸,就須要分片傳輸,傳輸成功以後再從新組裝。兩端傳輸數據的方式都是在這一層定義
傳輸層向高層屏蔽了下層數據通訊的細節
http協議要傳輸數據只需在瀏覽器輸入url,輸入url這個過程還涉及到一系列數據的拼裝及傳輸,好比分包傳輸具體是怎麼實現,服務器怎麼接收,ajax請求,整個過程傳輸層已經作好了封裝,這個過程用戶不須要知道。
http協議所在層
頭信息壓縮及推送
http2 解決了 http 裏總體性能低下的問題,在http1.1裏,每次發送請求和返回請求,它的http頭都會進行一個完整傳輸,而且不少字段都是以字符串形式保存,佔用大量帶寬。http2裏會將頭信息進行壓縮傳輸。
推送是什麼概念呢?在http1.1裏,客戶端發起請求而後服務端響應請求返回內容,客戶端永遠是主動方,服務端永遠是被動方。在http2裏,服務端是能夠主動發起數據傳輸的。好比:一個html頁面中引入了css和js,瀏覽器首先要對html進行分析,再尋找css和js對應的url去請求對應的文件,這就涉及到一個順序問題,須要先請求到html文本,在瀏覽器裏運行解析了這個文本以後才能發送css及js的請求。可是在http2中服務端能夠主動把css及js文件推送到客戶端,與html並行傳輸,極大提升傳輸效率
在客戶端和服務端之間進行http請求的發送和返回的過程中,須要建立一個 TCP connection。http只有請求和響應這個概念,不存在鏈接,請求和響應都是數據包, 之間要通過一個傳輸的通道,這個傳輸的通道就是在tcp裏建立的一個鏈接(TCP connection)。這個鏈接能夠保持狀態,http請求就是在這個鏈接之上發送的,因此在一個tcp鏈接上能夠發送多個http請求。
在不一樣版本下,http鏈接的模式不同,在http1.0裏,這個鏈接是在http請求建立同時建立tcp鏈接,請求結束後tcp鏈接就會關閉。
在http1.1裏,這個鏈接能夠經過某種方式聲明是否保持鏈接狀態。tcp鏈接在建立過程當中有三次握手消耗,三次握手就表示三次網絡傳輸(客戶端發送 - 服務端響應 - 客戶端再次發送),而後才能發送http請求。若是tcp鏈接一直保持,第二個http請求就沒有三次握手的開銷
在http2裏不只能夠保持tcp的鏈接,同時這個鏈接上的http請求能夠併發,就是說同一個用戶對同一個服務器發起一個網頁請求時只須要一個tcp鏈接。
(一) 在tcp的三次握手當中,客戶端會向服務端發起一個建立鏈接的數據包請求,這裏會有一個標識爲 SYN=1
,SYN是一個標誌位,表示建立請求的數據包。後面會發送一個叫 Seq=X
,X表示數字,通常爲1。服務端接收到這個數據包以後就會知道要建立一個鏈接
(二) 建立鏈接以後,服務端就會開啓一個 tcp 端口,返回給客戶端數據,這個數據 SYN=1,ACK=X+1,Seq=Y
,客戶端拿到這個數據表示服務端容許建立這個tcp鏈接。
(三) 這時候客戶端再去發送 ACK=Y+1,Seq=Z
。
那麼爲何tcp要進行三次握手呢,這是爲了防止服務端開啓一些無用鏈接,網絡鏈接具備延時性。客戶端向服務端發起建立鏈接請求時,服務端直接建立了這個鏈接,返回的數據包由於網絡緣由丟失,那麼客戶端就一直接收不到服務器返回的數據,鏈接超時這個鏈接就會關閉,而後再發起新的鏈接,若是沒有三次握手的話,這時服務端是不知道客戶端到底有沒有接收到返回的數據,浪費服務端的開銷。因此須要三次握手去即時的察覺到網絡緣由致使的數據包傳輸中斷的問題。
在http協議中,基本上使用的都是URL。
從圖中能夠看到http首部下面有一段空行,空行下面表示 http body 部分,上面就是 http headers 部分。
(一) GET
http請求頭中,首行第一部分包含的是 method
請求方法,每一個方法有各自的語義,分別是 GET(獲取數據)、POST(建立數據)、PUT(更新數據)、DELETE(刪除數據)。這幾種方法只是語義上的說明,並無強約束,好比使用 GET 方法去更新數據,只是這樣違反了http語義化的定義規則。
(二) /test/hi-there.txt
首行第二部分是請求的資源資源地址url,通常這裏是存放路由相關的內容
(三) HTTP/1.0
首行第三部分是http的版本,如今的web服務通常都是http1.1,http2也會有愈來愈多的實現,不一樣的版本也會有不一樣操做方式。
(一) 200 ok
http狀態碼,200 表明成功
GET - 請求指定的頁面信息,並返回實體主體。
HEAD - 相似於get請求,只不過返回的響應中沒有具體的內容,用於獲取報頭
POST - 向指定資源提交數據進行處理請求(例如提交表單或者上傳文件)。數據被包含在請求體中。POST請求可能會致使新的資源的創建和/或已有資源的修改。
PUT - 從客戶端向服務器傳送的數據取代指定的文檔的內容。
DELETE - 請求服務器刪除指定的頁面。
複製代碼
HTTP Status:
1xx(臨時響應)
表示臨時響應並須要請求者繼續執行操做的狀態代碼。
代碼 說明
100 (繼續) 請求者應當繼續提出請求。 服務器返回此代碼表示已收到請求的第一部分,正在等待其他部分。
101 (切換協議) 請求者已要求服務器切換協議,服務器已確認並準備切換。
2xx (成功)
表示成功處理了請求的狀態代碼。
代碼 說明
200 (成功) 服務器已成功處理了請求。 一般,這表示服務器提供了請求的網頁。
201 (已建立) 請求成功而且服務器建立了新的資源。
202 (已接受) 服務器已接受請求,但還沒有處理。
203 (非受權信息) 服務器已成功處理了請求,但返回的信息可能來自另外一來源。
204 (無內容) 服務器成功處理了請求,但沒有返回任何內容。
205 (重置內容) 服務器成功處理了請求,但沒有返回任何內容。
206 (部份內容) 服務器成功處理了部分 GET 請求。
3xx (重定向)
表示要完成請求,須要進一步操做。 一般,這些狀態代碼用來重定向。
代碼 說明
300 (多種選擇) 針對請求,服務器可執行多種操做。 服務器可根據請求者 (user agent) 選擇一項操做,或提供操做列表供請求者選擇。
301 (永久移動) 請求的網頁已永久移動到新位置。 服務器返回此響應(對 GET 或 HEAD 請求的響應)時,會自動將請求者轉到新位置。
302 (臨時移動) 服務器目前從不一樣位置的網頁響應請求,但請求者應繼續使用原有位置來進行之後的請求。
303 (查看其餘位置) 請求者應當對不一樣的位置使用單獨的 GET 請求來檢索響應時,服務器返回此代碼。
304 (未修改) 自從上次請求後,請求的網頁未修改過。 服務器返回此響應時,不會返回網頁內容。
305 (使用代理) 請求者只能使用代理訪問請求的網頁。 若是服務器返回此響應,還表示請求者應使用代理。
307 (臨時重定向) 服務器目前從不一樣位置的網頁響應請求,但請求者應繼續使用原有位置來進行之後的請求。
4xx(請求錯誤)
這些狀態代碼表示請求可能出錯,妨礙了服務器的處理。
代碼 說明
400 (錯誤請求) 服務器不理解請求的語法。
401 (未受權) 請求要求身份驗證。 對於須要登陸的網頁,服務器可能返回此響應。
403 (禁止) 服務器拒絕請求。
404 (未找到) 服務器找不到請求的網頁。
405 (方法禁用) 禁用請求中指定的方法。
406 (不接受) 沒法使用請求的內容特性響應請求的網頁。
407 (須要代理受權) 此狀態代碼與 401(未受權)相似,但指定請求者應當受權使用代理。
408 (請求超時) 服務器等候請求時發生超時。
409 (衝突) 服務器在完成請求時發生衝突。 服務器必須在響應中包含有關衝突的信息。
410 (已刪除) 若是請求的資源已永久刪除,服務器就會返回此響應。
411 (須要有效長度) 服務器不接受不含有效內容長度標頭字段的請求。
412 (未知足前提條件) 服務器未知足請求者在請求中設置的其中一個前提條件。
413 (請求實體過大) 服務器沒法處理請求,由於請求實體過大,超出服務器的處理能力。
414 (請求的 URI 過長) 請求的 URI(一般爲網址)過長,服務器沒法處理。
415 (不支持的媒體類型) 請求的格式不受請求頁面的支持。
416 (請求範圍不符合要求) 若是頁面沒法提供請求的範圍,則服務器會返回此狀態代碼。
417 (未知足指望值) 服務器未知足」指望」請求標頭字段的要求。
5xx(服務器錯誤)
這些狀態代碼表示服務器在嘗試處理請求時發生內部錯誤。 這些錯誤多是服務器自己的錯誤,而不是請求出錯。
代碼 說明
500 (服務器內部錯誤) 服務器遇到錯誤,沒法完成請求。
501 (還沒有實施) 服務器不具有完成請求的功能。 例如,服務器沒法識別請求方法時可能會返回此代碼。
502 (錯誤網關) 服務器做爲網關或代理,從上游服務器收到無效響應。
503 (服務不可用) 服務器目前沒法使用(因爲超載或停機維護)。 一般,這只是暫時狀態。
504 (網關超時) 服務器做爲網關或代理,可是沒有及時從上游服務器收到請求。
505 (HTTP 版本不受支持) 服務器不支持請求中所用的 HTTP 協議版本。
複製代碼
瀏覽器就是咱們最經常使用的http客戶端
同時 curl 能夠查看http請求返回的內容
curl還能夠查看請求的詳細內容, curl -v [host]
作過前端開發的同窗對跨域並不會陌生,一般咱們可使用jsonp去實現跨域請求。
jsonp跨域:
<script src="http://www.example.com:8080"></script>
複製代碼
原理:
瀏覽器容許 link、img、script 標籤上寫入路徑加載一些內容的時候,是容許跨域的。
服務端設置跨域:
這裏以express爲例,咱們只需在響應頭中添加 Access-Control-Allow-Origin
便可
response.writeHead(200, {
'Access-Control-Allow-Origin': '*'
})
複製代碼
瀏覽器在發送請求的時候並不知道服務是否跨域,仍是會發送請求而且接收返回內容,只是在瀏覽器接收內容的時候沒有找到 Access-Control-Allow-Origin
頭設置爲容許的話,它會把請求返回的內容忽略掉而且會在服務端報錯。這個是瀏覽器所提供的功能。
實際上 Access-Control-Allow-Origin 值爲 * 是不安全的,這樣會致使第三方服務也能夠經過跨域訪問你的服務,能夠設置爲特定的域名
response.writeHead(200, {
'Access-Control-Allow-Origin': 'http://xxxx.com'
})
複製代碼
瀏覽器是根據 header 判斷某個請求的返回是否容許,若是想要容許自定義的頭進行發送的話,須要返回新的頭告知瀏覽器容許
response.writeHead(200, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'X-Test-Cors'
})
複製代碼
同時會發現 network 中多出一個請求,這就是預請求,它的 Request Method
爲 OPTIONS
,服務端能夠根據不一樣method進行不一樣的操做,瀏覽器根據這個 OPTIONS 請求,來得到服務端容許的權限,接下來發送的它所承認的請求就會被容許,這就是瀏覽器對於跨域請求的預請求操做。
(一) 在跨域請求中,默認容許的方法只有 GET、HEAD、POST,其它的方法默認不容許,瀏覽器是有一個預請求的方式去驗證的。
response.writeHead(200, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'PUT'
})
複製代碼
還能夠設置某個請求下容許跨域的最大時間,這樣就不須要再次發送預請求去驗證,可直接發送正式請求
response.writeHead(200, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'PUT',
'Access-Control-Max-Age': '1000'
})
複製代碼
(二) 默認容許的Content-Type:text/plain、multipart/form-data、application/x-www-form-urlencoded。這三個就是在html裏使用form表單能夠設置的三種數據類型。其它的也須要預請求驗證過以後才能進行發送。
(三) 其它的請求頭限制具體能夠查看文檔 https://fetch.spec.whatwg.org/#cors-safelisted-request-header
(四) XMLHttpRequestUpload
對象均沒有註冊任何事件監聽器
(五) 請求中沒有使用 ReadableStream
對象
public
(任何地方都會緩存)private
(發起請求的瀏覽器)no-cache
能夠在本地進行緩存,可是每次發起請求都要在服務端驗證,若是服務端容許使用本地緩存,才能真正使用本地緩存。max-age = <seconds>
s-maxage = <seconds>
在代理服務器中才會生效,代理服務器中若是兩個都設置了,會優先選擇 s-maxage 配置項max-stale = <secon ds>
發起端設置。即使緩存失效,只要這個時間內還可使用過時的緩存,而不須要去原服務器請求新的內容。must-revalidate
在設置了max-age緩存中若是過時,必須去原服務端發送請求而後從新獲取數據再來驗證內容是否真的過時,而不能直接使用本地緩存。proxy-revalidate
和 must-revalidate 相似,可是用在緩存服務器當中。no-store
與 no-cache 對應,表示任何狀況下都不會存儲緩存,永遠都要去服務端請求新的內容才能使用它。即使服務端容許使用緩存,但本地沒有進行緩存no-transform
用在 proxy 服務器,有些proxy服務器返回資源過大,會幫助進行壓縮及格式轉換,該屬性會不容許。沒有緩存狀況下請求資源
設置客戶端緩存後
response.writeHead(200, {
'Content-Type': 'text/javascript',
'Cache-Control': 'max-age=200'
})
response.end('console.log("script loaded")')
複製代碼
能夠看到 Size 變爲了 == (from memory cache) ==,表示從瀏覽器中讀取緩存。
在作前端開發的時候,有些靜態資源文件咱們但願瀏覽器緩存下來,可是在服務端內容更新以後,客戶端請求的是緩存下的舊資源文件,這樣就無法更新應用。
目前最多見的方式就是前端編譯的時候加靜態資源文件md5戳。
首先看一張圖
瀏覽器建立請求,請求到達本地緩存(若是有cache-control),若是有本地緩存,就直接返回給瀏覽器顯示出來,不通過任何網絡傳輸。若是沒有本地緩存,請求進入網絡傳輸,若是有代理服務器就會進入並查找緩存設置,查看資源是否被緩存,有緩存就返回緩存資源通過本地緩存再到瀏覽器顯示。若是代理服務器未緩存,就會進入原服務器獲取新的內容再返回。
瀏覽器在請求資源的headers裏有 Last-Modified
這個頭,並指定了時間,這個時間內下次瀏覽器發起請求時就會帶上 Last-Modified
傳入的值,經過 If-Modified-Since
或 If-Unmodified-Since
(一般爲If-Modified-Since)帶到服務器上,服務器經過讀取 headers裏If-Modified-Since 帶入的值找到資源存在的地方對比上次修改的時間,若是時間同樣,就表示資源沒有被從新修改過,服務器就通知瀏覽器直接使用緩存的資源,這就是資源驗證的過程。
有任何的修改兩個簽名就會不同,最典型的作法就是對資源內容作哈希計算,計算以後會獲得一個惟一值,用這個簽名來標記這個資源,下一次瀏覽器發起請求時會帶上 If-Match或者If-Non-Match頭,這個頭的值就是服務端返回Etag的值,而後對比服務器拿到瀏覽器傳入的簽名和服務器存在的簽名,若是相同,就不須要返回新的內容。
Cookie
Cookie屬性
// Cookie設置,以express爲例
response.writeHead(200, {
'Content-Type': 'text/html',
'Set-Cookie': 'name=hello'
})
// 設置多個
'Set-Cookie': ['name=hello', 'age=12']
// 過時時間
'Set-Cookie': ['name=hello; max-age=2', 'age=12']
// 禁止js訪問cookie
'Set-Cookie': ['name=hello', 'age=12; HttpOnly']
// 子域名共享主域名cookie,前提是cookie要在主域名下設置
'Set-Cookie': ['name=hello', 'age=12; domain=example.com']
複製代碼
Session
session機制是一種服務器端的機制,服務器使用一種相似於散列表的結構(也可能就是使用散列表)來保存信息。
當程序須要爲某個客戶端的請求建立一個session的時候,服務器首先檢查這個客戶端的請求裏是否已包含了一個session標識 - 稱爲session id,若是已包含一個session id則說明之前已經爲此客戶端建立過session,服務器就按照session id把這個session檢索出來使用(若是檢索不到,可能會新建一個),若是客戶端請求不包含session id,則爲此客戶端建立一個session而且生成一個與此session相關聯的session id,session id的值應該是一個既不會重複,又不容易被找到規律以仿造的字符串,這個session id將被在本次響應中返回給客戶端保存。
保存這個session id的方式能夠採用cookie,這樣在交互過程當中瀏覽器能夠自動的按照規則把這個標識發揮給服務器。通常這個cookie的名字都是相似於SEEESIONID,而。好比weblogic對於web應用程序生成的cookie,JSESSIONID=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是JSESSIONID。
因爲cookie能夠被人爲的禁止,必須有其餘機制以便在cookie被禁止時仍然可以把session id傳遞迴服務器。常常被使用的一種技術叫作URL重寫,就是把session id直接附加在URL路徑的後面,附加方式也有兩種,一種是做爲URL路徑的附加信息,表現形式爲http://...../xxx;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
另外一種是做爲查詢字符串附加在URL後面,表現形式爲http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
這兩種方式對於用戶來講是沒有區別的,只是服務器在解析的時候處理的方式不一樣,採用第一種方式也有利於把session id的信息和正常程序參數區分開來。爲了在整個交互過程當中始終保持狀態,就必須在每一個客戶端可能請求的路徑後面都包含這個session id。
另外一種技術叫作表單隱藏字段。就是服務器會自動修改表單,添加一個隱藏字段,以便在表單提交時可以把session id傳遞迴服務器。好比下面的表單:
<form name="testform" action="/xxx">
<input type="text">
</form>
複製代碼
在被傳遞給客戶端以前將被改寫成:
<form name="testform" action="/xxx">
<input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
<input type="text">
</form>
複製代碼
這種技術如今已較少應用,實際上這種技術能夠簡單的用對action應用URL重寫來代替。
http的請求是在tcp的鏈接上進行發送的,tcp鏈接又分爲長鏈接和短鏈接。
長鏈接就是在tcp鏈接上把http請求發送並接收返回,這個時候一次http請求已經結束,瀏覽器和服務器會協商是否斷掉這個鏈接,長鏈接就是在不斷掉鏈接下能夠持續發送http請求,適合高併發。
咱們但願在加載網站首頁的時候能夠併發處理這些請求,瀏覽器能夠容許產生一個併發的建立tcp鏈接,chrome容許最大併發數爲6。
能夠看到,併發狀況下會建立不一樣的tcp鏈接,chrome若是超出了6個併發,後面的請求會等待前面的完成,而且會盡可能複用前面的鏈接地址而保持長鏈接,這是瀏覽器默認的行爲。
能夠手動關閉長鏈接
response.writeHead(200, {
'Content-Type': 'image/jpg',
'Connection': 'close'
})
複製代碼
關閉長鏈接以後能夠發現每次鏈接id都會不同,後面的也會等待前面的完成,沒有重複利用tcp鏈接,每次請求發送完成tcp鏈接就會關閉。
通常狀況下keep-alive都是開啓的,而且會設置一個自動關閉時間。
信道複用
在tcp鏈接上併發的發送http請求,也就是說在鏈接一個網站時只需一個tcp鏈接,只在同域下請求有效,http2實現了這個功能。
在客戶端發送給服務端請求的時候,客戶端會聲明這個請求拿到的數據格式以及數據相關的一些限制是怎樣的,服務端會根據客戶端的這個聲明作出判斷,從而返回不一樣的數據類型格式。
分類
Accept裏MIME_types相關對照表看這裏文檔
在開發中,咱們存放資源的位置若是發生了改變,頁面在請求時就會報404錯誤,爲了不這種錯誤,須要幫助瀏覽器指向到正確的地址。
http.createServer(function(req, res) {
if(req.url === '/'){
res.writeHead(302, {
'Location': '/new'
})
res.end('')
}
if(req.url === '/new'){
res.writeHead(200, {
'Content-Type': 'text/html'
})
res.end('<div>web</div>')
}
})
複製代碼
圖中能夠看到第一次請求頁面狀態碼爲302,並跳轉到了new這個url下。
可是 302 是臨時跳轉,每一次訪問都要通過服務端的跳轉,圖中也能夠看到有兩個url的請求。若是肯定每次訪問 / 下都會跳轉到 new 下,能夠指定狀態碼爲 301 永久跳轉,這樣訪問頁面就只出現 new。
須要注意的是,301 會盡量長時間的把跳轉頁面緩存下來,這時候服務端即便修改了url,瀏覽器仍是會從緩存裏讀取,這個是有用戶使用瀏覽器狀況所決定的,除非用戶主動去清理瀏覽器緩存。因此 301 要慎重處理。
Content-Security-Policy內容安全策略,這使得瀏覽器變得更加安全。
做用
限制方式
res.writeHead(200, {
'Content-Type': 'text/html',
'Content-Security-Policy': 'default-src http: https:'
})
複製代碼
加入限制以後瀏覽器就會阻止js腳本的加載並報錯
只容許本站下的資源
res.writeHead(200, {
'Content-Type': 'text/html',
'Content-Security-Policy': 'default-src \'self\''
})
複製代碼
容許某些站點的內容
res.writeHead(200, {
'Content-Type': 'text/html',
'Content-Security-Policy': 'default-src \'self\' http://www.example.com/'
})
複製代碼
限制form表單的跳轉
res.writeHead(200, {
'Content-Type': 'text/html',
'Content-Security-Policy': 'default-src \'self\'; form-action \'self\''
})
複製代碼
詳細內容能夠查看csp文檔 MDN CSP
內容安全策略若是出現了不但願出現的狀況下,能夠申請主動向服務端發起請求來彙報
res.writeHead(200, {
'Content-Type': 'text/html',
'Content-Security-Policy': 'default-src \'self\' report-uri /report'
})
複製代碼
若是咱們只想對限制進行錯誤報告而不阻止資源加載的話,能夠這麼寫
res.writeHead(200, {
'Content-Type': 'text/html',
'Content-Security-Policy-Report-Only': 'default-src \'self\''
})
```
scp不只能夠寫在 headers 裏,還能夠在html的meta標籤裏寫
複製代碼
``` 在meta下是不容許寫 report-uri 的,這個指令只能寫在 headers 裏。
nginx是如今互聯網界用的最多的web服務器,它是一個很是純粹的作http協議實現的服務器,並無一個工具來實現業務邏輯的開發。主要是用來作http的代理服務器。
nginx的安裝和用法能夠查網上相關教程。這裏介紹的主要是nginx的代理和緩存的功能。
一個最簡單的代理
// nginx.conf
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8080;
# proxy_set_header Host $host;
}
}
複製代碼
能夠看到,瀏覽器下host 是 test.com
,但在服務器下就變成了 127.0.0.1:8888
。這是由於設置了代理,瀏覽器請求是發送到nginx的,nginx再進行轉發,發送到實際的node服務,這時候做爲發起方,它認爲的 host 就是這裏設置的 proxy_pass
。
想要拿到瀏覽器的 host 。能夠設置 proxy_set_header
屬性 $host
。
中間代理能夠修改任何想要修改的數據,但只是在http中,https的傳輸過程是加密的,中間代理沒法解析。在手機上所看到的一些移動聯通的廣告就是通過代理層插入了一些代碼所展現的。
// nginx.conf
proxy_cache_path cache levels=1:2 keys_zone=my_cache:10m;
server {
listen 80;
server_name example.com;
location / {
proxy_cache my_cache;
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
}
}
複製代碼
**proxy_cache_path:**第一個選項表示緩存路徑,levels
是否建立二級文件夾,keys_zone
url對應的緩存位置及內存大小
response.writeHead(200, {
'Cache-Control': 'max-age=10, s-maxage=10, private'
})
複製代碼
s-maxage
是專門爲代理緩存設置過時時間的,而private就表示只容許瀏覽器緩存。
https在傳輸過程當中,客戶端會生成一個隨機數傳輸到服務端,中間會帶上一個支持的加密套件,服務端拿到以後保存而且也生成一段隨機數,而後把這段隨機數和服務端生成的證書一同發到客戶端,同時客戶端也會把服務端的隨機數保存,而且經過服務端證書生成預主祕鑰,生成過程也會生成一個隨機數,這個隨機數經過公鑰加密後傳輸給服務端,服務端經過私鑰解密拿到預主祕鑰。而後客戶端和服務端同時對這三個隨機數進行算法解密生成主密鑰(這裏會涉及到加密套件,服務端選擇的加密套件必須是客戶端所支持的),後續的數據傳輸都是通過主密鑰加密進行傳輸的。這對主密鑰只有客戶端和服務端共有,中間代理沒法破解,這就是https的加密原理。
這裏是經過抓取工具抓的https加密的站點,能夠看到,數據都被加密,沒法破解。
要部署https服務,首先要生成一對公鑰和私鑰,這裏有一個命令能夠幫助生成
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -keyout localhost-privkey.pem -out localhost-cert.pem
複製代碼
敲入回車以後能夠看到這樣的提示,這裏咱們測試,所有按回車跳過就好。
最終會生成兩個文件,而後在nginx配置這個證書
// nginx.conf
proxy_cache_path cache levels=1:2 keys_zone=my_cache:10m;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name test.com;
return 302 https://$server_name$request_uri;
}
server {
listen 443;
server_name test.com;
ssl on;
ssl_certificate_key /www/data/cert/localhost-privkey.pem;
ssl_certificate /www/data/cert/localhost-cert.pem;
location / {
proxy_cache my_cache;
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
}
}
複製代碼
配置好以後重啓nginx服務,而後輸入https的域名
提示非安全鏈接是由於chrome瀏覽器認爲的安全證書是要經過有權威的機構去簽發的,這種機構會先認證域名全部者與服務是否屬於你,驗證經過纔會簽發證書。
http2的使用
res.writeHead(200, {
'Content-Type': 'text/html',
'Link': '</test.jpg>; as=image; rel=preload'
})
複製代碼
server 頭信息了的 Link
能夠指定這個頭想要推送的內容,</xxx>
爲文件絕對路徑,as
指定文件類型,preload
表示須要進行服務端推送。
nginx裏也要作這些配置。在使用nginx作反向代理時,咱們但願nginx幫助處理這些東西,而 http2 也是在nginx裏提供的,node server 仍是爲http的服務,nginx 會把http2的請求轉化爲http的請求發送到node服務上。
爲什麼不在node上作http2的服務呢,由於在nginx開啓一個http2的服務是很是容易的,在 node 上作http2的服務可能還會涉及到大量的邏輯修改,成本開銷比較大。
須要注意的是,目前只有在https下才能開啓http2
在nginx下開啓http2很簡單。
// nginx.conf
proxy_cache_path cache levels=1:2 keys_zone=my_cache:10m;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name test.com;
return 302 https://$server_name$request_uri;
}
server {
listen 443 http2;
server_name test.com;
http2_push_preload on;
ssl on;
ssl_certificate_key /www/data/cert/localhost-privkey.pem;
ssl_certificate /www/data/cert/localhost-cert.pem;
location / {
proxy_cache my_cache;
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
}
}
複製代碼
http2_push_preload
開啓以後,在接收 node 返回信息裏若是有 Link:rel=preload
,就會去尋找該路徑資源,而後主動推送。
能夠看到 Protocal
值爲 h2
,這個就是http2的縮寫。
這裏有一個能夠測試http2性能的網站 網站入口
能夠看到,使用HTTP2的性能提高很是顯著。
有些瀏覽器不支持http2,nginx會幫助瀏覽器作兼容處理,這個兼容方案爲 ALPN ,客戶端跟服務端會進行協商用哪一個協議,若是客戶端只支持http1.1,服務端就會以http1.1的傳輸方式進行。
以上就是對http知識的總結,不少地方沒有寫全,有很差的地方請你們多多指正