HTTP

HTTP 狀態碼 (Status Codes)

HTTP 回應狀態碼 (Response Status Codes)
是由 3 個十進位整數 組合而成 (即 百位數字),
是為了方便 Client 理解請求的結果。
 
(3-1) 訊息格式 範例,
回應狀態碼 (Response Status Codes): 200
其後伴隨 原因短語 (OK):

HTTP/1.1 200 OK

Content-Type: text/html; charset=utf-8 Date: Sat, 18 Feb 2017 00:01:57 GMT Server: nginx/1.11.8 transfer-encoding: chunked Connection: Close

 

<!doctype html> <html> <head> <meta charset="utf-8"> <title>echo</title> ....略

 

Photo by HTTP Cats.
 


 

目錄

分類

狀態碼 (Response Status Codes)
依首位數字,分為五大類:

  • 1xx (Informational) — 資訊
  • 2xx (Successful) — 成功
  • 3xx (Redirection) — 重新導向
  • 4xx (Client Error) — 客戶端 錯誤
  • 5xx (Server Error) — 伺服端 錯誤

 
常見如:

  • 200 (OK),代表請求成功。
  • 302 (Found),代表 目標資源 (target resource) 暫時存在於不同的 URI。
  • 404 (Not Found),代表在 源伺服器 (Origin Server),
    找不到目標資源現有的表示 (或其實有,不願透露存在)。

 
HTTP Client 不需理解所有已註冊的狀態碼,
但至少得知道是何種分類 (1xx、2xx、3xx、4xx、5xx) 與涵義。
 
理解分類非常有用,假如開發網站時,遇到 5xx (Server Error)
得知是 伺服端 錯誤,而不需花大量的時間 檢查 Client (許多人犯這錯 😂),
儘管 Client 可能需要 檢查 網路狀況 或 重新載入 (略過快取)。
 

當然,這是以 Server 配置正確 為前提 (i.e., 站在 Client 的立場),
若基本的 rewrite、rule、virtual host… 都配置錯誤,
當然可能跑出 4xx (Client Error) 的 「客戶端」錯誤。

 


 

快取

預設 可快取 (cacheable) 的 狀態碼如下:

200, 203, 204, 206, [226] 300, 301, 308 404, 405, 410, 414, 421 501

 
除非 請求方法定義 或 顯式快取控制 (explicit cache controls) 另有說明,
這些 狀態碼 的 回應,可藉由 啟發式逾時 (heuristic expiration) 快取 重複使用
減少將來 等效請求 的回應時間 與 網路頻寬的消耗,以提升效能 (詳見 (6-1) 快取)。
 
而其他狀態碼,預設皆是 不可快取的
 
 
 
以下開始介紹各分類 與常用的 狀態碼 (完整的狀態碼 由 IANA 維護)。
 


 

1xx (Informational) — 資訊

已收到請求,繼續處理。

 
主要用於:

  • 傳達連接狀態的 臨時 (interim) 回應 (即所謂的 過渡期)。
  • 在完成請求動作,並發送最終回應之前,回報進度。

 
Client 必須能解析在最終回應之前,收到的一或多個 1xx 回應
使用者代理 (User Agent) 可以忽略非預期的 1xx 回應。
 
代理 (proxy) 必須轉發 1xx,除非其本身即生成此回應。
 
p.s.
HTTP/1.0 沒有定義任何 1xx 狀態碼
Server 不得向 HTTP/1.0 的 Client 發送 1xx 狀態碼。
 
 

100 (Continue) — 繼續

請求的初始部分已被接收,且尚未被 Server 拒絕。

 
100 (Continue) 時常與 請求訊息 的 Expect 表頭欄位 搭配使用,
例如,此 Client 發送包含 Expect 欄位之請求:

PUT /somewhere/fun HTTP/1.1 Host: origin.example.com Content-Type: video/h264 Content-Length: 1234567890987

Expect: 100-continue

 
Server 接收到時此訊息後,便可回覆:100 (Continue) — 繼續
 
這往往意味著 Server 期望收到更多 請求酬載 (Payload),以進行完整地處理,
因此 Client 應繼續發送請求 (例如大型檔案的上傳),
當然啦!1XX 只是個「過渡期」,最後 Server 仍得有個『最終回應』。
 
另外,這也有助於 Client 提高一些錯誤處理的效率,
例如上方的請求範例中,若 Sever 根本不同意 User 上傳資料,
則可以直接回應 401 (Unauthorized) — 未授權
405 (Method Not Allowed) — 方法不允許
而非讓 Client 傻傻地上傳完整檔案才拒絕。
 
一個小觀念:

即使 Client 發送包含 Expect: 100-continue 欄位之請求,
不需要等待 Server 回應 或任何特定時間長,而可以繼續發送請求!

 
 

101 (Switching Protocols) — 切換通訊協定

Server 理解並願意遵守 Client 透過 Upgrade 表頭欄位 切換通訊協定 的請求。

 
Client 與 Server 藉由 Upgrade 表頭欄位,指示欲切換的通訊協定,
通常僅在有利時,伺服器才進行切換。 (ex: 切換到新版本的 HTTP)
 
 

HTTP/2

如 Client 不清楚 Server 是否支持 HTTP/2
即可使用表頭欄位:

Upgrade: h2c

 
不支持 HTTP/2 的 Server,可以無視
就像 Upgrade 表頭欄位不存在一樣。
 
而支持 HTTP/2 的 Server,則回應:

HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: h2c [ HTTP/2 connection ...

 
 

WebSocket

支援 雙向資料傳輸 的 WebSocket 協定,
便是透過此方法建立連線:

Upgrade: websocket

 
例如,Client 請求切換 WebSocket 通訊協定:

GET /?encoding=text HTTP/1.1

Host: echo.websocket.org User-Agent: Test Origin: https://www.websocket.org Sec-WebSocket-Version: 13 Sec-WebSocket-Key: pRJwQiljvtRd5ixyROZalA== Upgrade: websocket

 

 
Server 允許並回應 101 (Switching Protocols) — 切換通訊協定
101 (Web Socket Protocol Handshake)

HTTP/1.1 101 Web Socket Protocol Handshake

Access-Control-Allow-Credentials: true Access-Control-Allow-Headers: content-type Access-Control-Allow-Headers: authorization Access-Control-Allow-Headers: x-websocket-extensions Access-Control-Allow-Headers: x-websocket-version Access-Control-Allow-Headers: x-websocket-protocol Access-Control-Allow-Origin: https://www.websocket.org Connection: Upgrade Date: Thu, 20 Jul 2017 10:11:24 GMT Sec-WebSocket-Accept: xRJEM77wy8Eya9zeu/HvwQs02jI= Server: Kaazing Gateway Upgrade: websocket

 

 
除外 101 之外的任何狀態碼 (即使 200 也一樣),
皆代表 WebSocket 交握尚未完成,此時協議保持為 HTTP。
 
 
(HTTP/2 與 WebSocket 不在本篇討論範圍,不加以累敘)
 


 

2xx (Successful) — 成功

已收到、理解並接受請求。

 
 

200 (OK)

請求已經成功。

 
200 (OK) 是最常見的成功狀態碼,
例如,瀏覽網頁時,成功看到畫面 (不考慮重新導向),幾乎都是 200:

 
200-OK
 
回應訊息中的 酬載 (payload) 內容,
取決於當初請求時 使用的 請求方法 (request method),例如:
 

請求 使用 GET 方法
回應 的 酬載,預期為 目標資源 的 表示 (representation)。
例如,最普遍的 網頁形式 (text/html)
請求 使用 POST 方法
回應 的 酬載,預期為 (server) 執行的狀態或結果。

 
排除 CONNECT 方法以外,所有 200(OK) 回應訊息 意義上 都該含有酬載 (儘管 長度為 0),
若沒有 酬載 (payload),源伺服器 應 發送 204(No Content) 替代。
 
 

201 (Created) — 建立

請求已經滿足,且建立了一或多個新的資源。

 
由該請求建立的主要資源,
由回應中的 Location 表頭欄位 或 有效請求 URI 標示。
 
201 回應 的 內容,通常描述並鏈接到所建立的資源。
也可以進一步提供 ETag 表頭欄位,提供 Client 後續進行 快取驗證,
以防止 遺失更新 (lost update) 的問題。
 
 

202 (Accepted) — 接受

Server 已經接受請求,但尚未處理完成。

 
請求不一定會處理完成,因處理過程中可能出現例外。
不論是否成功,HTTP/1.1 無法 從異步操作中 重新發送 回應狀態碼。
 
202 (Accepted) 主要是為了 讓 使用者代理 (User Agent) 與 Server 之間,
不用將 連線持續到 處理完成。
 
202 回應 的內容,通常描述請求目前的狀態,
或估計完成的進度、時間…。
 
 

203 (Non-Authoritative Information)
— 非權威資訊

請求已經成功,但回應內容已由 代理 (proxy) 變更。

 
此狀態碼可讓 代理 (proxy) 通知 回應接收者 (Client 或 另一個代理):「原始訊息內容已變更」,
這可能有利於實作某些內容決策,例如 連接鏈 (chain of connection) 的快取驗證。
 
 

204 (No Content) — 沒有內容

請求已經成功,且沒有需要回應的內容。

 
一個常見的使用情境是:線上文件編輯器的『存檔 (save)』動作,
存擋成功時 Server 可回應 204 (No Content) 指示已成功存檔。
 
由上例可得知,204 (No Content) 通常隱含著:
使用者代理 (user agent) 不需移走目前的頁面,
User 通常能繼續操作 (e.g., 存檔後可繼續編輯文件)。
 
如下範例所示,204 回應 不可 包含 訊息主體 (message body):

HHTTP/1.1 204 No Content

Server: nginx/1.10.3 (Ubuntu) Date: Sun, 28 Jan 2018 14:22:19 GMT

 

 
 
另一個常見功能是:『元資料 (Metadata) 的更新』,
例如,使用者發送 PUT 請求上傳檔案,若成功,Server 可在 204 回應 中加入 ETag 欄位
如此一來,使用者代理 (user agent) 便能保存該 ETag 作為往後 快取驗證 之用!
 
 

205 (Reset Content) — 重設內容

Server 已完成請求,並希望 使用者代理 (user agent) 重置:
「導致請求被發送 的 『文件視圖 (document view)』」至原始狀態。

 
例如,User 提交表單後,使用者代理 接收到 205 回應
意味著 Server 希望 使用者代理 (通常是 瀏覽器) 把 表單內容清空
(可能為方便新表單的提交,又或者 純粹美觀 🧐)
 
其他用例:

  • Canvas 初始化
  • UI 更新
  • ……

(恩…User Agent 目前對於 205 支援度相當差,大部分得搭配 JS 實作)
 
另外,205 回應 也隱含著 Server 並沒有要提供額外內容,
因此,Server 不得 在 205 回應 中生成 酬載 (payload)。
(i.e., 訊息主體不得有資料)
 
 

206 (Partial Content) — 部分內容

Server 藉由請求中的 Range 表頭欄位
成功獲取 並傳輸 一或多個滿足該範圍的 目標資源 之 表示 (representation)。

 
Client 可以使用 Range 表頭欄位,將 GET 語義用於 範圍請求 (range request),
使 Server 僅傳輸所選 表示 (representation) 的一部分。
(e.g., 大型文件 的單個頁面)
 
 

傳輸單個部分

生成 206 回應 的 Server,必須生成一個 Content-Range 表頭欄位,
描述 選擇表示所包含的範圍,與由 該範圍的組成的 酬載 (payload),例如:

HTTP/1.1 206 Partial Content Date: Wed, 15 Nov 1995 06:25:24 GMT Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT

Content-Range: bytes 21010-47021/47022

Content-Length: 26012 Content-Type: image/gif ...26012 bytes of partial image data...

 
 

傳輸多個部分

生成 206 回應 的 Server,不得 生成 Content-Range 表頭欄位,
而是使用 「多部分」的 內容類型 (Content-Type),例如:

Content-Type: multipart/byteranges; boundary=THIS_STRING_SEPARATES

詳見 (4-2) GET vs. POST
 
訊息範例:

HTTP/1.1 206 Partial Content

Date: Wed, 15 Nov 1995 06:25:24 GMT Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT Content-Length: 1741 Content-Type: multipart/byteranges; boundary=THIS_STRING_SEPARATES

 

--THIS_STRING_SEPARATES Content-Type: application/pdf Content-Range: bytes 500-999/8000 ...the first range... --THIS_STRING_SEPARATES Content-Type: application/pdf Content-Range: bytes 7000-7999/8000 ...the second range --THIS_STRING_SEPARATES--

 
 

207 (Multi-Status) — 多個狀態

[WebDAV] 用於為多個 獨立操作 提供不同狀態。

 
207 (Multi-Status)WebDAV 於 2007 年引入。
例如:

HTTP/1.1 207 Multi-Status

Content-Type: application/xml; charset="utf-8" Content-Length: xxxx

 

<?xml version="1.0" encoding="utf-8" ?> <D:multistatus xmlns:D="DAV:" xmlns:Z="https://ns.example.com/standards/z39.50/"> <D:response> <D:href>https://www.example.com/bar.html</D:href> <D:propstat> <D:prop><Z:Authors/></D:prop> <D:status>HTTP/1.1 424 Failed Dependency</D:status> </D:propstat> <D:propstat> <D:prop><Z:Copyright-Owner/></D:prop> <D:status>HTTP/1.1 409 Conflict</D:status> </D:propstat> <D:responsedescription> Copyright Owner cannot be deleted or altered.</D:responsedescription> </D:response> </D:multistatus>

詳見 RFC4918
 
 

208 (Already Reported) — 已經回報

[WebDAV] 可用於 DAV:propstat 回應元素中,
以避免重複枚舉多個綁定的內部成員至相同的集合。

 
propstat XML 元素,可參考 RFC4918 section14.22
208 之使用範例,可參考 RFC5842 section7.1.1
 
208 (Already Reported)RFC5842 於 2010 年引入。
 
 

226 (IM Used) — 已使用實例操作

Server 已完成對該資源的 GET 請求,且回應內容是:
對目前實例 使用一或多個 實例操作 (IM) 結果的 表示 (representation)。

 
226 回應 主要用於 差分編碼 (Delta encoding),意指:
Server 於回應中包含目前版本與舊版本間的 差異 (delta),
Client 即可藉此從快取中修正這些差異,並得到新版本 (類似版本控制的 merge),
如此一來便能節省頻寬,增進效能! (i.e., Server 不需重新傳送一個酬載)
 
實例操作 (instance-manipulations, IM) 則是指
差分編碼 (delta encoding) 或 範圍選擇 (range selection)。
 
概念流程下:

datatype operation leading to next datatype ======== ================================== resource | choose acceptable variant, if needed v variant | apply content-coding, if any v | compute/assign entity tag v instance | apply instance manipulation, if any v (delta encoding, range selection, etc.) entity-body | apply transfer-coding, if any v message-body

 
回應範例:

HTTP/1.1 226 IM Used

Date: Wed, 24 Dec 1997 14:01:00 GMT Delta-base: "abc" Etag: "ghi" IM: vcdiff

 

(當然,Server 必須回應 ETag 欄位,以供後續進行 快取驗證)
 
226 (IM Used)RFC3229 於 2002 年引入。
 


 

3xx (Redirection) — 重新導向

使用者代理 (User Agent) 需採取進一步行動,以完成請求。

 
多數時候,Server 應在 3xx 回應中,生成 Location 表頭欄位
包含引用該資源的首選 URI,以供 Client 重新導向,
且回應的 酬載 (payload) 內容,通常包含該 URI 的超連結。
(e.g., <a href"https://example.com">)
 
 

Location 表頭欄位

Location回應 (response) 表頭欄位中的 控制資料類 (Control Data),
使用範疇非常廣泛 (e.g., 使用者登入、新增資源 後導向)。
 
下例,使用者 將被導向至 https://www.facebook.com/ :

HTTP/1.1 301 Moved Permanently

Date: Wed, 15 Nov 1995 06:25:24 GMT

Location: https://www.facebook.com/

Content-Length: 87 ...略...

 
若該 3xx 回應 提供了 Location 表頭欄位,即使特定的 3xx 狀態碼 未被理解 (e.g., 378 🤔 ?),
使用者代理 (User Agent) 可自動將請求 重新導向 到 Location 表頭欄位 引用的 URI。
 
使用者代理 (User Agent) 自動 重新導向時,得仔細檢查請求是否為 安全方法
並小心惡意的 無窮迴圈 重新導向 (e.g., A站 -> B站 -> A站 -> … )。
 
以 PHP 使用 Location 表頭欄位,模擬登入跳轉為例:

<?php
header("Location: https://example.com"); // header 之前不能有任何輸出

die(); // 若無使用 die(); 程式會繼續往下執行

logOut(); // 若無使用 die(); 則使用者將永遠「登入」不了 xd

?>

 
Web Server 的使用,詳見 301302 節。
 
 

其他導向方式

需注意的是,Location 表頭欄位
不等價 JavaScript 的 Location 物件 或 HTML 的 Meta refresh
分辨的方式很簡單,記住 :

表頭欄位 不會 出現在 HTML 中 !

 
譬如以下的敘述,同樣能將瀏覽器 重新導向至 新頁面,
回應訊息 (Response Message) 並不會有 Location 表頭欄位 (除非混用)。
 
JavaScript:

<script>
    location = "https://example.com";
</script>

<script>
    location.replace("https://example.com");
</script>

 
 
Meta Refresh (官方不建議使用):

<meta http-equiv="refresh" content="0; url=https://example.com/" />

 
 
當然,扯更遠的「開新視窗」自然跟 Location 表頭欄位 無關喔!
許多惱人的 彈出式廣告,即藉此方式出現 😡,
因此,大部分瀏覽器會制定約束,讓使用者在發出動作時 (e.g., 點擊 button、link),才允許彈出視窗:

<script>
    window.open("https://example.com");
</script>

 
 

301 (Moved Permanently) — 永久移動

目標資源 已被分配到一個新的永久 URI,
將來任何對該資源的引用,都應使用此 新 URI

 
301 是最常見的重新導向之一,若您想「永久地」導向一個頁面 (e.g., 網頁搬家) 那就使用它吧!
它會告訴 搜索引擎Bot 舊連結將不再可用,因此不該被索引。
 
通常使用「301」導向頁面,能使 SEO 的影響最小
這也正是 Google Search Console 網站欲變更網址的必備步驟:

 
PHP 301 導向範例:

<?php
header("HTTP/1.1 301 Moved Permanently");
header("Location: https://example.com");
?>

 
 

302 (Found) — 找到

目標資源 已被分配到一個新的 暫時 URI,
將來任何對該資源的引用,都應使用 原有效 URI

 
由於 302 是『臨時』的重新導向,
若您想進行網頁搬家,請勿 使用此方式。
 
然而,302 的『暫時』特性,有利於許多 Services 之實作,
例如,常見的授權框架 OAuth2,便可以 302 來取得 Client 之 授權碼 (authorization code)

HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz

 
PHP 302 導向範例:

<?php
header("Location: https://example.com");
?>

 
 

301 vs. 302

301 (Moved Permanently)302 (Found) 的不同在於:
302 的 重新導向 URI 有可能改變
 
這便是為何,301 回應 允許被 快取 (因轉址連結永久有效),
而 302 預設為 不可快取的。
 
例如:

https://facebook.com/

永久 (301) 轉為:

https://www.facebook.com/

 

https://www.google.com.tw

暫時 (302) 轉為:

https://www.google.com.tw/?gfe_rd=cr&ei=XXYYZZ or https://www.google.com.tw/?gws_rd=ssl

 
可分別使用 Web Server 送出 301 (Moved Permanently)302 (Found)
 
Nginx:

if ($scheme = http) {
    return 301 https://$host$request_uri;
}

 
Apache:

<IfModule mod_ssl.c>
    RewriteEngine On
    RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R,L]
