---
title: 103 Early Hintsで始める、サーバー・フロントの協調最適化
tags: 
author: [_ma_me_](https://image.docswell.com/user/1313108)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/VJNYNR6178.jpg?width=480
description: 103 Early Hintsで始める、サーバー・フロントの協調最適化 by _ma_me_
published: June 05, 26
canonical: https://image.docswell.com/s/1313108/K6NJWP-2026-06-05-201721
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/VJNYNR6178.jpg)

103 Early Hintsで始める、
サーバー・フロントの協調最適化
1


# Page. 2

![Page Image](https://bcdn.docswell.com/page/YE9PRMLYJ3.jpg)

Webページの表示には通信が必須
サーバーがHTMLを生成している間
描画やリソースの取得は止まる
2


# Page. 3

![Page Image](https://bcdn.docswell.com/page/GE8DWLXKED.jpg)

Early Hintsが解決すること
HTML生成中の待ち時間を使い、
CSS / JS / font などのリソース取得が開始
結果、画面描画が早くなる！
HTMLのレンダリング中に、CSSの取得が並行して始まっている様子
3


# Page. 4

![Page Image](https://bcdn.docswell.com/page/LELMNL8P7R.jpg)

HTML生成中の待ち時間を埋めるのが、
HTTP 103 Early Hints
4


# Page. 5

![Page Image](https://bcdn.docswell.com/page/4JMYXM62JW.jpg)

このセッションのゴール
バックエンドで どう実装するのか
ブラウザが どう解釈・活用するのか
どのUX指標が どれだけ改善するのか
5


# Page. 6

![Page Image](https://bcdn.docswell.com/page/PJR9NVP579.jpg)

プロフィール
発表者
ma@me
所属
最近の業務
品質改善・不具合対応
6


# Page. 7

![Page Image](https://bcdn.docswell.com/page/PEXQNZ3XJX.jpg)

1xx のステータスコードの特徴
暫定レスポンスで、レスポンスボディを持たない
レスポンスが2回返る
ブラウザ
Request
1xx 暫定レスポンス（ボディなし）
Server
処理は継続
最終レスポンス（2xx など）
7


# Page. 8

![Page Image](https://bcdn.docswell.com/page/3EK9N8Y9ED.jpg)

レスポンスが2回返っている
HTML リクエストに 103 と 200 が並んでいる様子
8


# Page. 9

![Page Image](https://bcdn.docswell.com/page/L73WV29975.jpg)

Early Hintsのケース
通常の200ステータスのケース
9


# Page. 10

![Page Image](https://bcdn.docswell.com/page/87DK8ZG8JG.jpg)

103 応答ヘッダーの中身
約165バイトのヘッダーのみで、ボディよりも先に届く
Link: 〜の部分が先行取得の指示
server: 103の送出箇所。このケースではFrankenPHPのCaddyから送出
10


# Page. 11

![Page Image](https://bcdn.docswell.com/page/VJPK8D3WE8.jpg)

Early Hintsを含む
歴代の先行取得手法を紹介
11


# Page. 12

![Page Image](https://bcdn.docswell.com/page/2EVVN149EQ.jpg)

① HTTP/2 Server Push
ブラウザで再現できないので、イメージ図
登場年代：2015
サーバーが要求されていないリソースをHTMLと同時に押し出す仕組み。
ブラウザのキャッシュ状況をサーバーが把握できず、不要なリソースまで送
ってしまうため廃止
12


# Page. 13

![Page Image](https://bcdn.docswell.com/page/57GLK214EL.jpg)

② &lt;link rel=&quot;preload&quot;&gt;
&lt;link rel=&quot;preload&quot; href=&quot;/style.css&quot; as=&quot;style&quot;&gt;
&lt;link rel=&quot;preload&quot; href=&quot;/app.js&quot; as=&quot;script&quot;&gt;
登場年代：2016
指定したリソースを優先的に先行取得するようブラウザに指示する
HTML内で完結するため、サーバー側の対応が不要。主要ブラウザで広く対
応済み
HTMLとは直列の取得になる
13


# Page. 14

![Page Image](https://bcdn.docswell.com/page/4EQYNPDRJP.jpg)

③ 103 Early Hints
登場年代：RFC 2017 / ブラウザ実装 2022
HTML生成中にリソースの先行取得を始めるため、HTMLとリソースの取得
が並列で進む。
14


# Page. 15

![Page Image](https://bcdn.docswell.com/page/KJ4WGYZQ71.jpg)

各手法と取得タイミングまとめ
手法
タイミング
主導権
HTTP/2 Server Push
レスポンス時
サーバー
&lt;link rel=&quot;preload&quot;&gt;
HTML到達後
ブラウザ
103 Early Hints
HTML生成中
サーバー
15


# Page. 16

![Page Image](https://bcdn.docswell.com/page/LE1YD6RL7G.jpg)

103は、本レスポンス到達前の空白時間を有効活用
① &lt;link rel=&quot;preload&quot;&gt; ― HTML到達後に発火
③ 103 Early Hints ― 本レスポンス(200)到達前にヒント送信
16


# Page. 17

![Page Image](https://bcdn.docswell.com/page/GEWGYW1QJ2.jpg)

103 Early Hints に対してサーバー側が
どう主導権を握り
どう扱うのか
サーバー構成での実装を通して紹介
17


# Page. 18

![Page Image](https://bcdn.docswell.com/page/47ZLX5P3J3.jpg)

×
Hono
×
Cloudflare
PHP / Hono：バックエンドの処理を担当
Nginx / Cloudflare CDN：103 の生成・送出
18


# Page. 19

![Page Image](https://bcdn.docswell.com/page/YJ6W49MZJV.jpg)

構成の全体像
Client
Node
フロント Nginx
Request
サイドカー
Request
内部Nginx + PHP
Request
② 103 Early Hints
③ 200 OK（本文）
19


# Page. 20

![Page Image](https://bcdn.docswell.com/page/GJ5MQNZ9J4.jpg)

アプリは103を書かない
生成はインフラ / CDN 層任せ
PHP ＋ Nginx / サイドカー
アプリは普通に200を返すだけ。103はサイドカー/Nginxが送
出
Hono ＋ Cloudflare
アプリは103を発行しない。CloudflareがエッジでLinkから103
を生成
20


# Page. 21

![Page Image](https://bcdn.docswell.com/page/LE3WV292E5.jpg)

103 を生成・転送する2つの部品
バックエンドの処理を待たず、サイドカーが
res.writeEarlyHints() で 103 を生成。
それをフロントの Nginx が early_hints on; でクライアント
へ転送する。
location / {
early_hints on;
# ★ upstream の 103 をクライアントへ転送
proxy_pass http://hints:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
21


# Page. 22

![Page Image](https://bcdn.docswell.com/page/8EDK8ZG67G.jpg)

Early Hints が届く様子
curl -v --http1.1 -H &#039;Accept: text/html&#039; http://localhost:8888/demo
&lt; HTTP/1.1 103 Early Hints
← ① 先に届く（サイドカーが送出 / Nginx
&lt; Link: &lt;/css/demo.css&gt;; rel=preload; as=style
&lt; HTTP/1.1 200 OK
&lt; x-powered-by: PHP/8.4.21
← ② 約200ms後（PHPの本文）
← 200 は PHP-FPM 由来
ブラウザでの見え方
22


# Page. 23

![Page Image](https://bcdn.docswell.com/page/V7PK8D8ZJ8.jpg)

色々設定が大変だと感じた方
FrankenPHPなら
headers_send(103); を差し込めば
OK！
https://frankenphp.dev/
23


# Page. 24

![Page Image](https://bcdn.docswell.com/page/2JVVN1NMJQ.jpg)

FrankenPHPって何？という方へ
過去のFrankenPHP関連の登壇スライド
どこまで違う？！PHP実行環境パフォーマンス対決 - mod_php vs php-fpm
vs Swoole vs FrankenPHP
Node.jsに頼らずにFrankenPHPでリアルタイムWeb通信を実現する
Laravel OctaneはFrankenPHPをどう高速化しているのか？ソースコードか
ら読み解く、高速化の仕組み
24


# Page. 25

![Page Image](https://bcdn.docswell.com/page/5EGLK2KXJL.jpg)

実装ができても、どの画面でも効くわけで
はない
ここからは Early Hints が効く画面を見極め
る話
25


# Page. 26

![Page Image](https://bcdn.docswell.com/page/4JQYNPN57P.jpg)

103 が効く2つの画面構成
1. サーバー処理が長く、描画開始までの待ち時間が長い
2. クリティカルリソースが固定
26


# Page. 27

![Page Image](https://bcdn.docswell.com/page/K74WGYGVE1.jpg)

1. サーバー処理が長く
描画開始までの待ち時間が長い画面
サーバー側の処理で、本レスポンスまでに時間がかかる（目
安：200ms〜）
DB クエリ
外部 API 呼び出し
など
27


# Page. 28

![Page Image](https://bcdn.docswell.com/page/LJ1YD6D4EG.jpg)

イメージ
コンテンツ例
EC のカート / 注文確認
ダッシュボード
検索結果 / 一覧画面
認証後のマイページ
28


# Page. 29

![Page Image](https://bcdn.docswell.com/page/GJWGYWYZ72.jpg)

2. クリティカルリソースが固定の画面
このページなら必ずこれを読む、リソースがバチっと決まって
いる画面
29


# Page. 30

![Page Image](https://bcdn.docswell.com/page/4EZLX5XL73.jpg)

LP イメージ
コンテンツ例
LCP になるヒーロー画像
Web フォント
ファーストビューの CSS
エントリ JS
30


# Page. 31

![Page Image](https://bcdn.docswell.com/page/Y76W494M7V.jpg)

パフォーマンス検証
1. サーバー処理が長く、描画開始までの待ち時間が長い
2. クリティカルリソースが固定
実際にどれくらい速くなるのか、計測結果を見ていく。
31


# Page. 32

![Page Image](https://bcdn.docswell.com/page/G75MQNQQ74.jpg)

FCP / LCP とは？
FCP（First Contentful Paint）
最初のテキストや画像が表示された瞬間
LCP（Largest Contentful Paint）
主要コンテンツ（最大要素）が表示された瞬間
＝ ユーザーが体感する表示完了
32


# Page. 33

![Page Image](https://bcdn.docswell.com/page/9J29P5PWER.jpg)

計測対象のサイト
同じ HTML を返す2つのエンドポイントを用意。
共通条件
HTML を返すまでの遅延：200ms（両エンドポイント共通）
＝ 200 OK までの TTFB：約 200ms
各アセット / API の遅延：各 200ms
※ TTFB は 103 の性質上、早く出てしまうため、参考数値扱い。最初のバイトが届いてもレンダリン
グが始まらない。
33


# Page. 34

![Page Image](https://bcdn.docswell.com/page/DEY45K59JM.jpg)

計測対象のサイト
項目
/demo-no-early
/demo-early
103 Early Hints の送信
なし
あり（ headers_send(103) ）
preload 対象アセット
—
demo.css / inter-bold.woff2 / demo.js
ダッシュボード
34


# Page. 35

![Page Image](https://bcdn.docswell.com/page/VJNYNRND78.jpg)

計測結果
① サーバー処理が長く、描画開始までの待ち時間が長い画面
指標
なし
あり
差
218 ms
10 ms
−208 ms
FCP
504 ms
308 ms
−196 ms
LCP
800 ms
384 ms
−416 ms（−52%）
全リソース完了
693 ms
269 ms
−424 ms
TTFB
⚠️
35


# Page. 36

![Page Image](https://bcdn.docswell.com/page/YE9PRMR8J3.jpg)

改善事項
LCP が 800ms → 384ms（−52%）に短縮
リソース読み込み遅延が 260ms → 7ms にほぼ消滅
LCP 計測時点でヒーロー画像はダウンロード完了済み
36


# Page. 37

![Page Image](https://bcdn.docswell.com/page/GE8DWLWZED.jpg)

ネットワークタブ①
103なし
HTML 完了後に CSS, JS が直列で開始。
font はさらに後（発見カスケード）
103あり
HTML 生成中に CSS, JS, font, 画像を並行ダウンロード
※ ①（ダッシュボード）のネットワークタブ実測イメージ（数値は別計測）
37


# Page. 38

![Page Image](https://bcdn.docswell.com/page/LELMNLN17R.jpg)

計測対象
② クリティカルリソースが固定の画面
38


# Page. 39

![Page Image](https://bcdn.docswell.com/page/4JMYXMX5JW.jpg)

計測結果②
全体指標
指標
なし
あり
差
13 ms
9 ms
−4 ms
FCP
300 ms
308 ms
+8 ms
LCP
300 ms
308 ms
+8 ms
全リソース完了
499 ms
228 ms
−271 ms（−54%）
TTFB
⚠️
サブリソース別（取得完了）
リソース
なし
あり
差
CSS
441 ms
227 ms
−214 ms
JS
440 ms
228 ms
−212 ms
font
682 ms
228 ms
−454 ms
画像
695 ms
267 ms
−428 ms
39


# Page. 40

![Page Image](https://bcdn.docswell.com/page/PJR9NVNZ79.jpg)

改善事項
全リソース完了が 499ms → 228ms（−54%）に短縮
CSS → font の発見カスケードが解消し、並列ロードに
LCP は元々高速（300ms 前後）で横ばい
40


# Page. 41

![Page Image](https://bcdn.docswell.com/page/PEXQNZN1JX.jpg)

ネットワークタブ②
103なし
HTML後に CSS, JS が直列で開始
103対応
HTML生成中に CSS, JS, font, 画像を並行DL
※ ②（クリティカルリソース固定）のネットワークタブ実測イメージ（数値は別計測の中央値）
41


# Page. 42

![Page Image](https://bcdn.docswell.com/page/3EK9N8NMED.jpg)

サーバー処理が長いほど効果あり
HTML をクライアントへ返すまでの時間が長いページほど、
Early Hints の効果は大きい。
SQL クエリ
外部 API 呼び出し
テンプレート描画
etc.
42


# Page. 43

![Page Image](https://bcdn.docswell.com/page/L73WV2V275.jpg)

103で速度を解決！
…とはいかないパターンも
どんな構成にも効く銀の弾丸ではない。
効果が出るパターン、出ないパターンを確認。
43


# Page. 44

![Page Image](https://bcdn.docswell.com/page/87DK8Z86JG.jpg)

フロント構成との相性問題
✅ 効果あり
MPA / SSR
❌ 効果薄
SPA（CSR中心）
44


# Page. 45

![Page Image](https://bcdn.docswell.com/page/VJPK8DWZE8.jpg)

なぜ SPA では効果が薄いのか
サーバーが返すHTMLが薄く、待ち時間がほとんど生まれない
ため、Early Hints の効果が薄い。
メインとなるHTMLのレスポンスタイム
&lt;!doctype html&gt;
&lt;html lang=&quot;ja&quot;&gt;
&lt;head&gt;...&lt;/head&gt;
&lt;body&gt;
&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
&lt;script type=&quot;module&quot; src=&quot;/app.js&quot;&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
ソース全体像
45


# Page. 46

![Page Image](https://bcdn.docswell.com/page/2EVVN18MEQ.jpg)

❌
とりあえずearly hints は
外したヒントは、かえって遅くなる
103は名前の通り、あくまでヒント。
そのページで使わないリソースを指すと逆効果
Link: &lt;/app.css&gt;; rel=preload
Link: &lt;/unused.css&gt;; rel=preload
app.cssは全てのページで利用
unused.cssはindexページでしか利用しないのに、全部のページ
で読み込んでいる、など
46


# Page. 47

![Page Image](https://bcdn.docswell.com/page/57GLK25XEL.jpg)

適切に導入すれば
ユーザー体験アップ
⤴️
ご清聴ありがとうございました
47


