521
Cloudflare 自己的代碼、不是 IETF 的,指的是整個 5xx 範圍裡最窄的一種失敗:你的來源機主動拒絕了 Cloudflare 的 TCP 連線,所以沒有東西慢、也沒有東西送錯地方——是有東西說了不。
同樣三個數字,你站在請求的哪一邊,它就是不同的問題。直接讀你自己那一塊。
為什麼會看到它
這裡沒有你的事,而快速確認這一點就是你的貢獻。這個區域的每一個請求都以一模一樣的方式立刻失敗——HTML 文件、API、雜湊過的資源包,通通一樣——因為失敗發生在你的應用程式碼進入畫面之前。你看到的是 Cloudflare 自己的 HTML 頁,帶著 `server: cloudflare` 標頭和一個 `cf-ray`,而它會讓你用戶端裡任何一個 `res.json()` 在 `<` 上丟出解析錯誤。
該怎麼做
先確立它是全站的,因為光是這一件事就會把整個調查方向改掉:只有某一條路徑出現的 521 其實不是 521,那是你來源機自己的回應被轉過來;一個真的 521 連靜態資源都會一起掛。接著收集兩串真正有用的字——回應標頭裡的 `cf-ray` 值,還有確切時間——然後交出去,因為維運的人就是用這兩樣在 Cloudflare 那一側找到那個請求的。也別讓用戶端把事情搞得更糟:對一台正在拒絕連線的來源機做自動重試迴圈什麼都得不到,只會拖慢恢復,所以重試次數要設死上限。還有,不要把時間花在 CORS 上,就算 console 顯示的是 CORS 錯誤:錯誤頁不會帶 `access-control-allow-origin`,所以瀏覽器會在真正的失敗上面再疊一層 CORS 抱怨,而那則 CORS 訊息是症狀。
為什麼會看到它
來源機上有東西給了 Cloudflare 一個拒絕、而不是一次握手,而候選人只有三個。行程沒在跑或已經掛了,所以沒有東西握著那個 socket。行程在跑,但綁在錯的介面上——一個綁在 `127.0.0.1:8080` 的應用程式,對外面來的每一條連線的拒絕程度,跟一個停掉的行程一模一樣。或者有一道防火牆專門在拒絕 Cloudflare,而這是那種「沒有部署也會發生」的情況:fail2ban、一條新的安全群組規則,或某個主機端的封鎖工具在看到所有流量都來自少數幾個代理位址之後,把那段位址封了。
該怎麼做
照這個順序查,因為每一項都便宜,而且每一項都能排掉下一項。有沒有東西在監聽——在來源機上跑 `ss -ltnp`——而且它綁的是 `0.0.0.0` 還是回送位址?它在不在你 SSL/TLS 模式要求的那個連接埠上:Cloudflare 的文件寫著 Flexible 用 80、Full 和 Full (Strict) 用 443,而且 Cloudflare 根本只代理它公布的那份連接埠清單,所以一個跑在 3000 上的服務對它來說是不存在的。Cloudflare 有沒有被放行?Cloudflare 文件裡的解法就是在來源機防火牆或安全軟體裡放行它全部的 IP 範圍,並確認它的位址既沒被擋、也沒被限流——「被限流」跟「被擋」一樣重要,因為代理過的流量會把整個網站的請求集中到一小組來源位址上,看起來就跟攻擊一模一樣,天真的規則一定會中招。接著去讀來源機自己的錯誤紀錄找當機,那也是 Cloudflare 列出來的原因之一。最後記住這個讓它可診斷的不對稱:防火牆設成 REJECT 產生的是 521,同一道防火牆設成 DROP 產生的是 522——所以代碼直接告訴你要找的是哪一種規則。
為什麼會看到它
你正在讀的這一頁是 Cloudflare 寫的,它擋在網站前面而且運作正常;後面那台網站自己的伺服器沒有在接受連線。你的瀏覽器、裝置和網路都跟這件事無關——所有人在任何地方看到的都是同一頁,而且它是立刻出現的,不是等了很久才出現。它通常代表網站的伺服器停了或設定錯了,而且通常會持續到有人發現為止。
該怎麼做
這次在本機真的沒有東西可以試:重新整理、清 cookie、換瀏覽器、改 DNS、重開路由器,都沒辦法讓一台遠端伺服器開始接受連線。等幾分鐘再重新整理一次。如果你要回報,Cloudflare 那一頁底下印的 Ray ID 和確切時間是唯二值得送出去的東西,因為它們能在網站維運方的紀錄裡指出那個確切的請求。如果那是你有付費的服務,狀態頁或維運方的社群帳號是最快知道「他們是不是已經發現了」的地方;而 521 這種失敗通常很快就會被發現,因為它一次影響到每一位訪客。
對著出錯的那個網址跑一次。它只印狀態碼、不印錯誤頁,所以你看到的是伺服器真正說了什麼,而不是瀏覽器畫出來的東西。
curl -sS -o /dev/null --connect-timeout 5 -w '\n%{http_code} connect=%{time_connect}s\n' --resolve example.com:443:203.0.113.10 https://example.com/`--resolve` 會讓 curl 連到你指定的位址,同時照樣送出原本的主機名稱和 SNI,所以這重現的就是 Cloudflare 撥號到你來源機時做的事,只是把邊緣節點從路徑上拿掉——把範例位址換成你真正的來源機位址。`--connect-timeout 5` 讓拒絕發生得很快,而且同樣有用的是,它把「拒絕」和「安靜地丟掉」分開了。結果有三種,而每一種是不同的 bug。`curl: (7) Failed to connect to example.com port 443: Connection refused` 就是重現成功:來源機拒絕你的方式,跟它拒絕 Cloudflare 的方式一模一樣,而答案在來源機上——沒有東西在監聽、綁錯位址、錯的連接埠,或者一道設成 reject 的防火牆。卡住、然後在五秒的連線逾時結束,那根本不是 521;被丟掉的 SYN 是 Cloudflare 報成 522 的東西,所以同一道防火牆上的 DROP 規則和 REJECT 規則會產生兩個不同的錯誤號碼。而如果它正常回應,代表來源機接受來自你的連線、卻不接受來自 Cloudflare 的,那就直直指向來源位址過濾、不是服務本身——拿來源機的防火牆去對 Cloudflare 公布的 IP 範圍。之後再把 `--resolve` 拿掉跑一次,看邊緣節點的答案、讀 `cf-ray` 標頭,那就是維運方查得到的 id。
521 不在 RFC 9110 裡。IETF 登記的 5xx 代碼到 511 為止,而 520 到 526 這一段是 Cloudflare 自己的擴充,只有 Cloudflare 的文件在定義,而且只會出現在經過它代理的流量上——一筆灰雲(未代理)的 DNS 記錄不可能回出這個代碼。Cloudflare 的定義非常精確:錯誤 521 發生在來源網頁伺服器拒絕來自 Cloudflare 的連線時。「拒絕」這兩個字就是這個數字的全部價值。拒絕是一種回答——一個 TCP RST,或一個 ICMP unreachable——這代表封包有到來源機的網路,而那裡有東西回絕了它;光這一步就排掉了旁邊三個代碼。它不是 522,Cloudflare 把 522 定義成逾時:Cloudflare 送出 SYN 之後 19 秒內沒有 SYN+ACK,或者連線建立之後 90 秒內請求沒有被確認。它不是 523,Cloudflare 把 523 定義成完全聯絡不上來源機,典型原因是中間某台網路裝置沒有到來源機位址的路由。它也不是 520,那是來源機回了一個空的、未知的或非預期的東西。Cloudflare 為 521 列了兩個原因——來源網頁伺服器的應用程式離線,以及 Cloudflare 的請求被擋掉——而解法就從這兩個推出來:確認來源機有在回應、翻它的錯誤紀錄找應用程式當機或事故、確認 Cloudflare 的 IP 位址既沒被擋也沒被限流、在來源機的防火牆或其他安全軟體裡放行 Cloudflare 所有的 IP 範圍,還有確認來源機真的綁在、也監聽著你的 SSL/TLS 模式所要求的那個連接埠——Flexible 是 80,Full 和 Full (Strict) 是 443。最後那一項就是大家卡最久的那個情況,因為 Cloudflare 只代理一組固定的連接埠:HTTP 是 80、8080、8880、2052、2082、2086、2095,HTTPS 是 443、2053、2083、2087、2096、8443。監聽在這之外任何地方的來源機,不管它多健康,代理都碰不到。
| 容易搞混的 | 怎麼分 |
|---|---|
522 | 兩者都代表 Cloudflare 沒辦法跟你的來源機建立一條能用的連線,差別在於來源機有沒有回話。521 是拒絕,那是一種回答,所以它是瞬間的。522 是逾時,而 Cloudflare 定義得很精確:送出 SYN 之後 19 秒內沒有 SYN+ACK,或者連線建立之後 90 秒內請求沒有被確認。同一條防火牆規則,會因為是 REJECT 還是 DROP 而產生其中一個。 |
523 | 523 是 Cloudflare 根本聯絡不上來源機,而它的文件把原因歸給「中間某台網路裝置沒有到來源機位址的路由」。他們舉的實例是一份 AWS 路由表,裡面一條太寬的 `172.0.0.0/8` 把 Cloudflare 自己的 `172.64.0.0/13` 範圍吞掉了,於是回程流量被送去某個私有的地方。所以 521 代表封包到了、被拒絕;523 代表封包根本沒到。 |
520 | 520 是來源機回了一個 Cloudflare 用不了的東西——空的、未知的或非預期的,包括回應標頭超過 Cloudflare 128 KB 的上限,而過多的 cookie 碰到這條線的頻率比大家想的高。那是連線的另一端,跟 521 相反:520 的情況下握手、請求和回應全都發生了,只有回應的內容不對。 |
| 502 | 502 是任何代理都可以用來表示「從內部伺服器收到不合法回應」的 IETF 代碼,它把拒絕、重置和解析不了的回覆全部包在一起。521 則是 Cloudflare 把同一類縮到單一原因——而這就是 520 到 526 這一段存在的理由:一個籠統的 502 會讓你去查四件事,而一個 521 直接告訴你來源機拒絕了連線,把你送到監聽狀態和防火牆前面。 |
| 524 | 相較之下 524 反而讓人安心,因為它證明了連線是成功的:Cloudflare 連上了來源機,然後等了 125 秒都沒等到 HTTP 回應。所以 524 把 521 提出的每一個問題都排除掉了——有東西在監聽、連接埠是對的、防火牆有放 Cloudflare 進來——並且把整個調查搬進應用程式裡。 |
這些全都在你的瀏覽器裡跑,不會上傳任何東西。
查 521 的時候,通常也會順手看一下這幾個。
去讀回應標頭,不要讀那個網頁。curl -sS -o /dev/null -D - https://example.com/path 會只印標頭、不印內容,而 server、via 和 cf-ray 三個加起來,就能指出是哪一層回答的。有 cf-ray 的值代表這個回應是 Cloudflare 處理的,那個值也是他們客服會跟你要的編號。想把邊緣節點整個排除掉,就加上 --resolve example.com:443:203.0.113.10 再打一次:它會照樣送出原本的主機名稱和 SNI,但直接連到你指定的來源位址——答案變了,就代表邊緣節點和來源機講的不是同一件事。
不重要,而且絕對不要拿它來做判斷。RFC 9112 要求用戶端忽略原因片語,伺服器可以隨意改它,而 HTTP/2 和 HTTP/3 根本沒有這個欄位——所以在 HTTP/1.1 上是 404 Not Found,到了 HTTP/2 就只剩一個 404。這個網站會寫出登記在案的片語,是因為那是大家會拿去搜尋、也是會出現在 HTTP/1.1 紀錄裡的字串,不是因為有任何軟體依賴它。
5xx 和 429 可以重試,4xx 不要——下一次送過去也不會有任何不同。如果回應帶了 Retry-After 就照它做,RFC 9110 定義它就是為了這件事,值可以是秒數也可以是一個時間點。沒有的話,就用帶抖動的指數退避加上一個硬上限,而且只對冪等的方法這樣做:一個被重試的 POST 可能會刷兩次卡。對一台已經在失敗的伺服器丟出重試風暴,是短暫事故變成長時間事故最常見的那條路。
可以,唯一要守的規則是那個代碼必須是真的。所有會自動讀你回應的東西——搜尋引擎、監控、快取、用戶端的重試邏輯——都只看那個數字,從來不看那一頁寫了什麼。用 200 回一頁錯誤訊息,會把故障從你自己的警報裡藏起來;用 200 回一頁不存在的內容,會讓錯誤被當成內容收進索引;而對一個只是格式不對的請求回 500,會把值班的人送去堆疊裡錯的那一半。
521 還是卡住嗎?看完整的狀態碼對照表,或是回到上面,讀為你這一端寫的那一塊。