</IfModule>

 
另外,由於歷史原因,301、302 重新導向時,
使用者代理 (User Agent) 可能將 請求方法從 POST 更改為 GET
若不希望出現此問題,可分別改用 308 (永久重新導向)307 (臨時重新導向) 狀態碼。
 
 

303 (See Other) — 參閱其他

Server 將 使用者代理 (User Agent),
重新導向至回應訊息中 Location 表頭欄位 所含的 URI。

 
Location 表頭欄位 中的 URI,旨在對原始請求提供間接回應,
使用者代理 (User Agent) 可執行對該 URI 的檢索請求 (GET or HEAD),
並將 最終結果 (有可能繼續重新導向) 作為原始請求的答案。
 
注意,該 URI 並非 有效請求 URI (effective request URI)。
 
303 (See Other) 狀態碼適用於任何 HTTP 方法,但多數時候用於 POST 請求,如 (4-3) GET vs. POST:

如果處理 POST 請求 的結果,等同於某個存在資源的 表示 (representation),
則 源伺服器 可以通過在 Location 表頭欄位 中,
發送具有該資源的 辨識符 (URI) 的 303 (See Other) — 參閱其他 回應,
將 使用者代理 (User Agent) 重新導向到該資源。

 
用於 GET 方法,說明 源伺服器 (Origin Server),
沒有可藉由 HTTP 傳輸的 目標資源 之 表示 (representation),
回應中的 Location 表頭欄位 可能是對 Client 有益的其他資源。
 
 

