關於Https中的headers的理解

什麼是 HTTP Headers?

什麼是HTTP Headers

HTTP是“Hypertext Transfer Protocol”的所寫,整個萬維網都在使用這種協議,幾乎你在瀏覽器裏看到的大部分內容都是通過http協議來傳輸的,比如這篇文章。

HTTP Headers是HTTP請求和相應的核心,它承載了關於客戶端瀏覽器,請求頁面,服務器等相關的信息。

示例

當你在瀏覽器地址欄裏鍵入一個url,你的瀏覽器將會類似如下的http請求:

GET /tutorials/other/top-20-mysql-best-practices/ HTTP/1.1

Host: net.tutsplus.com

User-Agent: Mozilla/5.0 (Windows;U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: en-us,en;q=0.5

Accept-Encoding: gzip,deflate

Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7

Keep-Alive: 300

Connection: keep-alive

Cookie: PHPSESSID=r2t5uvjq435r4q7ib3vtdjq120

Pragma: no-cache

Cache-Control: no-cache

第一行被稱爲“Request Line” 它描述的是這個請求的基本信息,剩下的就是HTTP headers了。

請求完成之後,你的瀏覽器可能會收到如下的HTTP響應:

HTTP/1.x 200 OK

Transfer-Encoding: chunked

Date: Sat, 28 Nov 2009 04:36:25 GMT

Server: LiteSpeed

Connection: close

X-Powered-By: W3 Total Cache/0.8

Pragma: public

Expires: Sat, 28 Nov 2009 05:36:25 GMT

Etag: "pub1259380237;gz"

Cache-Control: max-age=3600, public

Content-Type: text/html; charset=UTF-8

Last-Modified: Sat, 28 Nov 2009 03:50:37 GMT

X-Pingback: http://net.tutsplus.com/xmlrpc.php

Content-Encoding: gzip

Vary: Accept-Encoding, Cookie, User-Agent

第一行呢被稱爲“Status Line”,它之後就是http headers,空行完了就開始輸出內容了(在這個案例中是一些html輸出)。

但你查看頁面源代碼卻不能看到HTTP headers,雖然它們同你能看到的東西一起被傳送至瀏覽器了。

這個HTTP請求也發出了一些其它資源的接收請求,例如圖片,css文件,js文件等等。

下面我們來看看細節。

怎樣才能看到HTTP Headers

下面這些FireFox擴展能夠幫助你分析HTTP headers:

1.Firebug

2. Live HTTP Headers

3. 在PHP中:

getallheaders()用來獲取請求頭部. 你也可以使用 $_SERVER 數組.

headers_list()用來獲取響應頭部.

文章下面將會看到一些使用php示範的例子。

HTTP Request 的結構

被稱作“first line”的第一行包含三個部分:

“method” 表明這是何種類型的請求. 最常見的請求類型有 GET, POST 和 HEAD.

“path” 體現的是主機之後的路徑. 例如,當你請求 “http://net.tutsplus.com/tutorials/other/top-20-mysql-best-practices/”時 , path 就會是 “/tutorials/other/top-20-mysql-best-practices/”.

“protocol” 包含有 “HTTP” 和版本號, 現代瀏覽器都會使用1.1.

剩下的部分每行都是一個“Name:Value”對。它們包含了各式各樣關於請求和你瀏覽器的信息。例如”User-Agent“就表明了你的瀏覽器版本和你所用的操作系統。”Accept-Encoding“會告訴服務器你的瀏覽可以接受類似gzip的壓縮輸出。

這些headers大部分都是可選的。HTTP 請求甚至可以被精簡成這樣子:

GET /tutorials/other/top-20-mysql-best-practices/ HTTP/1.1

Host: net.tutsplus.com

並且你仍舊可以從服務器收到有效的響應。

請求類型

三種最常見的請求類型是:GET,POST 和 HEAD ,從html的編寫過程中你可能已經熟悉了前兩種。

GET:獲取一個文檔

大部分被傳輸到瀏覽器的html, images, js, css, … 都是通過GET方法發出請求的。它是獲取數據的主要方法。

例如,要獲取Nettuts+ 的文章,http request的第一行通常看起來是這樣的:

GET /tutorials/other/top-20-mysql-best-practices/ HTTP/1.1

一旦html加載完成,瀏覽器將會發送GET 請求去獲取圖片,就像下面這樣:

GET /wp-content/themes/tuts_theme/images/header_bg_tall.png HTTP/1.1

表單也可以通過GET方法發送,下面是個例子:

First Name:

Last Name:

當這個表單被提交時,HTTP request 就會像這樣:

GET /foo.phpfirst_name=John&last_name=Doe&action=Submit HTTP/1.1

...

你可以將表單輸入通過附加進查詢字符串的方式發送至服務器。

POST:發送數據至服務器

儘管你可以通過GET方法將數據附加到url中傳送給服務器,但在很多情況下使用POST發送數據給服務器更加合適。通過GET發送大量數據是不現實的,它有一定的侷限性。

用POST請求來發送表單數據是普遍的做法。我們來吧上面的例子改造成使用POST方式:

First Name:

Last Name:

提交這個表單會創建一個如下的HTTP 請求:

POST /foo.php HTTP/1.1

Host: localhost

User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: en-us,en;q=0.5

Accept-Encoding: gzip,deflate

Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7

Keep-Alive: 300

Connection: keep-alive

Referer: http://localhost/test.php

Content-Type: application/x-www-form-urlencoded

Content-Length: 43

first_name=John&last_name=Doe&action=Submit

這裏有三個需要注意的地方:

第一行的路徑已經變爲簡單的 /foo.php , 已經沒了查詢字符串。

新增了 Content-Type 和 Content-Lenght 頭部,它提供了發送信息的相關信息.

所有數據都在headers之後,以查詢字符串的形式被髮送.

POST方式的請求也可用在AJAX,應用程序,cURL … 之上。並且所有的文件上傳表單都被要求使用POST方式。

HEAD:接收頭部信息

HEAD和GET很相似,只不過HEAD不接受HTTP響應的內容部分。當你發送了一個HEAD請求,那就意味着你只對HTTP頭部感興趣,而不是文檔本身。

這個方法可以讓瀏覽器判斷頁面是否被修改過,從而控制緩存。也可判斷所請求的文檔是否存在。

例如,假如你的網站上有很多鏈接,那麼你就可以簡單的給他們分別發送HEAD請求來判斷是否存在死鏈,這比使用GET要快很多。

HTTP響應結構

當瀏覽器發送了HTTP請求之後,服務器就會通過一個HTTP response來響應這個請求。如果不關心內容,那麼這個請求看起來會是這樣的:

第一個有價值的信息就是協議。目前服務器都會使用 HTTP/1.x 或者 HTTP/1.1。

接下來一個簡短的信息代表狀態。代碼200意味着我們的請求已經發送成功了,服務器將會返回給我們所請求的文檔,在頭部信息之後。

我們都見過“404”頁面。當我向服務器請求一個不存在的路徑時,服務器就用用404來代替200響應我們。

餘下的響應內容和HTTP請求相似。這些內容是關於服務器軟件的,頁面/文件何時被修改過,mime type 等等…

同樣,這些頭部信息也是可選的。

HTTP狀態碼

200 用來表示請求成功.

300 來表示重定向.

400 用來表示請求出現問題.

500 用來表示服務器出現問題.

200 成功 (OK)

前文已經提到,200是用來表示請求成功的。

206 部分內容 (Partial Content)

如果一個應用只請求某範圍之內的文件,那麼就會返回206.

這通常被用來進行下載管理,斷點續傳或者文件分塊下載。

404 沒有找到 (Not Found)

很容易理解

401 未經授權 (Unauthorized)

受密碼保護的頁面會返回這個狀態。如果你沒有輸入正確的密碼,那麼你就會在瀏覽器中看到如下的信息:

注意這只是受密碼保護頁面,請求輸入密碼的彈出框是下面這個樣子的:

403 被禁止(Forbidden)

如果你沒有權限訪問某個頁面,那麼就會返回403狀態。這種情況通常會發生在你試圖打開一個沒有index頁面的文件夾。如果服務器設置不允許查看目錄內容,那麼你就會看到403錯誤。

其它一些一些方式也會發送權限限制,例如你可以通過IP地址進行阻止,這需要一些htaccess的協助。

order allow,deny

deny from 192.168.44.201

deny from 224.39.163.12

deny from 172.16.7.92

allow from all

302(或307)臨時移動(Moved Temporarily) 和 301 永久移動(Moved Permanently)

這兩個狀態會出現在瀏覽器重定向時。例如,你使用了類似 bit.ly 的網址縮短服務。這也是它們如何獲知誰點擊了他們鏈接的方法。

302和301對於瀏覽器來說是非常相似的,但對於搜索引擎爬蟲就有一些差別。打個比方,如果你的網站正在維護,那麼你就會將客戶端瀏覽器用302重定向到另外一個地址。搜索引擎爬蟲就會在將來重新索引你的頁面。但是如果你使用了301重定向,這就等於你告訴了搜索引擎爬蟲:你的網站已經永久的移動到了新的地址。

500 服務器錯誤(Internal Server Error)

這個代碼通常會在頁面腳本崩潰時出現。大部分CGI腳本都不會像PHP那樣輸出錯誤信息給瀏覽器。如果出現了致命的錯誤,它們只會發送一個500的狀態碼。這時需要查看服務器錯誤日誌來排錯。

完整的列表

你可以在這裏找到完整的HTTP 狀態碼說明。

HTTP Headers 中的 HTTP請求

現在我們來看一些在HTTP headers中常見的HTTP請求信息。

所有這些頭部信息都可以在PHP的$_SERVER數組中找到。你也可以用getallheaders()函數一次性獲取所有的頭部信息。

Host

一個HTTP請求會發送至一個特定的IP地址,但是大部分服務器都有在同一IP地址下託管多個網站的能力,那麼服務器必須知道瀏覽器請求的是哪個域名下的資源。

Host: rlog.cn

只是基本的主機名,包含域名和子級域名。

在PHP中,可以通過$_SERVER['HTTP_HOST'] 或 $_SERVER['SERVER_NAME']來查看。

User-Agent

User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)

