クリティカルレンダリングパスの最適化原理
初回表示までに本当に必要なリソースだけを最短経路で届けられるようになる。CSSのレンダリングブロックとJSのパーサブロッキングを解く順序設計を、原理から組み立てます。
- 1.クリティカルレンダリングパスとは、HTMLバイト列を最初のピクセル(First Paint)まで変換する最短経路で、DOM構築・CSSOM構築・レンダーツリー結合・レイアウト・ペイントの各段からなる。
- 2.CSSはレンダーツリーの前提なのでレンダリングブロックリソース、外部JSは構築中のDOM/CSSOMを書き換えうるのでパーサブロッキングリソース。両者を最小化・遅延化することが最適化の本質。
- 3.順序設計の要点は、初回表示に必要な最小リソース集合(クリティカルリソース)をプリロードスキャナで早く拾わせ、それ以外を defer / async / 遅延CSSでパスから外すこと。
クリティカルレンダリングパスとは何か
クリティカルレンダリングパス(Critical Rendering Path, CRP)とは、サーバーから届いた HTML のバイト列を、画面上の最初のピクセル(First Paint や、意味のある内容が出る First Contentful Paint)へ変換するまでに必ず通る最短経路を指します。ブラウザはこの経路上で、おおむね次の段を順に踏みます。
- DOM 構築:HTML バイト列をトークン化し、ノードのツリー(DOM)へ組む。
- CSSOM 構築:CSS バイト列を解析し、スタイルのツリー(CSSOM)へ組む。
- レンダーツリー結合:DOM と CSSOM を合わせ、実際に描画される要素だけのツリーを作る(
display:noneは除外、visibility:hiddenは残る)。 - レイアウト(リフロー):各ノードの位置とサイズをビューポート基準で確定する。
- ペイント:確定したジオメトリを実ピクセルへ塗る。
最適化の問いはひとつです。「この最短経路を、どれだけ短く・どれだけ早く完了させられるか」。鍵は、経路をブロックするリソースを特定し、初回表示に不要なものをパスから外すことにあります。段ごとの内部動作は ブラウザのレンダリング と レンダリングパイプライン詳説 も参照してください。
なぜ CSS と JS が経路をブロックするのか
CRP を理解する核心は、何が・なぜブロックするのかを構築アルゴリズムから説明できることです。
| リソース | 分類 | ブロックする理由 |
|---|---|---|
| 外部CSS(link) | レンダリングブロック | レンダーツリーは CSSOM 完成を前提とするため |
| インラインCSS(style) | 実質ブロックしない | 外部往復が不要で即座に CSSOM へ反映される |
| 外部JS(script) | パーサブロッキング | DOM/CSSOM を書き換えうるためパーサを止める |
| script defer | ブロックしない | ダウンロードは並行、実行は DOM 構築後 |
| script async | 部分的にブロック | 取得完了時点に割り込み実行(順序保証なし) |
CSS がレンダリングブロックリソースである理由は、レンダーツリーが DOM と CSSOM の両方を入力にするからです。CSSOM が未完成のまま描画すると、後から到着したスタイルで見た目が崩れる(FOUC: Flash of Unstyled Content)。これを避けるため、ブラウザは外部 CSS をすべて取得・解析し CSSOM を構築するまで、レンダーツリー結合以降を進めません。つまり CSS は「ファイルの取得 + 解析」の往復まるごとが初回表示の待ち時間に直結します。詳細度やカスケードの基礎は CSS を参照してください。
JS がパーサブロッキングである理由は別です。<script> は実行時に document.write や DOM API でツリーを書き換えうるため、HTML パーサはスクリプトの取得と実行が終わるまでパースを止めます。さらに、その JS が getComputedStyle などでスタイルを読む可能性があるため、先行する CSS の CSSOM 構築が終わるまでスクリプト実行自体も待たされます。結果として「CSS がスクリプトをブロックし、スクリプトが DOM 構築をブロックする」という連鎖が生まれます。
head 内に「外部CSS → 直後に外部JS」を素朴に並べると、JS は CSS の往復を待ってから実行され、その間 DOM 構築が完全に止まります。CSS を待つのは実装上の安全策(スクリプトがスタイルを読めるようにするため)であり、たとえそのスクリプトがスタイルに触れなくても保守的に待ちます。だから「重い同期スクリプトを head に置く」のは CRP 上もっとも避けたい配置です。
プリロードスキャナ:パーサが止まっても先回りで取りに行く
パーサが同期スクリプトで止まっている間、ネットワークが遊んでしまうと致命的です。これを救うのが**プリロードスキャナ(preload scanner)**です。メインの HTML パーサとは別に、受信バイト列を先読みして src / href を持つリソース参照だけを抜き出し、パーサがブロックされていても並行でダウンロードを開始します。
ここに最適化の重要原則が出ます。プリロードスキャナは生の HTML テキストしか見ません。したがって、
- JS で動的に注入する(
document.createElement('script')など)リソースは先読みされない。発見が遅れる。 - CSS の
@importで連鎖した CSS は、外側の CSS を取得・解析するまで存在が分からず、直列の往復になる。
逆に、初回表示に必須のリソースは <link rel="preload"> で明示的に宣言すれば、プリロードスキャナが最優先で発見できます。これが「クリティカルリソースを早く拾わせる」具体的手段です。HTTP/2 以降の多重化と組み合わせると、発見済みリソースを並行で効率よく取得できます(HTTP/2の多重化)。
最小リソース集合(クリティカルリソース)の特定
CRP 最適化の出発点は、初回表示に本当に必要なリソースの集合を切り出すことです。判定基準は明確で、「そのリソースが First Contentful Paint より前に必要か」だけを問います。
クリティカルリソース集合 = { ファーストビューを描くために必須なもの }
含む : 初回表示に効く CSS(Above-the-fold のスタイル)
初回表示の文字を出すフォント(任意。FOIT/FOUT 戦略次第)
除く : スクロールしないと見えない領域の画像
初回操作まで不要な機能スクリプト
解析計測・チャットなどの第三者スクリプト
実装の定石はクリティカル CSS のインライン化です。ファーストビューに効くスタイルだけを抽出して <head> に <style> で直接埋め込み、残りの CSS は非ブロックで遅延読込します。
<head>
<!-- 初回表示に必須のスタイルだけをインライン化(外部往復ゼロ) -->
<style>/* above-the-fold critical css */</style>
<!-- 残りCSSはレンダリングをブロックさせず後追いで適用 -->
<link rel="preload" href="/full.css" as="style"
onload="this.rel='stylesheet'">
</head>
rel="preload" ... as="style" は CSS を非ブロックで取得し、onload で stylesheet へ昇格させて適用します。これにより、外部 CSS の往復を初回表示のクリティカルパスから外せます。
<link rel="stylesheet" href="print.css" media="print"> のように media 属性が現在の条件に合致しない CSS は、ブラウザがダウンロードはしてもレンダリングブロックとは扱いません。印刷用・特定幅専用のスタイルを別ファイルに分け適切な media を付けるだけで、初回表示の待ち時間からそれらを除外できます。
JS のブロックを解く:defer と async の使い分け
外部スクリプトのブロックを解く正攻法が defer と async です。挙動の違いは実行タイミングと順序保証にあります。
| 属性 | ダウンロード | 実行タイミング | 順序保証 |
|---|---|---|---|
| なし(同期) | 実行直前に同期取得 | 発見した位置で即時(パーサ停止) | 記述順 |
| defer | 並行(パースと同時) | DOMContentLoaded 直前・DOM完成後 | 記述順を保証 |
| async | 並行(パースと同時) | 取得完了し次第に割り込み | 保証なし(到着順) |
原理に基づく使い分けは次の通りです。
- DOM 全体に依存し、実行順が重要なアプリ本体:
defer。DOM 構築をブロックせず、構築完了後に記述順で走るため、依存関係が壊れません。 - 他と独立した第三者スクリプト(計測・広告など):
async。早く来たものから即実行でき、ページ本体と切り離せます。順序に依存しないことが前提です。 type="module"は既定でdefer相当の遅延実行になります(明示的にasyncも指定可能)。
<!-- アプリ本体:DOMを止めず、順序を保ったまま構築後に実行 -->
<script src="/app.js" defer></script>
<script src="/widgets.js" defer></script>
<!-- 独立した計測:本体を待たせず到着順に実行 -->
<script src="/analytics.js" async></script>
async は到着順で実行されるため、ライブラリ本体とそれに依存するコードを両方 async にすると、依存先が未ロードのまま依存元が走り壊れます。順序が要るものは必ず defer(または import でモジュールグラフに解決させる)を使ってください。「とりあえず async」は CRP を縮める一方で実行順バグを生みます。
順序設計:パスを最短化するレイアウト
ここまでの原理を一本のレイアウトに落とすと、<head> から </body> までの並べ方が決まります。
<head>
1. <meta charset> を最優先(文字コード未確定だと再パースが起きる)
2. クリティカルCSSをインライン <style>(外部往復ゼロで CSSOM 確定)
3. 必須リソースを <link rel="preload">(プリロードスキャナに最優先で発見させる)
4. 非クリティカルCSSは preload→onload で非ブロック適用
<body>
5. ファーストビューのマークアップを先に
6. スクリプトは defer(本体)/ async(独立)で末尾相当の遅延に
</head>
この設計の狙いは三つです。第一に、CSSOM を外部往復なしで即確定して、レンダリングブロックの待ちをゼロに近づける。第二に、プリロードスキャナが最小リソース集合を即発見できるよう、参照を生の HTML に静的に書く。第三に、JS を DOM 構築から切り離すことで、パーサブロッキングの連鎖を断つ。結果として「最小のクリティカルリソースだけが、最短の往復で、並行に届く」状態になります。
CRP 系の問いでは、(1) 経路の段順(DOM→CSSOM→レンダーツリー→レイアウト→ペイント)、(2) CSS がレンダリングブロック・外部JSがパーサブロッキングである理由、(3) CSS がスクリプト実行を待たせる連鎖、(4) プリロードスキャナが生のHTMLしか見ないこと、(5) defer(順序保証・DOM後)と async(順序なし・即時)の差、が頻出です。
計測との接続
CRP の良し悪しは体感指標に直結します。クリティカルパスが長いほど First Contentful Paint が遅れ、レンダリングブロック CSS や同期 JS は Largest Contentful Paint の遅延要因になります。改善の効果は計測で確認するのが原則です(Web パフォーマンス(Core Web Vitals))。
- クリティカルリソース数:初回表示までに取得が必要なファイル数。少ないほど良い。
- クリティカルバイト数:それらの総転送量。圧縮・分割で減らす。
- クリティカルパス長:直列に発生する往復の段数。
@import連鎖や動的注入が増やす。
まとめ
クリティカルレンダリングパスは DOM→CSSOM→レンダーツリー→レイアウト→ペイント の最短経路で、初回表示はこの経路の完了に律速されます。外部 CSS は CSSOM 完成を前提とするためレンダリングブロック、外部 JS は DOM/CSSOM を書き換えうるためパーサブロッキングで、しかも CSS が JS 実行を待たせる連鎖を生みます。最適化の本質は、初回表示に必要な最小リソース集合を切り出してクリティカル CSS をインライン化し、preload でプリロードスキャナに早く発見させ、残る JS を defer/async で経路から外すこと。仕上げに ブラウザのレンダリング の段理解と Web パフォーマンス の計測を合わせれば、配置から数値改善までが一本の筋で通ります。
Web/フロントエンド Article
クリティカルレンダリングパスの最適化原理を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
レンダリング
比較で見る軸
難易度: advanced / カテゴリ: Web/フロントエンド / タグ数: 6
導入後に効く点
CSSはレンダーツリーの前提なのでレンダリングブロックリソース、外部JSは構築中のDOM/CSSOMを書き換えうるのでパーサブロッキングリソース。両者を最小化・遅延化することが最適化の本質。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- Web/フロントエンド
- タグ数
- 6
判断チェックリスト
- 自社の用途が「レンダリング / ブラウザ」に近いか確認する。
- 強みである「クリティカルレンダリングパスとは、HTMLバイト列を最初のピクセル(First Paint)まで変換する最短経路で、DOM構築・CSSOM構築・レンダーツリー結合・レイアウト・ペイントの各段からなる。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。