TL

ブラウザの投機的パース処理とリソース優先度

なぜ script を head に置くと表示が止まり、画像に fetchpriority を付けると LCP が縮むのか。プリロードスキャナとリソース内部優先度の原理を、効かせ方まで詳説します。

応用ブラウザパフォーマンスHTMLリソース読み込みCore Web Vitals最終更新: 2026-06-21
TL;DR要点だけ先に
  • 1.HTMLパーサ本体が同期 script で止まっている間も、プリロードスキャナが先のトークンを走査して img・CSS・script などのURLを先行ダウンロードする。これが初期表示の取得を前倒しする中核。
  • 2.同期 script はパースをブロックし、後続の script は先行する CSSOM 構築の完了を待つ。だから head の重い script は描画を直列に止める。
  • 3.ブラウザは要素種別・位置・属性からリソースに内部優先度を割り当てる。fetchpriority はその値を high/low に上書きするヒントで、特に LCP 画像の前倒しに効く。

投機的パースとは何か:メインパーサと先読みの二段構え

ブラウザがHTMLを受信してDOMを組み立てるパーサは、本質的に直列です。トークンを先頭から順に消費し、<script>(インラインまたは同期外部)に出会うとスクリプトの取得・実行が終わるまでDOM構築を止めます。素朴に実装すると、この停止中はネットワークが完全に遊んでしまい、後続に並ぶCSSや画像のダウンロードが始まりません。

これを救うのがプリロードスキャナ(preload scanner、投機的パーサ)です。メインパーサが詰まっている間、スキャナは入力バッファのまだDOM化していない先の部分を軽量に走査し、src / href を持つトークン(imglink rel=stylesheetscript srcvideoposter など)からURLを抜き出して先行的に取得を開始します。DOMツリーは作らず、属性も完全には解釈しない「投機的」な読みなので、メインパーサより速く先へ進めます。

メインパーサ:  <head>……<script>―――(取得+実行で停止)―――▶ …<body><img>…
                                    ▲ここで停止しても
プリロードスキャナ: ───先のバッファを走査して img/css/js のURLを先に発見し取得開始───▶

要点は、取得(フェッチ)の前倒しであってDOM構築の前倒しではないことです。スキャナは「何を取りに行くべきか」を早く知るための仕組みであり、実行順序やDOMの意味づけは依然メインパーサが担います。基礎の全体像は ブラウザのレンダリングの仕組み を参照してください。

パーサブロッキング:script と CSS が描画を止める条件

なぜ <script> の位置が決定的なのか。鍵はスクリプトとスタイルの相互依存にあります。

  • 同期 script はパースをブロックする。 <script src>async / defer なし)はDOM構築を止め、取得と実行を完了させてから次へ進みます。これはスクリプトが document.write で後続のトークン列を書き換えうるため、ブラウザが先のDOMを確定できないからです。
  • script は先行する CSS の完了を待つ。 スクリプトは getComputedStyle などでスタイルを読み取れるため、ブラウザは未完成のCSSOMでスクリプトを走らせるわけにいきません。結果として、ある同期スクリプトの実行は、それより前に現れた <link rel=stylesheet>CSSOM構築完了を待ちます
head の同期 script が描画を直列に止める仕組み

head に <link rel=stylesheet> と同期 <script> がこの順で並ぶと、(1) CSS取得→CSSOM構築、(2) その完了を待ってスクリプト実行、(3) ようやくパース再開、と直列になります。スクリプト自体は描画をブロックしますが、その手前でCSSの完了待ちまで挟まるため、体感はさらに悪化します。async / defer を付けるとパースブロッキングが外れます。

指定取得タイミング実行タイミングパースブロック
script(無印・外部)発見後すぐ取得完了後すぐ(DOM到達順)する
script async発見後すぐ取得完了次第(順不同)しない
script defer発見後すぐパース完了後・DOMContentLoaded前(記述順)しない
link rel=stylesheet発見後すぐ描画をブロック(後続scriptも待たせる)

CSS はレンダリングブロッキングリソースです。スタイルが未確定のまま描画すると、いわゆるスタイルなしコンテンツのちらつき(FOUC)が起きるため、ブラウザは重要なCSSのCSSOM構築が終わるまで最初のペイントを抑制します。async / defer でも、document.write を使わないスクリプトでも、このCSSの描画ブロックは別の話として残ります。

リソースの内部優先度:ブラウザが自動で付ける順位

取得すべきURLが分かっても、回線とサーバーの同時接続には限りがあります。そこでブラウザは各リクエストに内部優先度(resource priority)を割り当て、スケジューラがこの優先度に従って発行順とネットワーク上の重みを決めます。HTTP/2 以降では、これがストリームの重み・依存としてサーバーへ伝わります(詳細は HTTP/2の多重化とHPACKヘッダ圧縮の原理 を参照)。

優先度はおおむね次の観点から自動決定されます(Chromium の挙動を基準とした概略で、ブラウザ差があります)。

リソース既定の優先度(概略)決め手
HTML(ドキュメント)Highest後続すべての起点だから
CSS(head のstylesheet)Highest描画をブロックするため最優先
同期 script(早い位置)Highパースを止めるため
script async / deferLow描画を妨げないため
フォントHighテキスト描画に直結
ビューポート内の画像Low→Mediumレイアウト確定後に引き上げ
ビューポート外の画像Lowest今は見えないため