這個頭部可以攜帶如下幾條信息:

瀏覽器名和版本號.

操作系統名和版本號.

默認語言.

這就是某些網站用來收集訪客信息的一般手段。例如,你可以判斷訪客是否在使用手機訪問你的網站,然後決定是否將他們引導至一個在低分辨率下表現良好的移動網站。

在PHP中,可以通過 $_SERVER['HTTP_USER_AGENT'] 來獲取User-Agent

if ( strstr($_SERVER['HTTP_USER_AGENT'],'MSIE 6') ) {

echo "Please stop using IE6!";

}

Accept-Language

Accept-Language: en-us,en;q=0.5

這個信息可以說明用戶的默認語言設置。如果網站有不同的語言版本,那麼就可以通過這個信息來重定向用戶的瀏覽器。

它可以通過逗號分割來攜帶多國語言。第一個會是首選的語言,其它語言會攜帶一個“q”值,來表示用戶對該語言的喜好程度(0~1)。

在PHP中用 $_SERVER["HTTP_ACCEPT_LANGUAGE"] 來獲取這一信息。

if (substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2) == 'fr') {

header('Location: http://french.mydomain.com');

}

Accept-Encoding

Accept-Encoding: gzip,deflate

大部分的現代瀏覽器都支持gzip壓縮,並會把這一信息報告給服務器。這時服務器就會壓縮過的HTML發送給瀏覽器。這可以減少近80%的文件大小,以節省下載時間和帶寬。