304 (Not Modified) — 未修改

已接收的 GET 或 HEAD 條件請求 (conditional request),將使 Server 回應 200 (OK)。
[若條件評估非假 (false) ]

 
條件請求 (conditional request) 是一種 HTTP 請求,其包括一或多個 表頭欄位,
指示在應用方法語義於目標資源之前,要測試的 先決條件 (precondition)
以確定該快取 (回應) 是否等效於資源目前的 表示 (representation)
其中,條件 GET 請求HTTP 快取更新 (cache updates) 的主要有效機制
 

304 回應 不得 包含訊息主體,它始終終止於表頭之後的第一個空行 (CRLF)。

 
304 (Modified) 主要用於 快取 (cache)
換句話說,因為該 條件請求 (conditional request) 指出:「客戶端 已經具有有效的 表示」,
Server 不需要重新處理與傳輸目標資源的 表示 (representation),
而是將 Client 重新導向已儲存的表示 (快取),就像是 200 (OK) 回應 的酬載一樣。
 
就像冰箱裡有雞蛋 (快取),老媽 (X) 我 (O) 就不需要出門買啦 !
 

 
Server 生成 304 (Modified) 時,
必須 包含 以下任一的表頭欄位,如同回覆 200 (OK) 一般:

Cache-Control, Content-Location, Date, ETag, Expires, and Vary.

 
304 回應 的目標是:

使已快取一或多個 表示 (representation) 的 Client,達到最小化的傳輸。

 
因此,除了上述的表頭欄位,或需更新快取資訊,
(e.g., 如果回應沒有 ETag 表頭欄位,則 Last-Modified 可能會很有用。)
Server 不應生成其他 表示元資料 (representation metadata)
 
當擁有 新鮮快取的 Client 發送 條件請求代理 (proxy)
代理 應送出 304 (Modified) 回應。
httpcachehit
Photo by Varnish.
 
 

305 (Use Proxy) — 使用代理

由於 代理 (Proxy) 頻內設置的安全性問題,此狀態碼 已棄用
 
 

307 (Temporary Redirect) — 暫時重新導向

目標資源 僅 暫時 駐留在不同的 URI 下,
將來任何對該資源的引用,都應使用 原有效 URI

 
307 (Temporary Redirect)302 (Found) 很相似,主要差別在於:
使用者代理 (user agent) 對於 307 回應之重新導向,不得變更 請求方法 (Request Method)
(基於歷史原因,對 302 之重新導向,通常會將 請求方法從 POST 更改為 GET)
 
