Cloudflare 免費方案上的抓取器洪水:什麼擋住了它,以及我們關掉了什麼
維運
某天早上,我們的一個商品頁網站打不開了。Cloudflare 向訪客顯示 524 錯誤,來源伺服器每頁需要兩到六分鐘,而大約十小時內沒有任何通知告訴我們。原因是抓取器透過住宅代理以每秒約 225 個請求的速度進行抓取。
洪水的樣貌
- 數千個位址,每個位址一到三個請求。來自美國、西班牙、法國、巴西、英國等地的家庭寬頻連線,也就是住宅代理網路出租的那種流量。
- Mac 上的 Chrome 使用者代理,版本號碼從 103 到 151。
- 幾乎沒有 Cookie 或 Referer。在伺服器上抽樣的 2,697 個請求中,只有 4 個帶有 Cookie,4 個帶有 Referer。
- 兩種客戶端。約一半沒有發送
Sec-Fetch-*標頭和客戶端提示:那是帶有複製使用者代理字串的腳本。另一半發送完整的瀏覽器標頭:那是無頭瀏覽器。
每個請求都渲染每個頁面,因此每個工作程序排隊數百次渲染並達到其 CPU 上限。
為什麼每個位址的限制幫不上忙
速率限制計算每個位址的請求數。當數千個位址每個只發送一到三個請求時,沒有任何單一地址接近限制。封鎖國家會封鎖我們的訪客。抓取器無法低成本偽裝的是:像一個以前來過該網站的瀏覽器那樣行為。
在 Cloudflare 邊緣:三個免費規則
免費方案允許每個區域有五個 WAF 自訂規則。我們使用了三個,全部使用受管挑戰而不是封鎖,因此被誤判的人可以點擊通過:
- 偽造瀏覽器:一個 GET 請求,其使用者代理包含
Chrome/且沒有Sec-Fetch-Mode標頭。自 2019 年起的每個 Chrome 都會發送它。 - 抓取器雲端:來自阿里巴巴、騰訊和華為雲端網路的頁面請求,我們挑戰的流量大部分來自那裡。
- 緊急情況,預設關閉:一個完全沒有 Cookie 且沒有 Referer 的 GET 請求。去過該網站的人,或透過連結到達的人,會無事通過;不保留 Cookie 的抓取器會在每個請求上遇到挑戰。我們只在洪水期間開啟它,因為它也會挑戰那些在全新瀏覽器中直接輸入位址的人。
這三種機制都會放行 Cloudflare 驗證過的機器人、自報家門的機器人 User Agent、我們自己的位址、API 與 webhook 路徑,以及非網頁的檔案。兩天後流量洪峰仍在持續:僅一小時內,假瀏覽器規則就對超過 10,000 個請求發起挑戰。Cloudflare 每次查詢回傳的 10,000 個事件來自 9,627 個位址,沒有任何一個位址超過 4 次。
在源站:應用層的一道閘門
無頭瀏覽器端具備完整的瀏覽器標頭,因此在緊急規則關閉時,僅靠邊緣規則無法攔截它。我們直接在應用層中,於頁面渲染器前方加入一個小型中介軟體:
- 帶有 Chrome User Agent 但既無
Sec-Fetch-Mode也無客戶端提示(client hints)的請求,在執行任何工作之前就會收到 403; - 陌生訪客若沒有我們的任何 Cookie 且無 Referer,僅在工作者(worker)有閒置容量時才會被服務,速率約為每秒幾個頁面。超過此速率時,會回傳一個 400 字節的 503 頁面,設定 Cookie 並在兩秒後重新載入。真人第二次嘗試即可取得頁面;不保留 Cookie 的爬蟲則永遠無法取得。
在啟動後的第一分鐘,閘門拒絕了約 6,700 個假瀏覽器,並擋下約 6,700 個陌生訪客,同時以每秒約 9 頁的速率服務陌生訪客。慢速請求降至零,工作者的 CPU 從滿載降至 12 到 15%,記憶體則從每個 4 到 5 GB 降至 0.65 GB。
我們保持關閉的項目
- Bot Fight Mode。它會挑戰自動化流量,且在免費方案中無法透過自訂規則跳過,結果連我們的支付服務商 webhook 也被擋下。
- 整個區域的 Under Attack 模式。它會將所有訪客、API 客戶端與 webhook 都導向中間驗證頁。緊急規則僅對陌生訪客的頁面請求執行相同工作。
- 免費方案的速率限制規則。它按位址計數,而這次流量洪峰從未觸發此限制。
- 封鎖 AI 爬蟲。我們希望被 AI 搜尋助理索引,而且那些爬蟲並非問題所在。
重點整理
透過住宅代理的流量洪峰很容易辨識,因為它缺少:瀏覽器 fetch 標頭、Cookie 與 Referer。在邊緣端對其發起挑戰,將陌生訪客的流量控制在工作者可渲染的範圍內,並確保伺服器外部有機制能在頁面無法開啟時察覺。Night Watch 將這三者打包在一起:守護程序、帶有緊急規則自動切換功能的規則集,以及專為 Python Web 應用設計的閘門。