在PHP中可以使用 $_SERVER["HTTP_ACCEPT_ENCODING"] 獲取該信息。 然後調用ob_gzhandler()方法時會自動檢測該值,所以你無需手動檢測。

// enables output buffering

// and all output is compressed if the browser supports it

ob_start('ob_gzhandler');

If-Modified-Since

如果一個頁面已經在你的瀏覽器中被緩存,那麼你下次瀏覽時瀏覽器將會檢測文檔是否被修改過,那麼它就會發送這樣的頭部:

If-Modified-Since: Sat, 28 Nov 2009 06:38:19 GMT

如果自從這個時間以來未被修改過,那麼服務器將會返回“304 Not Modified”,而且不會再返回內容。瀏覽器將自動去緩存中讀取內容

在PHP中,可以用$_SERVER['HTTP_IF_MODIFIED_SINCE'] 來檢測。

//assume $last_modify_time was the last the output was updated

// did the browser send If-Modified-Since header

if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {

//if the browser cache matches the modify time

if($last_modify_time==strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {

//send a 304 header, and no content

header("HTTP/1.1 304 Not Modified");

exit;

}

}

還有一個叫Etag的HTTP頭信息,它被用來確定緩存的信息是否正確,稍後我們將會解釋它。

Cookie

顧名思義,他會發送你瀏覽器中存儲的Cookie信息給服務器。

Cookie: PHPSESSID=r2t5uvjq435r4q7ib3vtdjq120; foo=bar

它是用分號分割的一組名值對。Cookie也可以包含session id。

在PHP中,單一的Cookie可以訪問$_COOKIE數組獲得。你可以直接用$_SESSION array獲取session變量。如果你需要session id,那麼你可以使用session_id()函數代替cookie。

echo$_COOKIE['foo'];

//output: bar

echo$_COOKIE['PHPSESSID'];

//output: r2t5uvjq435r4q7ib3vtdjq120

session_start();

echosession_id();

//output: r2t5uvjq435r4q7ib3vtdjq120

Referer

顧名思義, 頭部將會包含referring url信息。

例如,我訪問Nettuts+的主頁並點擊了一個鏈接,這個頭部信息將會發送到瀏覽器:

Referer: http://net.tutsplus.com/

在PHP中,可以通過 $_SERVER['HTTP_REFERER'] 獲取該值。

if(isset($_SERVER['HTTP_REFERER'])) {

$url_info=parse_url($_SERVER['HTTP_REFERER']);

//is the surfer coming from Google

if($url_info['host'] == 'www.google.com') {

parse_str($url_info['query'],$vars);

echo"You searched on Google for this keyword: ".$vars['q'];

}

}

//if the referring url was:

// http://www.google.com/searchsource=ig&hl=en&rlz=&=&q=http+headers&aq=f&oq=&aqi=g-p1g9

// the output will be:

// You searched on Google for this keyword: http headers

You may have noticed the word “referrer” is misspelledas“referer”. Unfortunately it made into the official HTTP specifications like that and got stuck.

Authorization

當一個頁面需要授權,瀏覽器就會彈出一個登陸窗口,輸入正確的帳號後,瀏覽器會發送一個HTTP請求,但此時會包含這樣一個頭部:

Authorization: Basic bXl1c2VyOm15cGFzcw==

包含在頭部的這部分信息是base64 encoded。例如,base64_decode(‘bXl1c2VyOm15cGFzcw==’) 會被轉化爲 ‘myuser:mypass’ 。

在PHP中,這個值可以用$_SERVER['PHP_AUTH_USER'] 和 $_SERVER['PHP_AUTH_PW'] 獲得。

更多細節我們會在WWW-Authenticate部分講解。

HTTP Headers 中的 HTTP響應

現在讓我瞭解一些常見的HTTP Headers中的HTTP響應信息。

在PHP中,你可以通過 header()來設置頭部響應信息。PHP已經自動發送了一些必要的頭部信息,如 載入的內容,設置 cookies 等等… 你可以通過 headers_list()函數看到已發送和將要發送的頭部信息。你也可以使用headers_sent()函數來檢查頭部信息是否已經被髮送。

Cache-Control

w3.org 的定義是:“The Cache-Control general-header field is used to specify directives which MUST be obeyed by all caching mechanisms along the request/response chain.” 其中“caching mechanisms” 包含一些你ISP可能會用到的 網關和代理信息。

例如:

Cache-Control: max-age=3600, public

“public”意味着這個響應可以被任何人緩存,“max-age” 則表明了該緩存有效的秒數。允許你的網站被緩存降大大減少下載時間和帶寬,同時也提高的瀏覽器的載入速度。

也可以通過設置 “no-cache” 指令來禁止緩存:

Cache-Control: no-cache

更多詳情請參見w3.org。

Content-Type

這個頭部包含了文檔的”mime-type”。瀏覽器將會依據該參數決定如何對文檔進行解析。例如,一個html頁面(或者有html輸出的php頁面)將會返回這樣的東西:

Content-Type: text/html; charset=UTF-8

‘text’ 是文檔類型,‘html’則是文檔子類型。 這個頭部還包括了更多信息,例如 charset。

如果是一個圖片,將會發送這樣的響應:

Content-Type: image/gif

瀏覽器可以通過mime-type來決定使用外部程序還是自身擴展來打開該文檔。如下的例子降調用Adobe Reader:

Content-Type: application/pdf

直接載入,Apache通常會自動判斷文檔的mime-type並且添加合適的信息到頭部去。並且大部分瀏覽器都有一定程度的容錯,在頭部未提供或者錯誤提供該信息的情況下它會去自動檢測mime-type。

你可以在這裏找到一個常用mime-type列表。

在PHP中你可以通過 finfo_file()來檢測文件的ime-type。

Content-Disposition

這個頭部信息將告訴瀏覽器打開一個文件下載窗口,而不是試圖解析該響應的內容。例如:

Content-Disposition: attachment;filename="download.zip"

他會導致瀏覽器出現這樣的對話框:

注意,適合它的Content-Type頭信息同時也會被髮送

Content-Type: application/zip

Content-Disposition: attachment;filename="download.zip"

Content-Length

當內容將要被傳輸到瀏覽器時,服務器可以通過該頭部告知瀏覽器將要傳送文件的大小(bytes)。

Content-Length: 89123

對於文件下載來說這個信息相當的有用。這就是爲什麼瀏覽器知道下載進度的原因。

例如,這裏我寫了一段虛擬腳本,來模擬一個慢速下載。

//it's a zip file

header('Content-Type: application/zip');

//1 million bytes (about 1megabyte)

header('Content-Length: 1000000');

//load a download dialogue, and save it as download.zip

header('Content-Disposition: attachment; filename="download.zip"');

//1000 times 1000 bytes of data

for($i= 0;$i< 1000;$i++) {

echostr_repeat(".",1000);

//sleep to slow down the download

usleep(50000);

}

結果將會是這樣的:

現在,我將Content-Length頭部註釋掉:

//it's a zip file

header('Content-Type: application/zip');

//the browser won't know the size

// header('Content-Length: 1000000');

// load a download dialogue, and save it as download.zip

header('Content-Disposition: attachment; filename="download.zip"');

//1000 times 1000 bytes of data

for($i= 0;$i< 1000;$i++) {

echostr_repeat(".",1000);

//sleep to slow down the download

usleep(50000);

}

結果就變成了這樣:

這個瀏覽器只會告訴你已下載了多少,但不會告訴你總共需要下載多少。而且進度條也不會顯示進度。

Etag

這是另一個爲緩存而產生的頭部信息。它看起來會是這樣:

Etag: "pub1259380237;gz"

服務器可能會將該信息和每個被髮送文件一起響應給瀏覽器。該值可以包含文檔的最後修改日期,文件大小或者文件校驗和。瀏覽會把它和所接收到的文檔一起緩存。下一次當瀏覽器再次請求同一文件時將會發送如下的HTTP請求:

If-None-Match: "pub1259380237;gz"

如果所請求的文檔Etag值和它一致,服務器將會發送304狀態碼,而不是2oo。並且不返回內容。瀏覽器此時就會從緩存加載該文件。

Last-Modified

顧名思義,這個頭部信息用GMT格式表明了文檔的最後修改時間:

Last-Modified: Sat, 28 Nov 2009 03:50:37 GMT

$modify_time = filemtime($file);

header("Last-Modified: " . gmdate("D, d M Y H:i:s", $modify_time) . " GMT");

它提供了另一種緩存機制。瀏覽器可能會發送這樣的請求:

If-Modified-Since: Sat, 28 Nov 2009 06:38:19 GMT

在If-Modified-Since一節我們已經討論過了。

Location

這個頭部是用來重定向的。如果響應代碼爲 301 或者 302 ,服務器就必須發送該頭部。例如,當你訪問 http://www.nettuts.com 時瀏覽器就會收到如下的響應:

HTTP/1.x 301 Moved Permanently

...

Location: http://net.tutsplus.com/

...

在PHP中你可以通過這種方式對訪客重定向:

header('Location: http://net.tutsplus.com/');

默認會發送302狀態碼,如果你想發送301,就這樣寫:

header('Location: http://net.tutsplus.com/', true, 301);

Set-Cookie

當一個網站需要設置或者更新你瀏覽的cookie信息時,它就會使用這樣的頭部:

Set-Cookie: skin=noskin;path=/; domain=.amazon.com; expires=Sun, 29-Nov-2009 21:42:28 GMT

Set-Cookie: session-id=120-7333518-8165026; path=/; domain=.amazon.com; expires=Sat Feb 27 08:00:00 2010 GMT

每個cookie會作爲單獨的一條頭部信息。注意,通過js設置cookie將不會體現在HTTP頭中。

在PHP中,你可以通過setcookie()函數來設置cookie,PHP會發送合適的HTTP 頭。

setcookie("TestCookie", "foobar");

它會發送這樣的頭信息:

Set-Cookie: TestCookie=foobar

如果未指定到期時間,cookie就會在瀏覽器關閉後被刪除。

WWW-Authenticate

一個網站可能會通過HTTP發送這個頭部信息來驗證用戶。當瀏覽器看到頭部有這個響應時就會打開一個彈出窗。

WWW-Authenticate: Basic realm="Restricted Area"

它會看起來像這樣:

在PHP手冊的一章中就有一段簡單的代碼演示瞭如果用PHP做這樣的事情:

if(!isset($_SERVER['PHP_AUTH_USER'])) {

header('WWW-Authenticate: Basic realm="My Realm"');

header('HTTP/1.0 401 Unauthorized');

echo'Text to send if user hits Cancel button';

exit;

}else{

echo"

Hello {$_SERVER['PHP_AUTH_USER']}.

";

echo"

You entered {$_SERVER['PHP_AUTH_PW']} as your password.

";

}

Content-Encoding

這個頭部通常會在返回內容被壓縮時設置。

Content-Encoding: gzip

在PHP中,如果你調用了ob_gzhandler()函數,這個頭部將會自動被設置。

發佈了11 篇原創文章 · 獲贊 0 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章