以 Nginx 配置為例:

location /old/url/ {
    return 307 /new/url;
}

 
[註]
此特性對一些 Web Services 之實作與測試非常有用 😉。
 
 

308 (Permanent Redirect) — 永久重新導向

目標資源 已 永久 分派給不同的 URI,
將來任何對該資源的引用,都應使用此 新 URI

 
308 (Permanent Redirect)301 (Moved Permanently) 的關係,之於「307 與 302」:
對於 308 之重新導向,使用者代理 (user agent) 不得改變原請求方法 (e.g., POST 改 GET)。
 
該規範定義於 RFC7538
 


 

4xx (Client Error) — 客戶端 錯誤

請求包含錯誤的語法 或 無法達成。

 
除非是 酬載 (payload) 內容的回應 (e.g., HEAD),
Server 應該發送一個 表示 (representation),其中包含關於 錯誤情況的說明,
以及是否為臨時或永久條件。
 
 

400 (Bad Request) — 錯誤的請求

請求被認定有誤,Server 不能 or 不會處理該請求。

 
例: 格式錯誤的請求語法、欺騙性的請求路由…。
 
 

401 (Unauthorized) — 未授權

請求未被採用,因其缺少對目標資源的 有效驗證憑證 (credential),
或其憑證無效,且被拒絕。

 
收到 401 (Unauthorized) 是 Server 告訴你:
「你沒有通過 身份驗證,或根本沒有進行身份驗證、驗證不正確,麻煩再重新 驗證 (authenticate)。」
—— Daniel Irvine
 
為了讓 Client 知道如何進行身份驗證,
Server 必須於 401 回應 中生成 WWW-Authenticate 表頭欄位
其中,包含至少一個對於目標資源的 盤問/提示 (challenge)
 
例如:

HTTP/1.1 401 Unauthorized

Server: nginx/1.10.3 (Ubuntu) Date: Sun, 28 Jan 2018 21:30:06 GMT Content-Type: text/html; charset=utf-8 Content-Length: 196 WWW-Authenticate: Basic realm="my app"

 

<html> <head><title>401 Authorization Required</title></head> <body bgcolor="white"> <center><h1>401 Authorization Required</h1></center> <hr><center>nginx/1.10.3 (Ubuntu)</center> </body> </html>

[註]:realm (範圍) 用於指出受保護的空間 (Protection Space)
 
Safari 得到該訊息後,將顯示 Basic 驗證提示視窗:

 
然而,這只是其中一種驗證方式,其他方式請參考 MDN
 
 

402 (Payment Required) — 需要付款

此狀態碼保留中,供未來使用。

 
 

403 (Forbidden) — 禁止

Server 了解 但 拒絕授權 該請求。

 
收到 403 (Forbidden) 是 Server 告訴你:
「很抱歉,我知道你是誰、也相信你的身份,但你沒有訪問這個資源的權限。
去問問系統管理員,或許會得到許可 🤔? 總之,除非情況有所改變,請 不 要 再 煩 我!」
—— Daniel Irvine
 
例: Server 禁止訪客存取某頁面,即可使用 403 (Forbidden)
403-forbidden
 
若 Server 好心,它可以在 403 回應的 酬載 (e.g., 失敗頁面) 中,告知禁止存取的原因,
若 Server 希望 "隱藏" 禁止的目標資源的 存在,則可以使用 404 (Not Found) 取代。
 
 

401 vs 403

401 (Unauthorized)403 (Forbidden) 的使用困惑了許多人,只須記住:
401 之語意傾向於 驗證錯誤 (authentication failed),例如:帳密輸入錯誤、未提供驗證碼;
403 則為 權限不足 (permission denied),例如:已登入成功,但普通會員想看 VIP 影片 🧐。
 
 

404 (Not Found) — 未找到

源伺服器 未找到 目標資源 目前的 表示 (representation),
不願透露 資源的 存在 (如 403 所述)。

 
某個頁面 或 載點 失效,最常看到它 😢:

 
404 (Not Found) 不代表 資源表示 的缺乏,是暫時或永久性的,
若 源伺服器 確認該條件可能是永久的,則 410 (Gone) 狀態碼優先於 404。
 
儘管 404 (Not Found) 於 RFC 定義中,預設是可快取的,
然而,實務上多數 使用者代理 (e.g., 瀏覽器) 認為 404 可能只是 暫時錯誤,因此並無快取 😎,
(410 (Gone) 則多預設為 可快取)
 
 

配置錯誤

然而,許多開發者的痛是:
「資源確實存在,卻仍出現 404 (Not Found)」。
 
這通常來自 程式撰寫 or Web Server 配置 錯誤,
此時,不妨檢查一下路由設置,可能有驚喜的結果 😆。

 
以 Java 為例:
除了 URL Mapping,也別疏忽 請求方法 囉!

@RequestMapping(path = "/yout/path/", method = RequestMethod.POST)
public String demo() {
    return "Hello, World!";
}

(此處省略其餘 Annotation,例如 @ResponseBody)
 
當然,有時問題可能來自更上層:

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

 
 
以 Nginx Web Server 為例:
其提供了 最後手段 (last resort) 的 Mapping:

location / {
    # First attempt to serve request as file, then
    # as directory, then fall back to displaying a 404.
    try_files $uri $uri/ =404;
}

 
但別忘了處理其餘內容,或設置 404 處理方式:

location ~ my-dynamic-content {
    ... 略 ...
}

 
或改寫預設 location (適用於 php):

location / {
    try_files $cachefile $uri $uri/ /index.php?$args;
}

 
 

405 (Method Not Allowed) — 方法不被允許

Server 對此 URI (目標資源),不支援該請求方法。

 
源伺服器 必須在 405 回應 中生成 Allow 表頭欄位
其中包含目標資源目前支援的方法列表。
 
 

406 (Not Acceptable) — 無法接受

