🚦 CloudflareのEarly Hints(103)を有効化した — トグルの場所とAPI、そして103がすぐ返らない仕様
HTTP 103 Early HintsをCloudflareで有効化する手順。トグルはSpeed > SettingsのContent Optimizationタブ(APIでも可)。有効化直後に103が返らない仕様(Linkヘッダー学習)とcurlでの確認方法をまとめる。
このサイトのパフォーマンスをChrome DevToolsで監査したとき、改善余地は実質ゼロだった。LCP 417ms、CLS 0.00。それでも1つだけ残っていた札がEarly Hintsで、有効化しようとしたらダッシュボードでトグルを見つけられず、APIで入れた。あとから分かったんですが、トグルはちゃんとある。それも含めて、探し回った記録と有効化後にハマった仕様を書いておく。
103は「本題の前のひとこと」
Early Hints(HTTP 103)は、サーバーが本レスポンスを組み立てている間に「先にこれを読み込んでおいて」というヒントだけを先行して返す中間レスポンスだ。ブラウザは103に載ったLinkヘッダーを見て、HTMLが届く前にフォントやCSSの取得を始められる。
クライアント → リクエスト
← HTTP/2 103 (link: フォントCSSをpreload) ← 即座に返る
← HTTP/2 200 (本体HTML) ← 揃ってから返る
効くのは「本レスポンスまでの待ち時間」がある場合と、クロスオリジンのリソースを早く繋ぎたい場合。このサイトは静的でTTFBが30ms程度しかないので、伸びしろは小さい。それでも当時はフォントCSSをCDN(クロスオリジン)から引いていたので、その取得開始を前倒しできる分の理屈上の得はあった。
追記(2026-07-26): そのフォントCDN依存のほうが問題だと後で分かった。第三者オリジンのstylesheetはレンダーブロッキングなので、相手が落ちるときではなく「中途半端に遅い」ときに描画が丸ごと待たされる。遅延1.5秒を注入して測ると初回描画は212msから3,340msまで伸びた。結局フォントを同一オリジンへ自己ホストし、Early Hintsのpreload先もそちらへ差し替えている。Early Hintsは接続確立を前倒しするだけで、レンダーブロッキングそのものは消せない。
トグルの場所と、探すのを諦めた人向けのAPI
正解を先に書くと、トグルは Speed > Settings の「Content Optimization」タブにある。自分は旧ダッシュボードの記憶で「Speed直下のOptimizationページ」を探して見つけられず、無いと思い込んだ。新ダッシュボードはSpeed配下がSettings+タブ構成に再編されていて、目当ての設定が一段深くなっている。同じ迷い方をする人は多いと思う。
UIを探すより速いのが好みなら、ゾーンAPIでも一発で入る。
# 現在値の確認
curl -s "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/settings/early_hints" \
-H "Authorization: Bearer $CF_API_TOKEN" | jq .result.value
# 有効化
curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/settings/early_hints" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{"value":"on"}'
前提がひとつあって、CloudflareはあなたのレスポンスにあるLinkヘッダーを元に103を作る。つまり先にLink: rel=preloadを返しておく必要がある。Cloudflare Pagesなら _headers に書くだけだ。
/*
Link: <https://cdn.example.com/font-400.css>; rel=preload; as=style; crossorigin
ハマりどころ: 有効化直後は103が返らない
有効化してすぐ curl -v で確認したら、103が出ない。設定は on なのに、だ。一瞬「またUIと実体がズレてるのか」と疑ったんですが、これは仕様だった。
CloudflareはURL単位でLinkヘッダーを学習してから103を返し始める。つまり1リクエスト目で「このURLはこのLinkを返すのか」を覚え、2リクエスト目以降に103として先出しする。有効化→即確認→出ない→数回叩くと出る、という順番になる。確認コマンドはこれだけ。
curl -sv --http2 -o /dev/null "https://upstream-dev.com/" 2>&1 | grep -E "^< HTTP|link"
うちの本番の出力はこうなっている。
< HTTP/2 103
< link: <...400.css>; as=style; crossorigin; rel=preload, <...700.css>; ...
< HTTP/2 200
< link: <...400.css>; rel=preload; as=style; crossorigin, ...
103が200より先に出ていれば動いている。なお103はHTTP/2以上で機能し、http:// の平文接続には出ない。ブラウザ側の活用はChromeが先行していて、対応状況は資産の種類(preload/preconnect)によっても違うので、「全ユーザーが恩恵を受ける」前提では見積もらないほうがいい。
設定ひとつに大袈裟な、と思うかもしれない。ただ「トグルが再編後のUIの一段深い場所にいる」「有効化しても即座には観測できない」の二段構えは、知らないと諦めるポイントとして絶妙すぎる。同じ場所で引き返した人がいたら、Content OptimizationタブかAPI、そして数回のリトライで通れます。TTFBが数百msあるオリジン(WordPressや重いSSR)ほど効く機能なので、静的サイトのうちより、あなたのサイトのほうが得をするかもしれませんよ。