ここで効いてくるのが位置とビューポートです。画像はマークアップ発見時点では既定で低優先ですが、レイアウトが進んで初期ビューポート内にあると判明すると、ブラウザは優先度を引き上げ直すことがあります。逆に画面外の画像は低いまま据え置かれます。<head> 内のCSSやフォントが高いのは、それらが描画を直接律速するからです。

プリロードスキャナと優先度は別レイヤー

プリロードスキャナは「いつ発見するか(前倒し)」、内部優先度は「発見したものをどの順で流すか(重み付け)」を担います。スキャナが早く画像を見つけても、その画像が低優先なら、高優先のCSSやフォントの後ろに回されます。両者を分けて理解すると、後述の fetchpriority がどこに効くかが明確になります。

Priority Hints(fetchpriority)の効き方と限界

fetchpriority 属性(high / low / auto)は、ブラウザが自動算出した内部優先度を上書きするヒントです。順序を直接固定するのではなく、スケジューラへの優先度シグナルを変える点が肝心です。

<!-- LCP となるヒーロー画像を最優先で取りに行かせる -->
<img src="hero.webp" fetchpriority="high" width="1200" height="630" alt="主役の写真" />

<!-- 重要度の低い装飾画像やサードパーティ画像を後ろへ下げる -->
<img src="decoration.webp" fetchpriority="low" alt="" />

<!-- preload と併用して、本当に最初に要るものを明示 -->
<link rel="preload" as="image" href="hero.webp" fetchpriority="high" />

最も費用対効果が高い使い所はLCP画像です。前述のとおり画像の既定優先度は低く、ブラウザがビューポート内と判定して引き上げるまでに一拍遅れが出ます。fetchpriority="high" を主役画像に付けると、その判定を待たず初手から高優先で取得を始められ、LCP が縮みます。逆に、ファーストビューに不要なカルーセルの2枚目以降などを low に下げると、重要リソースへ帯域を譲れます。

preload と fetchpriority は役割が違う

<link rel="preload"> は「プリロードスキャナより前に、確実に発見させる」ための先回り宣言です。一方 fetchpriority は「発見済みリソースの優先度を変える」ヒントです。CSS の background-image や JS で動的生成する画像など、スキャナが見つけられないリソースは preload で発見を早め、そこへ fetchpriority="high" を重ねると効果が最大化します。

効かない・逆効果になる典型

fetchpriority="high" を多くの要素に付けると、優先度の差が消えて全体が遅くなります。優先度は相対的な順位付けなので、「全部 high」は「全部 normal」と同義です。また fetchpriorityあくまでヒントで、<head> のCSSやフォントといった本来高優先のリソースを押しのけて画像を最優先化することは保証されません。レンダリングブロッキングの解消(async / defer、クリティカルCSSの分離)が先で、fetchpriority はその上の微調整です。

実務での適用指針

押さえどころ

出題・実務で問われるのは、(1) プリロードスキャナが「取得の前倒し」であってDOM構築の前倒しではない点、(2) 同期 script がパースをブロックし、かつ先行CSSのCSSOM完了を待つ理由(document.write とスタイル読み取り)、(3) 内部優先度が要素種別・位置・ビューポートで自動決定される点、(4) fetchpriority がその優先度を上書きする「ヒント」であり、相対順位なので濫用すると無効化する点、の4つです。

  • クリティカルパスを直列にしない:head の同期 script を避け、defer(記述順を保つ)か async(順不同で良いもの)へ。CSS は必要分を小さく保ち、描画ブロック時間を短くする。
  • LCP画像を前倒し:主役画像に fetchpriority="high" を付け、必要なら preload で発見も早める。ファーストビュー画像に loading="lazy" は付けない。
  • スキャナの死角を埋める:CSS背景やJS生成のリソースはスキャナに見えないため、preload で明示する。
  • 優先度は相対high は本当に効かせたい1〜2個に絞り、不要なものを low に下げて差を作る。

まとめ

まとめ

ブラウザはメインパーサでDOMを直列に組みつつ、プリロードスキャナで先のトークンを走査して画像・CSS・JSの取得を前倒しします。同期 script はパースをブロックし、先行CSSのCSSOM完了まで待つため、head の重い script は描画を直列に止めます。取得すべきURLが決まると、ブラウザは要素種別・位置・ビューポートから内部優先度を自動付与し、fetchpriority はその値を上書きするヒントとして働きます。特に低優先になりがちなLCP画像へ high を与えると表示が縮みます。仕上げに レンダリングパイプライン詳説Web パフォーマンス を合わせると、取得から描画までの律速点を一本の線で説明できます。

Web/フロントエンド Article

ブラウザの投機的パース処理とリソース優先度を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

ブラウザ

比較で見る軸

難易度: advanced / カテゴリ: Web/フロントエンド / タグ数: 5

導入後に効く点

同期 script はパースをブロックし、後続の script は先行する CSSOM 構築の完了を待つ。だから head の重い script は描画を直列に止める。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
Web/フロントエンド
タグ数
5

判断チェックリスト

  • 自社の用途が「ブラウザ / パフォーマンス」に近いか確認する。
  • 強みである「HTMLパーサ本体が同期 script で止まっている間も、プリロードスキャナが先のトークンを走査して img・CSS・script などのURLを先行ダウンロードする。これが初期表示の取得を前倒しする中核。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

ブラウザパフォーマンスHTMLリソース読み込みCore Web VitalsブラウザパフォーマンスHTML
参考: 公式情報