根據請求訊息中的 主動協商表頭欄位,
目標資源不具有 使用者代理可接受的 目前表示,
並且 Server 不願意提供預設 表示 (representation)。

 
對此,Server 應生成包含可用的 表示列表 於 酬載 (例如,html頁面 或 JSON資料) 中,例:

 
當然,這只是個簡單的範例,表示 (representation) 的範疇應更廣泛:

  • 內容類型 (Content-Type)
  • 內容編碼 (Content-Encoding)
  • 內容語言 (Content-Language)
  • 內容位置 (Content-Location)

 
相關欄位詳見 415 (Unsupported Media Type)
 
 

407 (Proxy Authentication Required) — 需要代理驗證

401 (Unauthorized) 非常類似,不同之處在於:
其指出 Client 需要經過身份驗證,才能夠使用 代理 (Proxy)。

 
 

408 (Request Timeout) — 請求逾時

Server 在預計的等待時間內,並未收到完整的請求訊息。

 
408 (Request Timeout) 意味著 Server 決定 關閉連線,而非繼續等待。
因此,許多時候 Client 根本不會收到 408 回應訊息:
server-disconnet
 
另一種 Server 做法,則是在回應訊息中添加「close」連線選項,
意指 連線將在回應完成後關閉:

HTTP/1.1 408 Request Timeout

Content-Type: text/html Date: Mon, 19 Mar 2018 08:44:32 GMT Server: ECSF (sjc/4E6C) Content-Length: 239 Connection: close

 

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>408 - Request Timeout</title> </head> <body> <h1>408 - Request Timeout</h1> <div>Server timeout waiting for the HTTP request from the client.</div> </body> </html>

 
 

409 (Conflict) — 衝突

由於 請求 與 目標資源目前的狀態 發生衝突,導致請求無法完成。

 
409 (Conflict) 最常發生在對 PUT 請求 的回應中。
例如,當上傳的文件版本與 Server 目前的版本發生衝突時。
 
Server 應於 酬載 (payload) 中提供足夠資訊,以供 User 識別衝突之來源。
 
 

410 (Gone) — 不存在

目標資源 (target resource) 的訪問在源伺服器上不再有效,且這種情況通常是 永久的

 
410 (Gone)404 (Not Found) 很相似,
然而,410 代表的是 源伺服器 對此 URI 未來 也不打算提供任何服務。
若不確定條件是否為永久,則應採用 404 (Not Found)
 
410 回應的主要目的是通知 回應接收方:資源被 有意地 標示為不可用,
且 Server 所有者希望對該資源的遠端連結都能被移除,以協助 Web 維護之任務。
 
 

411 (Length Required) — 需要長度

Server 拒絕接受未定義 Content-Length 表頭欄位 的請求。

 
Client 可以在添加了正確的 Content-Length 表頭欄位 後重新發送請求。
 
 

412 (Precondition Failed) — 前置條件失敗

Server 判定在此 條件請求 (conditional request) 中,至少有一個以上的條件不符合。

 
412 (Precondition Failed) 主要用於快取驗證的 條件請求 (conditional request) 中,
可以防止 Client 錯誤地改變目標資源之狀態 (e.g., Server檔案 與 Client快取 之版本不同)。
 
 

413 (Payload Too Large) — 酬載過大

Server 拒絕處理該請求,因 請求訊息的 酬載 (payload) 超過 Server 願意處理的大小。

 
例如: 上傳檔案至 Server 時,即可能超出大小限制,
而使 Server 回應 413 (Payload Too Large)
且 Server 可能關閉連接,以防止 Client 繼續請求。
 
Client 需選擇較小的檔案 或 調整 Server 的限制大小 以解決,
若僅為臨時條件,Server 應生成 Retry-After 表頭欄位,告知 Client 何時可再次嘗試。
 
[例] Nginx 調整 請求訊息酬載上限 為 20M:

http {
    client_max_body_size 20M;	
}

 
[例] PHP 可編輯 php.ini 以修改最大檔案上傳限制:
max_file_uploads
 
 

414 (URI Too Long) — URI 過長

Server 拒絕服務該請求,因 URI 已超出 Server 願解譯的長度。

 
[註]:原文中是針對請求訊息的 request-target 做長度計算,這裡為方便暸解而使用 URI。
 
至於,URI 到底要多長才算過長,得依 Server 配置而定,
例如 Nginx 的 large_client_header_buffers

http {
  large_client_header_buffers 4 8k;
}

 
 

415 (Unsupported Media Type) — 不支援的媒體類型

源伺服器 拒絕服務請求,因為在格式上酬載無法支援。

 
這通常出現在 內容協商 (Content Negotiation)
請求訊息中 使用 Accept 或 Accept-Encoding 或 其他隱含特徵…等,
指明接受的 內容類型、內容編碼。
 
然而,Server 沒有符合的 表示方式 (representation),
則可能回應 415 或 406 錯誤
 
Client 只能在 協商特徵中 (e.g., Accept、User-Agent),
滿足 Server 的給定條件,並使用該表示方式。
 
例如 api.github.com 僅支援 application/json 格式,
若發送此訊息,將導致 415 錯誤:

GET / HTTP/1.1

Host: api.github.com Accept: application/xml User-Agent: Test

 

 
加上 */* 就解決啦 (因包含 application/json) :

GET / HTTP/1.1

Host: api.github.com Accept: application/xml,*/* User-Agent: Test

 

 
更好的方式是加上 q 值,若支援 xml 了 (q 值預設為 1),即可優先使用 ! (雖然不太可能 🤔):

GET / HTTP/1.1

