
在瀏覽器輸入網址並送出後,到底發生了什麼事?
「在瀏覽器輸入網址並送出後,到底發生了什麼事?」這個主題內其實牽扯到很多事情,原本想說把流程整理一下就好,結果一路挖下去,才發現從網址解析、網路連線到畫面渲染,每一段都還能再拆成好幾個主題。
這篇會先站在比較高的地方看完整流程,讓我們知道每個階段在做什麼,以及遇到效能問題時可以往哪裡查。至於各階段的實作細節,會留給後面的文章慢慢深挖。畢竟功能都還沒做出來,產品也還賣不出去,就先把所有優化加好加滿,通常只會讓自己更忙而已!
那就開始這趟旅程吧!
先看一次完整的 Navigation
以下會以 Chromium 架構為主。其他瀏覽器的 process 配置與內部實作可能不同,但從解析網址、建立連線、取得文件到產生畫面的主要階段仍然相近。
參考 Chrome 的 Inside look at modern web browser(part 2),一次新的頁面 Navigation 可以先整理成以下流程:
- 使用者在網址列輸入內容後,Browser Process 的 UI Thread 會判斷這段文字應該當成 URL,還是交給預設搜尋引擎搜尋。
- 確認目標 URL 後,瀏覽器開始 Navigation。若沒有可直接重用的連線,就要經過 DNS resolution、TCP connection 與 HTTPS 所需的 TLS handshake,再送出 HTTP request。
- 如果伺服器回傳 redirect response,瀏覽器會依狀態碼與相關 header 決定新的目標,再進行下一次 Navigation request。
- 收到 response 後,瀏覽器會參考
Content-Type、MIME type sniffing 結果與安全檢查,判斷內容要交給 Renderer Process 顯示,還是當成下載檔案處理。 - Browser Process 選定 Renderer Process 後,雙方會透過 IPC 進行 Navigation commit。從這個時間點開始,網址列、上一頁與下一頁等瀏覽器 UI 才會切換到新的文件狀態。
- Renderer Process 解析 HTML、套用 CSS、執行 JavaScript,並經過 Layout、Paint、Rasterization 與 Compositing 產生畫面。頁面也會繼續載入圖片、字型等其他資源。
- 文件與相關資源完成載入後,Renderer Process 會通知 Browser Process 更新頁面狀態,瀏覽器才會在適當時機停止網址列上的 loading 圖示。
這裡要特別區分兩件事:Navigation commit 不代表畫面已經渲染完成,而畫面第一次出現也不代表所有資源都載入完畢。實際分析效能時,還是要透過 DevTools 與 Performance API 觀察每個階段花了多少時間。
開始前,先認識 Process 與 Thread
在繼續往下之前,要先有一個基本概念:process 是作業系統分配資源與隔離執行環境的單位,thread 則是在 process 裡實際排程工作的單位。同一個 process 可以有多條 thread,而不同 process 之間通常會透過 IPC(Inter-Process Communication)交換資料。
Chromium 不會把所有工作都塞進同一個 process,而是拆成 Browser、Renderer、GPU、Network Service 等不同角色。這樣可以改善穩定性與安全隔離,不過也會增加記憶體使用量與 IPC 成本。
一個 Tab 也不一定只對應一個 Renderer Process。受到 Site Isolation、跨站 iframe、裝置資源與 process 數量上限等因素影響,同一個 Tab 可能使用多個 Renderer Process,多個頁面也可能共用同一個 process。
想繼續了解這些 process 如何分工,以及為什麼不是拆得越多就越好,可以接著看瀏覽器 Multi-process 架構。

瀏覽器怎麼拆解 URL?
以下面這個 URL 為例:
https://blog.itskylen.com:443/tech/browser-navigation/?source=ironman#navigation可以拆成幾個常見部分:
https:scheme,決定這個 URL 使用的協定與解析規則。blog.itskylen.com:host;如果是網域名稱,後續通常需要透過 DNS 取得可連線的 IP 位址。443:port。HTTP 與 HTTPS 的預設 port 分別是80與443,使用預設值時通常可以省略。/tech/browser-navigation/:path,用來識別來源上的資源位置;它是 URL path,不一定直接對應伺服器上的實體 UNIX 檔案路徑。?source=ironman:query,由應用程式自行定義參數用途。#navigation:fragment,用來指出文件內的位置或狀態;一般 HTTP request 不會把 fragment 傳給伺服器。
瀏覽器會先把輸入內容解析成 URL,再根據 scheme、host、port 等資訊決定後續如何連線。假如輸入的不是有效 URL,網址列才可能改用預設搜尋引擎處理。
從網域名稱走到 HTTP Response
確認 URL 後,接下來會進入網路階段。這一段最容易被一句「瀏覽器發出 request」帶過,但實際上還包含 DNS、TCP、TLS、HTTP 版本協商、redirect、內容壓縮與 cache 等工作。
DNS Resolution
如果 host 是網域名稱,瀏覽器需要先取得可用的 IP 位址。不過這不代表每次都會從 Root Name Server 開始查詢:瀏覽器、作業系統與 recursive resolver 都可能已經有 cache。
DNS 也不一定是整次載入最花時間的階段。命中 cache 時可能幾乎看不到額外等待;cache miss、網路品質不佳或 resolver 回應較慢時,時間才會比較明顯。完整查詢流程可以參考瀏覽器如何把網域名稱轉換成 IP?。
建立連線並傳送 HTTP Request
取得 IP 位址後,瀏覽器才能繼續建立連線。以 HTTPS over TCP 來說,通常還會經過 TCP 與 TLS handshake;如果已有可重用的連線,則不一定每次都重新執行完整流程。
HTTP 版本也會影響多個 request 如何共用連線。HTTP/1.1 可以使用 persistent connection,但同一條連線仍可能遇到 application-layer 的 head-of-line blocking;HTTP/2 則把 message 拆成 frame,讓多個 stream 可以在同一條 TCP connection 上交錯傳輸。詳細差異可以看 HTTP/1.0、HTTP/1.1 跟 HTTP/2 之間到底差在哪?。
如果 request 或 response 帶有 body,也不能只靠 GET、POST 直接推斷會使用幾個 TCP segment。HTTP message 怎麼被切成 TCP segment,會受到資料大小、TCP、TLS 與網路環境影響;100 Continue 也只有 Client 送出 Expect: 100-continue 時才會進入對應流程,而且常見瀏覽器通常不會自行送出這個 header。
壓縮與 Cache
伺服器可以根據 request 的 Accept-Encoding 選擇 gzip、Brotli 等 content coding,再透過 Content-Encoding 告訴瀏覽器如何解壓縮。這能減少網路上的傳輸量,但不代表原始 JavaScript、CSS 或圖片就不需要繼續控制大小。完整協商流程可以參考 HTTP 內容壓縮:透過 Accept-Encoding 減少傳輸量。
如果 response 允許快取,瀏覽器也可能直接重用仍然 fresh 的內容,或使用 ETag、Last-Modified 向伺服器進行 revalidation。不過 HTTP Cache、Cache Storage 與 BFCache 解決的是不同問題,不能全部當成同一套 cache。這一段可以接著看瀏覽器快取機制有哪些?。
收到 Response 後,瀏覽器怎麼處理?
開啟 Chrome DevTools 的 Network 面板並選擇一筆 request,可以先從 General 區塊看到這次請求的 URL、method、status code 與 remote address。這裡顯示的是 HTTP request 的摘要,不是底層實際傳送了幾個 TCP segment。

