Cloudflare で投機ルール API をアドオンし、先回りレンダリング
はじめに
以前の記事で Cloudflare Dev docs が投機ルール API のプリフェッチを利用していた件に触れました。
Chrome for Developers Blog の Speculation Rules API を改善 ページをみると、Akamai CDN で自分のサイトにこの API を有効にする例がありましたので、Cloudflare 版を試します。
方針
プロキシー側で実装する場合、元の HTML ドキュメントに投機ルールを挿入する方式より、HTTP レスポンスヘッダーを追加し、投機ルールの配信先に誘導する方が素直だと思うので、そちらで試します。
具体的には Cloudflare で
・レスポンスヘッダー Speculation-Rules
を追加
・上記ヘッダーに設定した URL から投機ルールの定義ファイルを配信
Codelab に投機ルール API のチュートリアルがあります(オリジン側での事前レンダリング実装)。
その環境を一部利用し、プロキシー側での実装に切り替えても同様の効果が出るかを確認します。
準備
オリジン
オリジンは Codelab に記載してある Glitch の内容をそのまま利用させてもらいます。
ただ、③デモサイトを作成する までにします。
デモサイトには効果測定のための人為的な遅延が仕込まれています。
これで、デモサイトの設定は完了です。クリックして動作を確認してください。ページ 2 と 3 は、メインスレッドの人為的なブロックにより、読み込みに時間がかかっているはずです。
:
ブロック中というデバッグ メッセージと、このページの LCP の表示に 3,658 ミリ秒かかったことも確認できます。これは、ダミーのブロック関数によりメインスレッドがブロックされていることによるものです。
実装
ヘッダー挿入とファイル配信なのでいくつか方法が考えられますが、今回は下記 2 つの方式を試します。
- Workers
- Rules と R2
1. Workers
Workers だけで済ます例です。
wrangler.toml
routes = [
{ pattern = "fast.oymk.work", custom_domain = true }
]
index.ts
- 投機ルール定義ファイルの MIME タイプは
application/speculationrules+json
に指定 - Glitch オリジンは user-agent がないと動かなかったのでリクエストヘッダーに追加
import { Hono } from 'hono'
export interface Env {}
const app = new Hono<{ Bindings: Env }>()
app.get('/speculationrules.json', (c) => {
return c.json({
prerender: [{
where: {
and: [{
href_matches: "/*",
relative_to: "document"
}]
},
eagerness: "moderate"
}]
}, 200, {
'content-type': 'application/speculationrules+json',
})
})
app.get('/*', async (c) => {
const url = new URL(c.req.url)
const res = await fetch("https://apple-sixth-seaplane.glitch.me" + url.pathname, {
headers: {
'user-agent': 'cf-worker',
}
})
const newRes = new Response(res.body, res)
newRes.headers.set('Speculation-Rules', '"/speculationrules.json"')
return newRes
})
export default app
wrangler tail
でリクエストの着信を見ます。
投機ルール定義ファイルの eagerness
が moderate
だと、Page 2 にカーソルを合わせた時点で、リンク先リソースやサブリソースにアクセスが発生してます。また、prerender
指定で事前レンダリングが行われるので、(仕込まれたブロック関数の時間が経過した)3 秒より後でクリックするとすぐ描画されます。
eagerness
を immediate
に変えると、事前レンダリングのタイミングが変わり、index.html ロードの後に即発動されます。
2. Rules と R2
投機ルール定義ファイルの配信
R2(Public bucket)から配信します。
-
Bucket を作成し、
R2.dev
での公開を許可
カスタムドメインが望ましいですが、テストなのでサクッと R2.dev を使います。
-
Bucket に Object (投機ルール定義ファイル)を配置
ルール定義ファイル
{
"prerender": [{
"where": {
"and": [{
"href_matches": "/*",
"relative_to": "document"
}]
},
"eagerness": "moderate"
}]
}
R2 へアップロード
MIME タイプを指定しています。
❯ npx wrangler r2 object put 0-pub/speculationrules.json --file=speculation.json --content-type=application/speculationrules+json --cache-control=max-age=60
- Object に付与された R2.dev URL を確認
R2 > 対象 Bucket > 対象 Object
この URL がSpeculation-Rules
ヘッダーの値となるので、メモしておきます。
Cloudflare プロキシー設定
Glitch オリジンで動作させるための設定です。
他のオリジンの場合はそれぞれ合わせた調整になります。
-
DNS
Glitch で生成されたドメインに CNAME
-
Origin Rules
Host ヘッダーを Glitch に書き換え
-
Transform Rules > Modify Response Header
レスポンスヘッダーSpeculation-Rules
追加
Value は 投機ルール定義ファイルの R2 Object URL("..." で囲うこと)
その他
Page Shiled で CSP の調整など、セキュリティ機能の利用状況に応じてつじつまを合わせる。
LCP time 結果
下記の通り、オリジン側で投機ルール API を実装した Codelab のデモと同じ結果を Cloudflare プロキシーでの実装でも得られることを確認しました。
事前レンダリングによって LCP time が短縮される理由は下記のとおりです。
この例ではまた、有効化の時間が 4,298 ミリ秒である一方、LCP の時間はそれよりはるかに短い 128 ミリ秒であることもわかります。パフォーマンス指標の時間測定はすべて、ページが読み込みを開始した時点を基準として行われます。ただし、LCP の記録に使われるウェブに関する指標のライブラリは、ユーザーの視点で LCP を測定するため、activationStart の時間を差し引く特殊なロジックとなっています。Chrome ユーザー エクスペリエンス レポート(CrUX)でもこの方法で LCP を算出しており、Google Search Console の PageSpeed Insights や Core Web Vitals レポートなどのツールもこれに準拠しています。
Chrome for Developers
投機ルール API なし
Page 2: 3836ms
Page 3: 4545ms
シーケンス(簡略化)
投機ルール API あり
Page 2: 78ms
Page 3: 67ms
シーケンス(簡略化)
- 事前レンダリング
moderate
- 事前レンダリング
immediate
事前レンダリングの途中でクリックが発生したら?
その分だけ表示までの時間が短縮されます。
リンクをクリックすると、事前レンダリングされたページが読み込まれます。リンクをクリックするまでに、ページを読み込む十分な時間があった場合は一瞬で表示されます。ページの読み込みが完了していなかった場合でも、ある程度速く表示されます。いずれにしても、投機ルールを使用しない場合と比べて LCP の時間は短くなります。
Chrome for Developers
以上、Cloudflare で投機ルール API を追加するサンプルでした。
参考ページ
Discussion