Host: api.github.com Accept: application/xml,*/*;q=0.9 User-Agent: Test

 

(Accept 用法請見 內容協商)
 
 

416 (Range Not Satisfiable) — 範圍不滿足

請求訊息中的 Range 表頭欄位 範圍,與 Server 該資源目前的範圍 沒有交集、
或者 範圍無效、過多的小範圍請求…,導致該請求被拒絕。

 
206 (Partial Content) 所述:
Client 可以使用 Range 表頭欄位,將 GET 語義用於 範圍請求 (range request),
使 Server 僅傳輸所選 表示 (representation) 的一部分。
(e.g., 大型文件 的單個頁面)
 
若 Server 回應 416 (Range Not Satisfiable) 意味著它無法處理該請求區間。
最常見的情況是,所請求的資料區間不在文件範圍內。
(i.e., Range 欄位的值,雖然語法正確,但沒有任何意義。)
— — MDN
 
對此,Server 應發送 Content-Range 欄位指出所選表示的當前長度 (星號),例如:

HTTP/1.1 416 Range Not Satisfiable

Date: Fri, 20 Jan 2012 15:41:54 GMT Content-Range: bytes */47022

 

 
注意:因為 Server 可以自由忽略請求中的 Range 欄位
所以許多 Server 實作只會回應 200 (OK) 回應並包含『整個』所選表示。
這是因為大多數 Client 都預計接收 200(OK)來完成任務 (儘管效率較低),
部分原因是許多 Client 在接收到完整的 表示 (representation) 前,可能會不斷提出部無效的部分請求。
因此,Client 不能假定將在錯誤的範圍請求中接收到 416,即使它是最合適的。
 
 

417 (Expectation Failed) — 預期失敗

至少有一個 Inbound Server 表示無法滿足 請求訊息的「Expect 表頭欄位」之期望值。

 
更多資訊,可參考 100 (Continue)
 
 

418 (I’m a teapot) — 我是個茶壺

任何企圖以茶壺沖泡咖啡者,皆應回覆 418 錯誤碼
意譯: 目前的伺服器是茶壺,而非咖啡壺,因此拒絕煮咖啡。

 
恩…你可能覺得我到底在公尛。
 
沒關係,別太認真 😂,
事實上,這根本不是正式的 HTTP 回應狀態碼。
 
418 (I’m a teapot) 源自於 超文本咖啡壺控制協定 (HTCPCP)
純粹是個 1998 年的愚人節玩笑 — 惡搞RFC 🤡!
 
然而,它也成了許多網站與後端語言的有趣彩蛋,
甚至在 Mark Nottingham 呼籲移除此狀態碼時,
引起了些許 爭議,而促使了 拯救418 運動 😂。
 
最後,Mark Nottingham 選擇折衷,將 418 註記為:
「保留的 HTTP 狀態碼」,而不會被其他狀態碼所取代。
 
 

421 (Misdirected Request) — 導向錯誤之請求

該請求被導向到無法產生回應的 Server。

 
421 (Misdirected Request) 由 HTTP/2 引入 (RFC 7540),
在 HTTP/2 中,只要 源伺服器 具有 權威性 (authoritative) 與 憑證 (certificate),
儘管 URI 不同,為優化效能,有些連線是允許重複使用的 😎!
 
然而,這也可能引發許多安全性、可維護性問題,
若 Server 不允許 Client 使用重複連線,即可發送 421 回應。
 
當然,所有的 代理 (proxy) 皆不允許生成 421 回應。
 
 

422 (Unprocessable Entity) — 不可處理之實體

[WebDAV] 儘管 Server 理解請求實體 (酬載) 之 內容類型 (content type),
且該請求實體之 語法 (syntax) 也正確,但仍無法處理對該實體內容進行處理。

 
舉例來說:
若 XML 請求主體包含格式正確 (i.e., 語法正確),但語義錯誤的 XML 指令,則可能出現此錯誤情況。
 
由於 Server 已理解 內容類型,因此 415 (Unsupported Media Type) 並不恰當,
又其語法正確,因此 (2007年時) 400 (Bad Request) 也不適用 [註]
於是,422 (Unprocessable Entity)WebDAV 於 2007 年引入。
 
[註]
然而,儘管發生類似情況 (e.g., JSON語法正確,但是語意錯誤),
很多人 (包括我) 仍選擇使用 400 (Bad Request)
因 400 之定義已在 RFC7231 中得到擴充,詳情可參考 此篇
 
 

423 (Locked) — 鎖定

[WebDAV]方法的來源或目標資源已被鎖定。

 
423 (Locked)WebDAV 於 2007 年引入。
對於 423 回應,應包含一個合適的前置或後置條件碼 (WebDAV),例如:
『lock-token-submitted』或『no-conflicting-lock』。
 
 

424 (Failed Dependency) — 錯誤依賴

[WebDAV] 請求的操作 (action) 得取決於另一個操作,而該操作失敗了,
因此該方法無法在該資源上執行。

 
424 (Failed Dependency)WebDAV 於 2007 年引入,詳見 section-11.4
 
 

426 (Upgrade Required) — 需要升級

Server 拒絕使用目前協議處理請求,
但 Server 可能願意在 Client 升級到不同協議後執行 (請求)。

 
對於 426 (Upgrade Required),Server 必須在回應中生成 Upgrade 表頭欄位 以指示需要的協議,
例如,此 Server 需要使用 HTTP/3.0 協議:

HTTP/1.1 426 Upgrade Required

Upgrade: HTTP/3.0 Connection: Upgrade Content-Length: 53 Content-Type: text/plain

 

 
此範例引用自 RFC7231,恩…我覺得有點爛 😂,
因為目前並沒有 HTTP/3.0 只有 HTTP/2 喔!
 
 

428 (Precondition Required) — 需要前置條件

源伺服器 要求請求須包含 條件請求欄位 (e.g., If-Match)。

 
此狀態碼的典型應用是:避免遺失更新 (lost update) 問題,
例如,Client 使用 GET 請求,得到資源狀態並修改之,
隨後希望使用 PUT 請求,更改 Server 該資源狀態。
然而,若有第三者先修改 Server 之狀態,便可能導致版本衝突。
 
因此,透過要求使用 條件請求 (conditional request)
Server 便可確保 Client 是使用正確的副本版本。
 
對於 428 回應,Server 應解釋如何正確地重新提交請求,例如:

HTTP/1.1 428 Precondition Required

Date: Sat, 18 Feb 2017 00:01:57 GMT Content-Type: text/html; charset=utf-8

 

<!doctype html> <html> <head> <title>Precondition Required</title> </head> <body> <h1>Precondition Required</h1> <p>This request is required to be conditional; try using "If-Match".</p> </body> </html>

 

 
當然,若 Client 已使用條件請求,但版本不一致,
Server 才接著考慮使用 412 (Precondition Failed) — 前置條件失敗。
 
