為什麼你的 Time to First Byte 很慢,以及該怎麼做
TTFB 不是單一錯誤。它是由 DNS、連線建立、CDN 路由、伺服器工作、快取未命中,有時還有一個緩慢的資料庫查詢所造成的可見延遲。
目錄
- 先從 TTFB 實際測量的內容開始
- 什麼算是緩慢的 TTFB?
- 從不只一個地方測量
- 1. 瀏覽器開發者工具
- 2. 來自多個地區的合成測試
- 3. 真實使用者監控或伺服器日誌
- TTFB 緩慢的常見原因
- 你的 HTML 沒有被快取
- 你的 CDN 只快取資產
- 你的伺服器在回應前做太多事
- 資料庫查詢緩慢或不可預測
- 你的應用程式有 cold starts
- Redirects 浪費了第一個請求
- 實用的除錯順序
- Step 1: 測試主要文件,而不只是整個頁面
- Step 2: 比較地區
- Step 3: 檢查回應標頭
- Step 4: 檢查 origin timing
- Step 5: 修正已確認的最大延遲
- 通常有效的修正
- 在 edge 快取公開 HTML
- 將非關鍵工作移出請求路徑
- 減少後端相依鏈
- 把運算放得離使用者更近
- 讓 redirects 保持無聊
- 不該做的事
- 冷靜版計畫
先從 TTFB 實際測量的內容開始
Time to First Byte 通常簡稱為 TTFB,指的是瀏覽器請求某個資源,到收到回應第一個位元組之間的時間。
這聽起來像是伺服器指標,但它不只是伺服器指標。TTFB 包含幾個步驟:
- DNS 查詢,如果主機名稱尚未被解析
- TCP 連線建立
- HTTPS 的 TLS 協商
- 請求傳送到伺服器或 CDN edge 的時間
- 伺服器上的排隊與處理
- 回應傳回瀏覽器的時間
所以,高 TTFB 可能表示你的後端很慢。也可能表示使用者離你的 origin 很遠、CDN 設定錯誤、快取持續未命中,或伺服器花太多時間決定要送出什麼內容。
這很重要,因為 TTFB 位於載入鏈的開端附近。如果 HTML 文件很晚才抵達,瀏覽器也會很晚才發現 CSS、JavaScript、字型與圖片。即使你有很好的前端最佳化,只要第一個文件回應花了 1.5 秒,使用者仍然會覺得慢。
什麼算是緩慢的 TTFB?
沒有一個通用數字能適用於每個網站、地區與架構。不過,實務上的門檻仍然有幫助。
Google 的 web.dev 指引將低於 800 ms 的 TTFB 歸類為良好,800–1800 ms 需要改善,高於 1800 ms 則視為不佳。對於在使用者附近提供、且有良好快取的行銷頁面,你通常可以做得比這更好。對於需要執行動態工作的複雜已驗證 dashboard,可接受的數字可能較高,但它仍然應該能被解釋。
重要的習慣是將數字分段。全球平均 TTFB 900 ms,可能掩蓋了靠近 CDN edge 的使用者只需 150 ms,而另一個地區的使用者卻需要 2200 ms 的情況。同樣地,你的首頁可能沒問題,但搜尋、分類或登入後頁面可能正默默地讓人痛苦。
從不只一個地方測量
不要只憑一次 Lighthouse 執行結果來診斷 TTFB。Lighthouse 很有用,但它只是來自單一環境的一次測試。如果你剛開始解讀它,可以先冷靜閱讀如何不慌張地閱讀 Lighthouse 報告——主要重點是區分實驗室訊號與實際現場狀況。
對於 TTFB,你至少需要三種視角:
1. 瀏覽器開發者工具
開啟 Network 面板,停用快取後重新載入,並檢查主要文件請求。時間分解會顯示 DNS、連線、TLS、等待與下載階段。「waiting」階段通常是人們所說的後端時間,不過它也可能包含上游延遲。
2. 來自多個地區的合成測試
從靠近與遠離使用者的位置執行測試。如果某個地區的 TTFB 低、另一個地區高,在重寫應用程式碼之前,應先懷疑地理位置、CDN 路由、origin 位置或快取覆蓋率。
3. 真實使用者監控或伺服器日誌
現場資料會告訴你真實使用者在不同裝置、網路與 session 中的體驗。伺服器日誌可以告訴你 origin 是否快速產生了回應。用戶端觀察到的 TTFB 與 origin 處理時間之間的差異,通常就是 CDN 與網路問題出現的地方。
TTFB 緩慢的常見原因
你的 HTML 沒有被快取
這是內容網站與 ecommerce 網站最常見的問題。靜態資產被積極快取,但 HTML 文件——也就是瀏覽器最先需要的東西——每次請求都重新產生。
有時這是必要的。但通常不是。
如果一個公開頁面每天只變更幾次,它大概不應該要求每個匿名訪客都重新進行一次資料庫渲染。視情況使用完整頁面快取、edge 快取、靜態生成,或 stale-while-revalidate 模式。
檢查回應標頭中的訊號,例如 Cache-Control、CDN-Cache-Status、Age、Vary 與 Set-Cookie。如果頁面對每位訪客都送出唯一 cookie,可能會意外讓自己變得不可快取。如果你需要一個實用方式來思考這一層,我們關於 production 中 redirects 與 HTTP headers 除錯的指南中的相同除錯習慣,可以直接套用到 TTFB 工作上。
你的 CDN 只快取資產
許多團隊加上 CDN 後,就以為效能工作完成了。但如果 CDN 只提供圖片、CSS 與 JavaScript,第一個 HTML 請求可能仍然會一路傳到單一 origin 伺服器。
對於使用者都在本地的在地商家網站,這可能沒問題。但對國際受眾來說就不行。使用者離 origin 越遠,在後端工作甚至開始之前,你要付出的延遲就越多。
對 TTFB 友善的 CDN 設定通常表示:
- 在安全的情況下快取公開 HTML
- 尊重針對已驗證或個人化頁面的刻意 bypass 規則
- 避免不必要的
Vary標頭,導致快取被切得過細 - 使用快取清除或重新驗證,而不是完全停用快取
- 確認 edge locations 確實提供命中,而不是轉送每個請求
CDN 不是魔法。它是快取與路由層。請把它當成這樣來看待。
你的伺服器在回應前做太多事
緩慢的後端路徑可能來自許多小延遲:資料庫查詢、API 呼叫、模板渲染、feature flag 檢查、驗證、個人化、記錄,以及 cold starts。
最糟的模式是序列相依工作。例如:
- 擷取頁面資料
- 然後擷取相關商品
- 然後擷取價格
- 然後呼叫推薦服務
- 然後渲染 HTML
如果每個步驟都等待前一步完成,TTFB 會快速增加。將彼此獨立的工作平行化,從第一個回應中移除非關鍵呼叫,並快取成本高的結果。
一個有用的原則:如果使用者無法立即看到或使用該結果,它大概不應該阻塞第一個位元組。
資料庫查詢緩慢或不可預測
資料庫經常造成 TTFB 問題,因為它們在開發環境表現良好,卻在真實流量下表現不佳。缺少索引、大型 join、N+1 查詢、鎖競爭,以及過大的結果集,都會表現為「伺服器很慢」。
這裡不要猜。擷取慢請求的查詢時間。看 p95 與 p99,而不只是平均值。一個頁面通常在 120 ms 回應,但偶爾阻塞 4 秒,仍然會造成糟糕的使用者體驗。
常見修正包括:
- 新增或修正索引
- 移除 N+1 查詢模式
- 快取讀取量大的資料
- 對大型查詢分頁
- 將報表或 analytics 查詢移出請求時間
- 為下游呼叫設定合理逾時
你的應用程式有 cold starts
Serverless 與容器化平台可以很出色,但當流量突增或區域資源配置不足時,cold starts 可能會傷害 TTFB。
如果閒置後的第一個請求比後續請求慢很多,請調查 cold starts。你可能需要 provisioned concurrency、較小的 bundles、較少的啟動相依、warmer functions,或針對延遲敏感路由採用不同部署型態。
這不是反對 serverless 的論點。這是反對假裝 runtime 模型不存在的論點。
Redirects 浪費了第一個請求
Redirect 會在瀏覽器收到最終文件前,增加另一個請求-回應循環。舊連結可能無法避免從 http:// 到 https:// 的一次 redirect,但 chains 是浪費。
常見 chains 包括:
http://example.com→https://example.com→https://www.example.com- protocol 正規化後才進行 trailing slash 正規化
- 快取查詢前先做 geo 或語言 redirects
- legacy campaign links 會跳轉經過數個 URLs
盡可能修正來源連結,合併 redirect 規則,並讓 canonical URLs 直達。Redirect 時間不一定會被回報為最終請求的 TTFB,但使用者仍然要付出代價。
實用的除錯順序
當 TTFB 看起來很慢時,請使用這個順序。它能避免在確認快取與路由行為之前,就先最佳化應用程式碼的常見錯誤。
Step 1: 測試主要文件,而不只是整個頁面
找出 HTML 文件的請求。記錄總 TTFB 與時間分解。分別在有無瀏覽器快取的情況下重複測試。如果相關,測試公開頁面、動態頁面與登入後頁面。
Step 2: 比較地區
從多個地理位置執行相同 URL。如果較慢的地區與距離 origin 的遠近相關,優先處理 CDN 與 edge 快取。如果每個地區都慢,則查看後端處理與 origin 容量。
Step 3: 檢查回應標頭
尋找快取標頭、cookies、Age、CDN 狀態與 Vary。缺少 Age 標頭或反覆快取未命中都是線索。公開 HTML 上廣泛的 Vary: Cookie 標頭通常是快取殺手。
Step 4: 檢查 origin timing
加入伺服器時間 instrumentation。Server-Timing 標頭可以揭露後端階段,例如資料庫時間、渲染時間與上游 API 時間。即使是簡單標籤也很有用:
Server-Timing: db;dur=82, render;dur=41, api;dur=210
現在你的瀏覽器 timing 可以顯示伺服器是否花了 300 ms 做實際工作,或延遲是否發生在請求到達應用程式之前。
Step 5: 修正已確認的最大延遲
這聽起來很明顯,但團隊常常修熟悉的東西,而不是測量出來的東西。如果快取未命中占主因,就修快取。如果資料庫占主因,就修查詢。如果全球使用者的 TLS 與連線建立占主因,就修路由、CDN 覆蓋率或 origin 地理位置。
前端工作仍然重要。字型、圖片與 JavaScript 會影響 HTML 抵達後發生的事情。但它們不能替代快速的第一個回應。如果你也正在處理渲染效能,web fonts 仍然是許多網站上最容易取得的效能收益之一,因為它們會影響文件抵達後文字多快變得可用。
通常有效的修正
在 edge 快取公開 HTML
對於行銷頁面、文件、部落格、landing pages 與分類頁面,edge 快取通常是最大的 TTFB 改善。若內容經常變更,使用短 TTL。如果快取在背景更新時可接受內容稍微過時,使用 stale-while-revalidate。
請小心個人化。如果頁面依貨幣、語言、登入狀態或實驗群組而異,請明確定義這些變體。意外的逐使用者差異會破壞快取效率。
將非關鍵工作移出請求路徑
寄送 email、analytics enrichment、推薦生成、webhook 呼叫與繁重 logging,很少應該阻塞第一個位元組。將它們放進 queues,或在 response 開始後執行。
減少後端相依鏈
將獨立呼叫平行化。快取慢速 API 的回應。設定逾時。為有幫助但非必要的服務設計 fallback content。
一個緩慢的推薦 widget 不應該延遲整個商品頁面。
把運算放得離使用者更近
如果你的使用者遍布全球,而 origin 只在一個地區,延遲就是結構性的。CDN 快取可以為公開內容隱藏大部分延遲。對於動態內容,考慮區域部署、適合路由的 edge rendering,或將 APIs 移近受眾。
讓 redirects 保持無聊
用一次跳轉正規化 URLs。更新內部連結,讓使用者與 crawlers 直接前往最終目的地。稽核舊 campaign URLs 與平台遷移。Redirects 很容易被忽略,因為它們在正常運作時看不見,但它們仍然花時間。
不該做的事
不要為每個 route 追求完美 TTFB 數字。會執行實際運算的已驗證報表,不會像已快取的部落格文章一樣表現。
不要把平均 TTFB 當成唯一指標。百分位數很重要。地理位置很重要。頁面類型很重要。
不要假設有 CDN 就代表你的 HTML 已經被快取。請驗證它。
也不要把 TTFB 視為與產品決策無關。個人化、實驗、即時庫存與第三方服務都有延遲成本。有些值得。有些只是習慣。
<!-- tool-cta:start -->
💡 試試這個: 診斷 TTFB 時,Get Headers 會顯示快取狀態、伺服器耗時和重新導向,這些資訊通常能說明延遲來自哪裡。
<!-- tool-cta:end -->
冷靜版計畫
一旦你停止把緩慢的 TTFB 當成模糊的「伺服器問題」,它通常都可以修正。測量文件請求。依地區與頁面類型分段。檢查標頭。比較用戶端 timing 與 origin timing。然後修正已確認的最大瓶頸。
多數網站不需要奇特的架構。它們需要更少可避免的快取未命中、更少阻塞後端工作、更乾淨的 redirects,以及更清楚知道在送出第一個位元組之前,哪些事情必須發生。