ブラウザにWebページが表示されるまでの流れ
はじめに
タイトルの通り、ブラウザに URL が入力されてからページが表示されるまでの間に何が起こっているのかをまとめます。
いろいろな方が同じテーマで記事を書かれていますが、自分なりに理解を深めたいと思い調べてみました。
URL をパースする
URL をブラウザに入力してエンターを押すと、最初にブラウザは URL のパースを行います。
URL はプロトコル、ドメイン、パスに分けられます。
例としてhttps://foo.bar.com/bazというURLにアクセスすると以下のようになります。
この場合は https というプロトコルをつかって foo.bar.com の baz というリソースを要求しているということになります。
preload HSTS リストをチェックする
HSTS とは Web サイトにアクセスする際に HTTPS 通信を強制するための仕組みです。
ここではセキュリティの話は省略しますが、HTTP でのリクエストは、通信が暗号化されていないため中間者攻撃等のリスクがあります。
通常 SSL 化された Web サイトでは、HSTS を利用して http でリクエストされても https での通信にリダイレクトされるようになっていますが、最初の一回は http で通信するため完全にリスクを排除することはできません。
最初のアクセスから https でリクエストを行うため、ブラウザは HSTS プリロードリストを参照し、アクセスしようとするドメインがリストに登録されている確認します。
そしてリストに登録されている場合は、初回のアクセスから https でリクエストが送られるようになります。
このリストは Google が管理しており、次の URL(https://hstspreload.org/)から登録することができます。
IP アドレスの取得(DNS)
続いて行われるのは IP アドレスの取得です。
URL を入力しただけではリクエストを送るサーバーを特定することはできず、ネットワーク上の各機器を一意に識別するための IP アドレスが必要になります。
ドメインと IP アドレスを紐付けて管理しているのが DNS(Domain Name System)です。
ただしいきなり DNS にリクエストを行うのではなく、まずはキャッシュの確認から行われます。
最初に確認されるのはブラウザ自身のキャッシュです。
ブラウザのキャッシュにない場合は、ローカルに保存されている hosts ファイルを確認します。
hosts ファイルにも無い場合に、ブラウザに登録された DNS サーバーにリクエストを送ります。キャッシュ DNS サーバーにドメインが登録されていなかった場合は、ルートドメインサーバーにリクエストを転送し、名前解決ができるまで以下のプロセスを繰り返します。
- サーバーに登録された下位ドメインの DNS サーバーの IP アドレスを返す
- 下位 DNS サーバーにリクエストをおくる
こうして目的の IP アドレスが取得できると IP アドレスをメモリ領域に格納し、アプリケーションに返却します。
ARP(Address Resolution Protocol)
ARP は IP アドレスから MAC アドレスの解決を行うためのプロトコルです。[1]
ARP は同一リンク上のすべてのホストやルーターに ARP リクエストを送り、目的の MAC アドレスを取得します。
ソケットをつなぐ
IP アドレスが取得できると、OS がもつ Socket ライブラリを使って、当該サーバーにリクエストを送る準備をします。
ソケットとは双方のコンピューターをつなぐパイプの出入り口のようなもので、一方からパイプにデータを送るともう片方のコンピュータに届くようなイメージです。
ソケットをつなぐ動作は以下のステップで行われます。
- Socket ライブラリの socket を呼び出しソケットを作成する。
- ソケットが完成するとディスクリプタが帰ってくるのでメモリに格納する。(ディスクリプタ=ソケットを識別するために使用される)
- Socket ライブラリの connect を呼び出し、ソケットの接続を行う。その際に、ディスクリプタ、IP アドレス、ポート番号を指定する。(ポート番号はルールによって定められている。HTTP であれば 80 番、HTTP であれば 443 番)
- Socket ライブラリの write を呼び出し、HTTP リクエストの内容をメモリーに格納する。
HTTP リクエストを送信する
HTTP リクエストの内容が完成すると、クライアントは HTTP プロトコルを使って、以下の形式のリクエストを送信します。
GET /baz HTTP/2
Host: foo.bar.com
<メッセージボディ>
内容は上から順に以下のようになります。
- リクエストライン
- HTTP メソッド(GET)、リクエスト対象(/baz)、HTTP バージョン(HTTP/2)
- ヘッダー
- メッセージのメタデータを格納する。今回の例では Host に foo.bar.com を指定している。一つのメッセージで複数のヘッダーを持つこともできる。
- 空行
- メッセージボディ
- リソースを作成・更新するような場合には、リソースの表現をメッセージボディに格納する。
HTTP レスポンスを受け取る
HTTP リクエストが送信されるとサーバーサイドで HTTPD(Web サーバーの常駐プログラム)がリクエストを受け取り、リクエストに応じたレスポンスを以下の形式でクライアントに返します。
HTTP/2 200
content-type: text/html; charset=utf-8
<メッセージボディ>
内容は上から順に以下のようになります。
- ステータスライン
- プロトコルバージョン(HTTP/2)、ステータスコード(200)
- ヘッダー
- この例では Content-Type ヘッダでメディアタイプと、その文字エンコーディング方式を表している。
- 空行
- メッセージボディ
- ボディにはヘッダーに記載された内容のリソースのリソースが格納されている。今回の例では HTML。
HTML をレンダリングする
ブラウザが HTTP レスポンスを受け取ると以下の手順で Web ページを画面に表示します。
-
HTML の解析
- ブラウザのレンダリングエンジンが HTML のデータを受け取り DOM ツリーを生成する。
-
CSS の解析
- DOM ツリーを生成している際に、CSS ファイルへの参照がある場合、CSS の解析に移る。CSS も HTML と同様に CSSOM ツリーという構造に変換される。
-
JavaScript の実行
- DOM ツリーを生成している際に、JavaScript への参照がある場合、script の実行に移る。JavaScript が実行されることによって DOM や CSSOM の内容が変化する。
- JavaScript を実行している間はドキュメントの解析は止まる(Parser Blocking)。
-
Render ツリーの生成
- DOM ツリーと SSOM ツリーが完成すると、その二つを結合して Render ツリーが生成される。
引用:https://web.dev/critical-rendering-path-render-tree-construction/
- レイアウト
- Render ツリーに基づいて、ページの各要素の一とサイズを計算し、ビューボートに合わせてレイアウトを行う。
- 画面にページを描画する
さいごに
以上がブラウザに URL を入力してから画面が表示されるまでの大まかな流れです。
実際には通信の中身や、HTML 解析の流れはもっと深掘りすることができますが、ポイントになる部分をまとめてみました。
すこしでも参考になれば幸いです。
参考
- https://github.com/alex/what-happens-when
- https://web.dev/howbrowserswork/
- https://developer.mozilla.org/ja/docs/Web/HTTP
- https://zenn.dev/ak/articles/61d25099295372
-
ARP は IPv4 でのみ利用されるプロトコルです。IPv6 では NeighborDiscovery(近隣探索メッセージ)が利用されます。 ↩︎
Discussion