どこでも高速に動作するサイトを開発するのは難しい作業です。デバイスの機能の多様性と、デバイスが接続するネットワークの品質を考えると、この課題は克服できないように思えるかもしれません。ブラウザの機能を活用して読み込みパフォーマンスを改善することはできますが、ユーザーのデバイスの機能やネットワーク接続の品質をどうやって把握すればよいでしょうか。解決策はクライアント ヒントです。
クライアント ヒントは、ユーザーのデバイスと接続先のネットワークに関するこれらの側面に関する分析情報を提供する、オプトイン HTTP リクエスト ヘッダーのセットです。サーバーサイドでこの情報を利用することで、デバイスやネットワークの状態に基づいてコンテンツの配信方法を変更できます。これにより、より包括的なユーザー エクスペリエンスを実現できます。
コンテンツの交渉がすべて
クライアント ヒントは、コンテンツ ネゴシエーションの別の方法です。つまり、ブラウザのリクエスト ヘッダーに基づいてコンテンツ レスポンスを変更します。
コンテンツ ネゴシエーションの例として、Accept
リクエスト ヘッダーがあります。ブラウザが認識するコンテンツ タイプを記述します。サーバーは、この情報をレスポンスのネゴシエーションに使用できます。画像リクエストの場合、Chrome の Accept
ヘッダーの内容は次のとおりです。
Accept: image/webp,image/apng,image/*,*/*;q=0.8
すべてのブラウザは JPEG、PNG、GIF などの画像形式をサポートしていますが、この場合の Accept は、ブラウザが WebP と APNG もサポートしていることを意味します。この情報を使用して、各ブラウザに最適な画像タイプをネゴシエートできます。
<?php
// Check Accept for an "image/webp" substring.
$webp = stristr($_SERVER["HTTP_ACCEPT"], "image/webp") !== false ? true : false;
// Set the image URL based on the browser's WebP support status.
$imageFile = $webp ? "whats-up.webp" : "whats-up.jpg";
?>
<img src="<?php echo($imageFile); ?>" alt="I'm an image!">
Accept
と同様に、クライアント ヒントはコンテンツのネゴシエーションの手段ですが、デバイスの機能とネットワークの状態のコンテキストで使用されます。クライアント ヒントを使用すると、ネットワーク状態が悪いユーザーにクリティカルでないリソースを提供するかどうかなど、ユーザーの個々のエクスペリエンスに基づいてサーバーサイドのパフォーマンスに関する決定を行うことができます。このガイドでは、使用可能なヒントと、それらを使用してコンテンツ配信をユーザーにとってより快適にするための方法について説明します。
オプトイン
Accept
ヘッダーとは異なり、クライアント ヒントは魔法のように現れるわけではありません(後述する Save-Data
を除く)。リクエスト ヘッダーを最小限に抑えるため、ユーザーがリソースをリクエストしたときに Accept-CH
ヘッダーを送信して、受信するクライアント ヒントをオプトインする必要があります。
Accept-CH: Viewport-Width, Downlink
Accept-CH
の値は、サイトが後続のリソース リクエストの結果の決定に使用するリクエストされたヒントのカンマ区切りリストです。クライアントがこのヘッダーを読み取ると、「このサイトは Viewport-Width
と Downlink
のクライアント ヒントを必要としている」と伝えられます。特定のヒント自体は気にする必要はありません。これについては後ほど説明します。
これらのオプトイン ヘッダーは、任意のバックエンド言語で設定できます。たとえば、PHP の header
関数を使用できます。これらのオプトイン ヘッダーは、<meta>
タグの http-equiv
属性を使用して設定することもできます。
<meta http-equiv="Accept-CH" content="Viewport-Width, Downlink" />
すべてのクライアント ヒント
クライアント ヒントは、ユーザーが使用しているデバイスと、サイトへのアクセスに使用しているネットワークのいずれかを表します。使用可能なヒントをすべて簡単に説明します。
デバイスに関するヒント
一部のクライアント ヒントは、ユーザーのデバイスの特性(通常は画面の特性)を表します。これらの機能の一部は、特定のユーザーの画面に最適なメディア リソースを選択するのに役立ちますが、必ずしもメディア中心であるとは限りません。
このリストに入る前に、画面とメディアの解像度を説明するために使用される主な用語をいくつかご紹介します。
固有のサイズ: メディア リソースの実際のサイズ。たとえば、Photoshop で画像を開くと、画像サイズ ダイアログに表示される寸法は、その画像の固有のサイズを表します。
密度補正済みの固有サイズ: ピクセル密度が補正されたメディア リソースのサイズ。これは、画像の固有のサイズをデバイスのピクセル比で割った値です。たとえば、次のマークアップについて考えてみましょう。
<img
src="whats-up-1x.png"
srcset="whats-up-2x.png 2x, whats-up-1x.png 1x"
alt="I'm that image you wanted."
/>
この場合、1x
画像の固有サイズが 320x240、2x
画像の固有サイズが 640x480 であるとします。このマークアップが、画面デバイスのピクセル比が 2 のデバイス(Retina 画面など)にインストールされたクライアントによって解析されると、2x
画像がリクエストされます。2x
画像の密度補正された固有サイズは 320x240 です。これは、640x480 を 2 で割ると 320x240 になるためです。
外部サイズ: CSS やその他のレイアウト要素(width
属性や height
属性など)が適用された後のメディア リソースのサイズ。密度補正された元のサイズが 320x240 の画像を読み込む <img>
要素があるとします。この要素には、256px
と 192px
の値がそれぞれ適用された CSS width
プロパティと height
プロパティも含まれています。この例では、その <img>
要素の外部サイズは 256x192 になります。
用語を理解したところで、デバイス固有のクライアント ヒントのリストを見てみましょう。
Viewport-Width
Viewport-Width
は、ユーザーのビューポートの幅(CSS ピクセル単位)です。
Viewport-Width: 320
このヒントは、他の画面固有のヒントとともに使用して、特定の画面サイズ(アート ディレクション)に最適な画像のさまざまな処理(切り抜き)を提供したり、現在の画面幅に不要なリソースを省略したりできます。
DPR
DPR
(デバイス ピクセル比の略)は、ユーザーの画面の物理ピクセル数と CSS ピクセル数の比率を報告します。
DPR: 2
このヒントは、画面のピクセル密度に対応する画像ソースを選択する場合に役立ちます(srcset
属性の x
記述子など)。
幅
Width
ヒントは、sizes
属性を使用して <img>
タグまたは <source>
タグによってトリガーされた画像リソースのリクエストに表示されます。sizes
は、リソースの外部サイズをブラウザに伝えます。Width
は、その外部サイズを使用して、現在のレイアウトに最適な内部サイズの画像をリクエストします。
たとえば、ユーザーが 320 CSS ピクセルの幅の画面と DPR 2 のページをリクエストしたとします。デバイスは、sizes
属性値が 85vw
の <img>
要素を含むドキュメントを読み込みます(すべての画面サイズでビューポート幅の 85% です)。Width
ヒントが有効になっている場合、クライアントは <img>
の src
のリクエストとともに、この Width
ヒントをサーバーに送信します。
Width: 544
この場合、クライアントは、リクエストされた画像の最適な固有の幅は、ビューポートの幅(272 ピクセル)の 85% に画面の DPR(2)を掛けた値(544 ピクセル)であることをサーバーにヒントしています。
このヒントは、画面の密度補正された幅を考慮するだけでなく、この重要な情報をレイアウト内の画像の外部サイズと調整するため、特に強力です。これにより、サーバーは画面とレイアウトの両方に最適な画像レスポンスをネゴシエートできます。
Content-DPR
画面にはデバイスのピクセル比があることはすでにご存じでしょうが、リソースにも独自のピクセル比があります。最も単純なリソース選択のユースケースでは、デバイスとリソースのピクセル比率は同じにできます。ただし、DPR
ヘッダーと Width
ヘッダーの両方が使用されている場合、リソースの外部サイズによって、両者が異なるシナリオが発生する可能性があります。ここで Content-DPR
ヒントが役立ちます。
他のクライアント ヒントとは異なり、Content-DPR
はサーバーが使用するリクエスト ヘッダーではなく、DPR
ヒントと Width
ヒントを使用してリソースを選択するたびにサーバーが送信するレスポンス ヘッダーです。必須です。Content-DPR
の値は、次の式の結果にする必要があります。
Content-DPR
= [選択した画像リソースのサイズ] ÷([Width
] ÷ [DPR
])
Content-DPR
リクエスト ヘッダーが送信されると、ブラウザは画面のデバイスのピクセル比とレイアウトに合わせて指定された画像をスケーリングする方法を把握します。これを指定しないと、画像が正しくスケーリングされない可能性があります。
Device-Memory
技術的には Device Memory API の一部である Device-Memory
は、現在のデバイスのメモリの概算量(GiB)を返します。
Device-Memory: 2
このヒントのユースケースとしては、メモリが限られているデバイスのブラウザに送信される JavaScript の量を減らすことが考えられます。JavaScript は、ブラウザが通常読み込むコンテンツ タイプの中で最もリソースを消費するタイプであるためです。また、デコードに使用するメモリが少なくなるため、DPR の低い画像を送信することもできます。
ネットワークのヒント
Network Information API は、ユーザーのネットワーク接続のパフォーマンスを記述する別のカテゴリのクライアント ヒントを提供します。これらは、最も有用なヒントセットだと思います。これにより、低速接続でクライアントにリソースを配信する方法を変更することで、エクスペリエンスをユーザーに合わせて調整できます。
RTT
RTT
ヒントは、アプリケーション レイヤでのラウンドトリップ時間の概算(ミリ秒単位)を提供します。RTT
ヒントには、トランスポート レイヤの RTT とは異なり、サーバー処理時間が含まれます。
RTT: 125
このヒントは、読み込みパフォーマンスにおけるレイテンシの役割が原因で役立ちます。RTT
ヒントを使用すると、ネットワークの応答性に基づいて意思決定を行うことができます。これにより、(一部のリクエストを省略するなどして)エクスペリエンス全体の配信を高速化できます。
ダウンリンク
読み込みパフォーマンスではレイテンシが重要ですが、帯域幅も影響します。Downlink
ヒントは、メガビット / 秒(Mbps)で表され、ユーザーの接続の下り(ダウンロード)速度の概算を示します。
Downlink: 2.5
RTT
と組み合わせて使用すると、ネットワーク接続の品質に基づいてユーザーにコンテンツを配信する方法を変えるのに役立ちます。Downlink
エクアドル時間(ECT)
ECT
ヒントは、効果的な接続タイプを表します。値は、接続タイプの列挙リストのいずれかです。各接続タイプは、RTT
値と Downlink
値の両方の指定された範囲内の接続を表します。
このヘッダーには、実際の接続タイプは表示されません。たとえば、ゲートウェイが携帯電話の基地局か Wi-Fi アクセス ポイントかについては表示されません。現在の接続のレイテンシと帯域幅を分析し、最も類似するネットワーク プロファイルを決定します。たとえば、Wi-Fi 経由で低速ネットワークに接続すると、ECT
に 2g
の値が入力されることがあります。これは、有効な接続に最も近い近似値です。
ECT: 2g
ECT
の有効な値は、4g
、3g
、2g
、slow-2g
です。このヒントは、接続品質の評価の出発点として使用できます。その後、RTT
ヒントと Downlink
ヒントを使用して精度を高めることができます。
Save-Data
Save-Data
は、ネットワークの状態を記述するヒントというよりは、ページで送信するデータ量を減らすように指示するユーザー設定です。
Save-Data
は、他のネットワーク ヒントに似ているため、ネットワーク ヒントとして分類することをおすすめします。また、レイテンシが高い環境や帯域幅が低い環境で有効にする可能性もあります。このヒントは、存在する場合は常に次のようになります。
Save-Data: on
Google では、Save-Data
でできることについて説明してきました。パフォーマンスに及ぼす影響は甚大なものになる可能性があります。これは、ユーザーが文字通り、送信するコンテンツを減らして欲しいと伝えているシグナルです。こうしたシグナルをリッスンして対応すると、ユーザーは喜びます。
すべてを組み合わせる
クライアント ヒントに対して何を行うかは、ユーザー次第です。多くの情報を提供しているため、さまざまなオプションがあります。アイデアを得るために、中西部の田舎にある架空の製材会社 Sconnie Timber でクライアント ヒントがどのように役立つかを見てみましょう。遠隔地ではよくあることですが、ネットワーク接続は不安定になることがあります。このような状況では、クライアント ヒントなどのテクノロジーがユーザーにとって大きな違いを生む可能性があります。
レスポンシブ画像
最もシンプルなレスポンシブ画像のユースケースを除き、複雑になる可能性があります。異なる画面サイズと異なる形式に同じ画像の複数の処理とバリエーションがある場合はどうすればよいですか?このマークアップは、非常に複雑になり、非常に迅速に複雑になります。間違えやすく、重要なコンセプト(sizes
など)を忘れたり誤解したりしがちです。
<picture>
と srcset
は間違いなく優れたツールですが、複雑なユースケースでは開発とメンテナンスに時間がかかります。マークアップの生成は自動化できますが、<picture>
と srcset
が提供する機能は複雑であるため、自動化は柔軟性を維持する方法で行う必要があります。
クライアント ヒントを使用すると、この処理を簡素化できます。クライアント ヒントを使用して画像レスポンスをネゴシエートするコードは次のようになります。
- ワークフローに該当する場合は、まず
Viewport-Width
ヒントをオンにして、画像処理(アートディレクションによる画像)を選択します。 Width
ヒントとDPR
ヒントをチェックし、画像のレイアウトサイズと画面密度に合ったソースを選択して、画像の解像度を選択します(srcset
でx
記述子とw
記述子が機能する仕組みに似ています)。- ブラウザがサポートするファイル形式のうち、最も最適なものを選択します(
Accept
は、ほとんどのブラウザでこの作業を支援します)。
架空の木材会社クライアントの場合、クライアント ヒントを使用した PHP で単純なレスポンシブ画像選択ルーティンを開発しました。つまり、すべてのユーザーに次のようなマークアップを送信するのではなく、
<picture>
<source
srcset="
company-photo-256w.webp 256w,
company-photo-512w.webp 512w,
company-photo-768w.webp 768w,
company-photo-1024w.webp 1024w,
company-photo-1280w.webp 1280w
"
type="image/webp"
/>
<img
srcset="
company-photo-256w.jpg 256w,
company-photo-512w.jpg 512w,
company-photo-768w.jpg 768w,
company-photo-1024w.jpg 1024w,
company-photo-1280w.jpg 1280w
"
src="company-photo-256w.jpg"
sizes="(min-width: 560px) 251px, 88.43vw"
alt="The Sconnie Timber Staff!"
/>
</picture>
個々のブラウザのサポートに基づいて、以下に絞り込むことができました。
<img
src="/image/sizes:true/company-photo.jpg"
sizes="(min-width: 560px) 251px, 88.43vw"
alt="SAY CHEESY PICKLES."
/>
この例の /image
URL は、mod_rewrite によって書き換えられたパラメータが続く PHP スクリプトです。画像ファイル名と追加のパラメータを受け取り、バックエンド スクリプトが特定の条件で最適な画像を選択できるようにします。
最初の質問は「これはバックエンドで <picture>
と srcset
を再実装しているだけではないですか?」ということでしょう。
ある意味では、そうです。ただし、重要な違いがあります。アプリケーションがクライアント ヒントを使用してメディア レスポンスを作成する場合、ほとんど(すべてではない)の作業を自動化できます。この作業には、代わりにこの作業を行うサービス(CDN など)を含めることができます。一方、HTML ソリューションでは、すべてのユースケースに対応するために新しいマークアップを記述する必要があります。はい、マークアップの生成は自動化できます。ただし、設計や要件が変更された場合は、将来的に自動化戦略を見直す必要がある可能性があります。
クライアント ヒントを使用すると、ロスレスの高解像度画像から始め、画面とレイアウトの任意の組み合わせに最適なように動的にサイズを変更できます。ブラウザが選択できる画像候補の固定リストを列挙する必要がある srcset
とは異なり、このアプローチはより柔軟に使用できます。srcset
では、256w
、512w
、768w
、1024w
など、ブラウザにバリエーションの粗いセットを提供する必要がありますが、クライアント ヒントを使用したソリューションでは、大量のマークアップを使用せずにすべての幅を提供できます。
もちろん、画像選択ロジックを自分で記述する必要はありません。Cloudinary は、w_auto
パラメータを使用するときにクライアント ヒントを使用して画像レスポンスを作成します。クライアント ヒントをサポートするブラウザを使用している場合、ユーザーの中央値でダウンロードされるバイト数が 42% 減少することが確認されています。
ただし、デスクトップ版 Chrome 67 の変更により、クロスオリジン クライアント ヒントのサポートが削除されました。幸い、これらの制限はモバイル版 Chrome には影響しません。また、機能ポリシーの作業が完了すると、すべてのプラットフォームで完全に解除されます。
ネットワーク速度が遅いユーザーをサポートする
適応型パフォーマンスとは、クライアント ヒントから得られる情報(特に、ユーザーのネットワーク接続の現在の状態に関する情報)に基づいて、リソースの配信方法を調整できるという考え方です。
Sconnie Timber のサイトでは、ネットワークが遅いときに負荷を軽減するために、バックエンド コードで Save-Data
、ECT
、RTT
、Downlink
ヘッダーが検査されます。処理が完了すると、ネットワーク品質スコアが生成されます。このスコアは、ユーザー エクスペリエンスを改善するために介入する必要があるかどうかを判断するために使用できます。このネットワーク スコアは 0
~1
の範囲で、0
はネットワークの品質が最も低く、1
は最も高いことを示します。
最初に、Save-Data
が存在するかどうかを確認します。有効な場合、スコアは 0
に設定されます。これは、ユーザーがエクスペリエンスを軽量化、高速化するために必要なことは何でも行いたいと想定しているためです。
Save-Data
がない場合、ECT
、RTT
、Downlink
のヒントの値を重み付けして、ネットワーク接続品質を示すスコアを計算します。ネットワーク スコア生成ソースコードは GitHub で入手できます。つまり、ネットワーク関連のヒントを何らかの形で使用すれば、ネットワーク速度が遅いユーザーのエクスペリエンスを改善できます。
サイトがクライアント ヒントから提供される情報に適応する場合は、「すべてまたはなし」のアプローチを採用する必要はありません。送信するリソースをインテリジェントに決定できます。レスポンシブ画像選択ロジックを変更して、特定のディスプレイに低品質の画像を送信し、ネットワーク品質が低い場合に読み込みパフォーマンスを高速化できます。
この例では、低速ネットワーク上のサイトのパフォーマンスを改善する際のクライアント ヒントの影響を把握できます。以下は、クライアント ヒントに適応していない低速ネットワーク上のサイトの WebPagetest ウォーターフォールです。
同じ低速接続で同じサイトのウォーターフォールです。ただし、このサイトではクライアント ヒントを使用して、重要なページ リソース以外を除外しています。
クライアント ヒントにより、ページの読み込み時間が 45 秒以上からその 10 分の 1 未満に短縮されました。このシナリオでのクライアント ヒントのメリットは強調しきれません。遅いネットワークで重要な情報を探しているユーザーにとって、大きなメリットになります。
さらに、クライアント ヒントをサポートしていないブラウザのエクスペリエンスを損なうことなく、クライアント ヒントを使用することもできます。たとえば、サポートされていないブラウザでも完全なエクスペリエンスを提供しながら、ECT
ヒントの値を使用してリソース配信を調整する場合は、次のようにデフォルト値にフォールバックするように設定できます。
// Set the ECT value to "4g" by default.
$ect = isset($_SERVER["HTTP_ECT"]) ? $_SERVER["HTTP_ECT"] : "4g";
ここで、"4g"
は ECT
ヘッダーで記述されている最高品質のネットワーク接続を表します。$ect
を "4g"
に初期化すると、クライアント ヒントをサポートしていないブラウザは影響を受けません。オプトイン最高!
キャッシュに注意してください。
HTTP ヘッダーに基づいてレスポンスを変更する場合は、キャッシュがそのリソースの今後の取得をどのように処理するかに注意する必要があります。ここでは、キャッシュ エントリを指定されたリクエスト ヘッダーの値にキー付けするため、Vary
ヘッダーが不可欠です。簡単に言うと、特定の HTTP リクエスト ヘッダーに基づいてレスポンスを変更する場合は、ほとんどの場合、次のように Vary
にそのヘッダーのリクエストを含める必要があります。
Vary: DPR, Width
ただし、これには大きな注意点があります。頻繁に変更されるヘッダー(Cookie
など)でキャッシュに保存可能なレスポンスを Vary
することは絶対に避けてください。これらのリソースは事実上キャッシュに保存できなくなります。RTT
や Downlink
などのクライアント ヒント ヘッダーで Vary
を使用しないようにすることをおすすめします。これらのヘッダーは、接続要因であり、頻繁に変更される可能性があるためです。これらのヘッダーでレスポンスを変更する場合は、ECT
ヘッダーのみをキーにすることを検討してください。これにより、キャッシュミスを最小限に抑えることができます。
もちろん、これはレスポンスをキャッシュに保存している場合にのみ適用されます。たとえば、コンテンツが動的である HTML アセットはキャッシュに保存しません。再訪時にユーザー エクスペリエンスが損なわれる可能性があるためです。このような場合は、必要に応じて自由に回答を変更し、Vary
を気にする必要はありません。
サービス ワーカーのクライアント ヒント
コンテンツのネゴシエーションは、もはやサーバーの専用ではありません。Service Worker はクライアントとサーバー間のプロキシとして機能するため、JavaScript を介してリソースを配信する方法を制御できます。これにはクライアント ヒントも含まれます。サービス ワーカーの fetch
イベントでは、event
オブジェクトの request.headers.get
メソッドを使用して、リソースのリクエスト ヘッダーを次のように読み取ることができます。
self.addEventListener('fetch', (event) => {
let dpr = event.request.headers.get('DPR');
let viewportWidth = event.request.headers.get('Viewport-Width');
let width = event.request.headers.get('Width');
event.respondWith(
(async function () {
// Do what you will with these hints!
})(),
);
});
有効にしたクライアント ヒント ヘッダーはすべて、この方法で読み取ることができます。ただし、この方法で得られる情報は一部に限られます。ネットワーク固有のヒントは、navigator
オブジェクトの同等の JavaScript プロパティで読み取ることができます。
クライアント ヒント | JS の同等関数 |
---|---|
「ECT」 | 「navigator.connection.effectiveType」 |
「RTT」 | navigator.connection.rtt |
「Save-Data」 | navigator.connection.saveData |
「ダウンリンク」 | navigator.connection.downlink |
「Device-Memory」 | navigator.deviceMemory |
これらの API はすべての地域で利用できないため、in
演算子を使用して機能チェックする必要があります。
if ('connection' in navigator) {
// Work with netinfo API properties in JavaScript!
}
サーバー側で使用するロジックと同様のものを使用できます。ただし、クライアント ヒントを使用してコンテンツをネゴシエートするサーバーが必要ないことが異なります。Service Worker は、ユーザーがオフラインの場合でもコンテンツを配信できる追加機能があるため、エクスペリエンスを高速化および復元力を高めることができます。
まとめ
クライアント ヒントを使用すると、完全にプログレッシブな方法でユーザー エクスペリエンスを高速化できます。特に複雑なユースケースでは、<picture>
と srcset
に依存するよりもレスポンシブ画像の配信が容易になるように、ユーザーのデバイスの機能に基づいてメディアを配信できます。これにより、開発側の負担を軽減できるだけでなく、
さらに重要なのは、送信内容と送信方法を変更することで、ネットワーク接続の不具合を検出し、ユーザーの不満を解消できることです。これにより、不安定なネットワーク上のユーザーがサイトにアクセスしやすくなります。Service Worker と組み合わせることで、オフラインで利用できる非常に高速なサイトを作成できます。
クライアント ヒントは Chrome と Chromium ベースのブラウザでのみ使用できますが、サポートしていないブラウザに負担をかけないように使用できます。クライアント ヒントを使用して、すべてのユーザーのデバイスの機能と、接続するネットワークを認識する、真に包括的で適応性のあるエクスペリエンスを作成することを検討してください。他のブラウザ ベンダーもその価値を認識し、実装の意向を示してくれることを願っています。
リソース
- クライアント ヒントによる自動レスポンシブ画像
- クライアント ヒントとレスポンシブ画像 - Chrome 67 での変更
- (クライアントの)ヒントを活用しましょう。(スライド)
Save-Data
を使用した高速で軽量なアプリケーションの提供
この記事に関する貴重なフィードバックと編集をしてくださった Ilya Grigorik 氏、Eric Portis 氏、Jeff Posnick 氏、Yoav Weiss 氏、Estelle Weyl 氏に感謝いたします。