收到 response header 與部分資料後,瀏覽器會判斷內容類型並執行安全檢查。假如這次是 HTML document,瀏覽器不需要等整份檔案下載完成才開始處理,而是可以一邊接收資料、一邊解析並建立 DOM。
HTML parser 遇到 stylesheet、script、圖片或字型等內容時,還會觸發後續資源請求。這些資源被發現的時間、下載優先度與執行方式,都可能影響畫面多快出現。
CSS、JavaScript 與資源發現
CSS 裡的 @import 或 background-image,通常要等瀏覽器取得並解析 stylesheet 後才會被發現;直接寫在 HTML 裡的 <link> 則能更早被 parser 或 preload scanner 發現。若有確定會使用、但正常流程較晚才被找到的關鍵資源,可以評估使用 preload、modulepreload 等方式提示瀏覽器,詳細差異可以看優化資源的載入時機:Resource Hints。
一般的 classic script 會在下載與執行期間暫停 HTML parsing;加上 async、defer,或改用 module script 後,下載與執行時機都會不同。要怎麼選,取決於 script 是否依賴 DOM、其他 script 與固定執行順序,可以參考 script 中 async、defer 與 module 的差異。
Renderer Process 如何把內容變成畫面?
Renderer Process 取得 HTML、CSS 與 JavaScript 後,會逐步建立 DOM、計算 style、執行 Layout 與 Paint,再經過 Rasterization 和 Compositing 組成畫面。這不是一條每次都從頭跑到尾的固定流水線;後續更新會依變動內容,決定是否需要重新 Layout、Paint 或只做 Compositing。
想知道 DOM、CSSOM、Layout 與 Paint 各自負責什麼,可以繼續看瀏覽器渲染流程:從 DOM、CSSOM 到 Layout 與 Paint。
圖片也會直接影響傳輸量、解碼成本與畫面穩定性。可以先從圖片格式怎麼選?PNG、JPEG、SVG、WebP 與 AVIF開始,根據圖片內容、畫質與透明度需求選擇合適格式。
選好格式後,再透過響應式圖片怎麼做?srcset、sizes 與 picture,讓瀏覽器依照圖片顯示尺寸、viewport、DPR 與格式支援度選擇適合的候選資源。最後可以接著看延遲圖片載入需要注意什麼,判斷哪些非首屏圖片適合延後載入,以及如何利用圖片尺寸與 placeholder 降低版面位移。這樣就能依序處理「格式、尺寸、載入時機」三個問題。
預先準備下一次 Navigation
前面介紹的是使用者送出網址或點擊連結後,瀏覽器才開始執行 Navigation。那既然最後都要走過網路請求、解析與渲染,有沒有辦法在使用者真的前往下一頁之前,先完成一部分工作呢?
Speculation Rules API 可以讓網站描述使用者接下來可能前往的 document。瀏覽器可以依規則進行 prefetch;當下一次 Navigation 的機率夠高時,也可以使用 prerender,提前載入並準備整個頁面,等使用者真的前往時再 activate。
不過 Speculation Rules 是提示,不是命令,瀏覽器可以依使用者設定、裝置資源與其他條件忽略規則。如果預測錯誤,提前消耗的頻寬、CPU、記憶體與 Server 資源也可能全部變成浪費,所以不能看到連結就全部 prerender!詳細用法與限制可以參考如何透過 Speculation Rules 預先準備下一個頁面?。
優化前,先把問題量出來
從網址列按下 Enter 到畫面出現,中間有太多階段可能影響速度。DNS、TCP、TLS、Server response、資源下載、JavaScript、Layout 與 Paint 都可能是瓶頸,但不會每個網站都卡在同一個地方。
所以比起先背一份「一定要做的優化清單」,更實際的做法是先用 DevTools Network 與 Performance 面板找出時間花在哪裡,再回到對應階段處理。這樣做的優化才是在解決真正的問題,而不是讓專案多出一堆暫時用不到的複雜度!