428 (Precondition Required)RFC6585 於 2012 年引入。
 
 

429 (Too Many Requests) — 過多請求

User 在給定的時間內發送了過多的請求。

 
對於 429 回應,Server 應解釋錯誤的詳細資訊,
且可以生成 Retry-After 欄位,指示需等待多長時間才能發送新請求,例如:

HTTP/1.1 429 Too Many Requests

Date: Sat, 18 Feb 2017 00:01:57 GMT Content-Type: text/html; charset=utf-8 Retry-After: 3600

 

<!doctype html> <html> <head> <title>Too Many Requests</title> </head> <body> <h1>Too Many Requests</h1> <p>I only allow 50 requests per hour to this Web site per logged in user. Try again soon.</p> </body> </html>

 
429-Too-Many-Requests
 
429 (Too Many Requests)RFC6585 於 2012 年引入。
 
[註]:當 Server 受到攻擊或接收到來自單方的大量請求時 (e.g., DDoS),對每個請求進行 429 回應也相當消耗資源。此時 Server 應採取更合適的作法 (e.g., 關閉 TCP 連線)。
 
 

431 (Request Header Fields Too Large) — 請求表頭欄位過大

因為請求中的表頭欄位過大,Server 不願意進行處理。

 
有可能是『整塊』表頭欄位的總和過大,也有可能只是某個欄位的值太長,
若是後者,Server 應在回應的酬載中,詳細指出是哪一個欄位導致錯誤發生,
Client 可以在減少表頭欄位大小後,重新提交請求。

HTTP/1.1 431 Request Header Fields Too Large

Date: Sat, 18 Feb 2017 00:01:57 GMT Content-Type: text/html; charset=utf-8

 

<!doctype html> <html> <head> <title>Request Header Fields Too Large</title> </head> <body> <h1>Request Header Fields Too Large</h1> <p>The "Example" header was too large.</p> </body> </html>

 
431-Request-Header-Fields-Too-Large
 
431 (Request Header Fields Too Large)RFC6585 於 2012 年引入。
 


 

5xx (Server Error) — 伺服端 錯誤

Server 無法完成顯然有效的請求。

 

500 (Internal Server Error) — 內部伺服器錯誤

Server 遇到例外的狀況,使其無法完成請求。

 
1
 
 

501 (Not Implemented) — 未實作

Server 不支持完成請求所需的功能,
如 Server 無法識別請求方法 且 無法支援任何資源。

 
 

502 (Bad Gateway) — 錯誤的閘道

作為 閘道 (gateway) 或 代理 (proxy) 的 Server,
在嘗試完成請求時,由 inbound Server 接收到無效的回應。

 
發生的原因很多,例如 網際網路服務供應商 (Internet Service Provider, ISP)、
中間人 (intermediary) 或 源伺服器 (Origin Server)… 出了問題:
502-Bad-Gateway
 
 

503 (Service Unavailable) — 服務無法使用

由於暫時的 超載 或 定期維護,Server 目前無法處理請求,
情況可能會在一段時間後得到緩解。

 
503 (Service Unavailable) 回應訊息,時常搭配 Retry-After 表頭欄位
告知 Client 大約什麼時候能再取得服務 or 造訪網站,
可以使用 HTTP Date 或 秒數 兩種格式:

Retry-After: Sat, 27 Jan 2018 19:53:43 GMT

 

Retry-After: 120

(i.e., 告知 Client 服務預計於 2分鐘後 恢復)
 
當然,Server 發生超載時,並非一定要使用 503,
也可以回應其他 5xx 狀態碼 或純粹 拒絕連線
 
 

504 (Gateway Timeout) — 閘道逾時

作為 閘道 (gateway) 或 代理 (proxy) 的 Server,
未在時限內收到上游伺服器的回應,故無法完成請求。

 
[註]:「所有」 訊息,都由 上游 『流』到 下游,與 Client/Server 無關。
 
 

505 (HTTP Version Not Supported) — HTTP版本不支援

Server 不支援 (或不願支援) 請求訊息中使用的 HTTP 之 主要版本 (major version)。

 
Server 應該為 505 回應 生成一個表示,
並描述為何該版本不被支援、該 Server 支援哪些協定。
 
 

507 (Insufficient Storage) — 儲存空間不足

[WebDAV] Server 無法儲存 用於完成請求的 表示 (representation),
因此請求方法無法執行於目標資源上。
(此情況被認為是暫時的)

 
507 (Insufficient Storage)WebDAV 於 2007 年引入。
 
 

508 (Loop Detected) — 檢測到迴圈

[WebDAV] 因為在處理 “Depth:infinity” 請求時,遇到了無窮迴圈,Server 已終止操作。

 
此狀態碼表示整個操作皆失敗。
508 (Loop Detected)RFC5842 於 2010 年引入。
 
 

511 (Network Authentication Required) — 需要網路驗證

Client 需要進行 驗證 (authenticate) 才能獲得網絡訪問權限。

 
回應的 表示 (representation) 應包含一個指向資源的連結,以允許 User 提交憑證 (e.g., 使用 HTML 表單)。
源伺服器 不應 生成 511 回應,其主要作為 攔截代理 (intercepting proxy) 控制網絡訪問的一種手段。
(然而,這種非權威性回應也會引發許多安全問題,在使用上須多加注意)
 
範例:

HTTP/1.1 511 Network Authentication Required

Date: Sat, 18 Feb 2017 00:01:57 GMT Content-Type: text/html; charset=utf-8

 

<!doctype html> <html> <head> <title>Network Authentication Required</title> <meta http-equiv="refresh" content="0; url=https://login.example.net/"> </head> <body> <p>You need to <a href="https://login.example.net/"> authenticate with the local network</a> in order to gain access.</p> </body> </html>

 

 
511 (Network Authentication Required)RFC6585 於 2012 年引入。
 
 

作者: 鄭中勝
喜愛音樂,但不知為何總在打程式 ? 期許能重新審視、整理自身所學,幫助有需要的人。

發表迴響