ブログに戻る

フォロー&ご登録

Web におけるデバイス検出の未来

Andrew Betts

Principal Developer Advocate, Fastly

自社のWebサイトにアクセスするデバイスの種類を認識することで、画像解像度の上げ下げや、デバイスの能力に合わせた配信が可能になり、すべてのユーザーにより優れたエクスペリエンスを提供することが可能になります。これまでは User-Agent HTTP ヘッダーを解析し、そのデータを基にリクエストを処理することで、組織はこうしたニーズに対応してきました。一方、近年に登場した Client Hints では、さらに進化した新しいアプローチが採用されています。そのため、User-Agent ヘッダーの使用が減り始めています。しかし心配はご無用です。

ほとんどの開発者が認めるように、User-Agent ヘッダーは若干扱いづらく、ブラウザ開発者と Web 開発者が30年もの間、好き勝手にせめぎ合いを繰り広げてきた結果、典型的な User-Agent ヘッダーは非常にひどい状態にあります。以下は、現在使用しているブラウザが各リクエストと一緒に送信する User-Agent ヘッダーです。

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36

これは明らかに正常ではありません。私は「Mozilla」を使っていません (少なくてもここに示されている意味で使用していません)。また、私の Mac に Intel のチップは内蔵されていません。さらに、ブラウザは WebKit や KHTML、Gecko を使用しておらず、Safari ではありません。このヘッダー値で唯一正しい情報は「Chrome/120.0.0.0」のみです。残りはネイティブフィルターとの互換性を向上させる目的で仕立て上げられた単なる舞台にすぎません。

プライバシーと User-Agent ヘッダーの情報削減

User-Agent ヘッダーに含まれる情報の長さと多様さは、ユーザープライバシーの面で望ましくありません。W3C の Technical Architecture Group</u> は、2015年に許可を受けていないトラッキング</u>の問題をドキュメント化しています (その後、私は2年間にわたり TAG に参加しました)。Web でアクセスするたびにブラウザから送信されるヘッダーに含まれる値を組み合わせることで、Webサイトの運用者はユーザー固有の ID を特定できる可能性があります。 

つまり、(ログインしたことで) あるサイトで自分の ID が知られていても、他のサイトにアクセスした場合、匿名性が確保されていると思われるかもしれませんが、実は同じ一意のフィンガープリントが送信し続けられ、自分の希望とは関係なく、自分自身を特定していることになるのです。

この問題に対処するため、Google は User-Agent の情報削減プログラム</u>を開始し、このような情報の一意性を削減することを目指しています。(この記事の最初の方でご紹介した) 私自身の User-Agent ヘッダーの値に、怪し過ぎるほど切りの良い Chrome のバージョン番号が示され、私の Mac に Intel が使用されていないにも関わらず、使用されていると表示されている理由がまさにここにあるのです。

User-Agent ヘッダーの情報削減は、一定の有益な情報を提供することと、ユーザーの一意性の識別を可能にする具体的なデータを多く含まないようにすることの妥協点と言えます。ちなみにご利用のブラウザの一意性は amiunique.org</u> で確認できます。

エッジでのデバイス検出

Fastly はこのプログラムをサポートします。ユーザーのためにも、Web のためにも望ましいからです。また Fastly では、User-Agent ヘッダーの解釈に基づくデバイス検出変数が自動的に挿入される機能を使用して、エッジでエクスペリエンスを簡単にカスタマイズできます。  これにより、以下のようなことが可能になります。

if (client.display.width > 1000) {
   set req.http.Feature-Device-Size = "large";
} else {
   set req.http.Feature-Device-Size = "small";
}

User-Agent ヘッダーに対するこの変更は、以前よりも具体性と正確さに欠ける情報がこれらの変数によって提供されることを意味します。さらに、これまで提供されてきた情報の一部が User-Agent 文字列から削除され (またはルックアップできなくなり)、これらの情報が得られなくなります。実質的にエッジで利用できなくなる、画面の幅や高さといった非常に細かい値について特にこれが当てはまります。

しかし、このことはエッジにおけるデバイス検出の終わりを意味しません。情報量が削減された UA ヘッダーでも、Fastly が重要な (しかも最も人気の高い) デバイスの種類を示す変数を挿入するのに十分な情報を含んでいます (client.browser.name</u>client.platform.mobile</u> など)。従って、引き続きこれらを使用してカスタマイズされたエクスペリエンスを提供できます。

また、ブラウザは専用のヘッダーセットで、デバイスの種類に関する新しい世代のメタデータも送信するようになりました。

Sec-Ch-Ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Sec-Fetch-User: ?1

これらにより、データがより構造化された形で提供されるため、Vary コマンドを利用</u>しやすくなるだけでなく、Fastly のサービスで使用するメタデータをさらに簡単に挿入することができます。さらに、過度にシンプルな解析を回避するための対策も含まれます (奇妙な「not a brand」のことです)。つまり、デバイス検出は今後も活躍し続けるということでしょうか?

クライアントヒントへの移行

もう少し詳細な情報がどうしても必要なのに、ユーザーをトラッキングするためのフィンガープリントとして使用される懸念から、ブラウザが直にその情報を提供したがらない場合、どうすべきでしょうか? そこで クライアントヒント</u> の出番です。これは、Google によって開発された機能のひとつで、プライバシーが保護された形でデータにアクセスできる方法を提供することを目的としています。

原理的にクライアントヒントは次のように機能します。Web 開発者がレスポンシブなレイアウトのWebサイトを作成する場合、(前述のように) ユーザーの最初のリクエストに含まれるデバイスに関する情報のみを参考に HTML ページがカスタマイズされます。ただし、同時に Accept-CH レスポンスヘッダーを含むことで、ブラウザに「もう少し情報が欲しい」と伝え、これにより後続のリソースリクエストに対してより優れたユーザーエクスペリエンスを提供することが可能になります。

Accept-CH: Sec-CH-Viewport-Width, Sec-CH-UA-Arch, Device-Memory, RTT

ページのレンダリングを遂行するのに必要な画像やスタイルシート、スクリプトなどの後続リクエストをユーザーのブラウザが行う際、(ユーザーの許可を得て) ブラウザは求められた追加情報を提供します。

Sec-CH-Viewport-Width: 1000
Sec-CH-UA-Arch: "x86"
Device-Memory: 4
RTT: 125

すばらしいユーザーエクスペリエンスの実現を可能にする、まさに情報の宝庫です!Fastly をご利用いただいている場合、エッジはクライアントヒントを扱うのに最適な場所です。特に、可能な順列が多数ある「エントロピーが高い」ヒントの処理にはエッジが適しています。 

例えば WebGL シーンのように、デバイスで高い計算能力が必要とされるエクスペリエンスを想像してみましょう。Device-Memory ヒントを使用してシーンのレンダリングの解像度や目標のフレームレートを決定することができます。Device-Memory には6つの可能な値がありますが、「高い」および「低い」値しか得られない可能性があります。サーバーで決定を行う場合、Fastly のような CDN は6つのキャッシュスロットでこれら2つのバリアントをキャッシュする必要があり、キャッシュがヒットしてユーザーリクエストを迅速かつ効率的に処理できる可能性が低くなります。undefined

クライアントヒントは HTTP API としてのみ利用可能なわけでなく、NavigatorUAData</u> の JavaScript インターフェイスを使用して JavaScript で書かれたクライアントヒントを読み取ることもできます。

非常に便利に見えるクライアントヒントですが、未だに重大な課題を抱えています。

  • **Firefox と Safari ではまったくサポートされていません。**現時点でクライアントヒントは Chrome ベースのブラウザに完全に限られており、Google は10年以上クライアントヒントを推進していることから、他のブラウザによるサポートがすぐに実現するとは思われません。

  • **クライアントヒントを使用して HTML ページをカスタマイズするのは非常に困難です。**理由は、ブラウザにクライアントヒントを送信するよう「頼む」必要があり、前のリクエスト (通常 HTML ページのリクエスト) へのレスポンスでこれが行わるためです。この問題を緩和する試みとして、Critical-CH</u> (以前は Accept-CH-Lifetime</u>) などがありますが、どれも完璧なソリューションとは言えません。

それでもクライアントヒントは今後も存続することが予想されるので、将来的にさらに発展してより多くのユースケースに対するソリューションを提供し、すべてのブラウザでより広範にサポートされることを願うばかりです。

Fastly でデバイス検出を行う現在のベストプラクティス

ベストなアプローチは、複数の場所から収集したシグナルを組み合わせ、ユーザーのプライバシーを尊重しながらデバイスの能力に対する理解を最大限に高めることです。VCL を使用している場合でも、Fastly の Compute SDK</u> のひとつを使用している場合でも、ヘッダーをレスポンスに簡単に追加できる方法があります。これにより、必要なクライアントヒントを送信してくれるようブラウザに頼むことができます。例えば、VCL サービスをご利用の場合、VCL スニペット</u>またはカスタム VCL</u> を使用して必要なクライアントヒントのリストを vcl_deliver</u> に追加できます。

set resp.http.accept-ch = "Sec-CH-Viewport-Width, Sec-CH-UA-Arch, Device-Memory, RTT";

すると、req.http.sec-ch-viewport-width のような HTTP ヘッダーとしてヒント情報にアクセスできます。一般的にこのような情報とデバイス検出変数を組み合わせることで、非常にスマートな判断が可能になります。単に情報をリアルタイムログのエンドポイントに記録したい場合は、以下のようなコードを vcl_log に追加します。

log "syslog " + req.service_id + " my-endpoint-name :: {"
  {" "client-hints": { "}
    {" "viewport-width": ""} + req.http.sec-ch-viewport-width + {"","}
    {" "ua-arch": ""} + req.http.sec-ch-ua-arch + {"","}
    {" "device-memory": ""} + req.http.device-memory + {"","}
    {" "rtt": ""} + req.http.rtt + {"""}
  {" },"}
  {" "device-detection": { "}
    {" "mobile": ""} + client.platform.mobile + {"""}
    {" "touch": ""} + client.display.touchscreen + {"""}
  {" },"}
  {" "ua": { "}
    {" "sec-ch-ua": ""} + req.http.sec-ch-ua + {"","}
    {" "sec-ch-ua-mobile": ""} + req.http.sec-ch-ua-mobile + {"","}
    {" "sec-ch-ua-platform": ""} + req.http.sec-ch-ua-platform + {"""}
  {"}"}
{"}"}
;

このコードにより、新しい UA ヘッダーで提供される情報 (sec-ch-ua-* ヘッダー) と、Fastly のデバイス検出変数 (client.* 変数)、デバイスのメモリなどオプトインのクライアントヒントを組み合わせられます。クライアントヒントをログに記録できるほか、リクエストに注釈を付けたり、特別なレスポンスをトリガーしたり、キャッシュキーを変更したりするなど、考えられるあらゆるソリューションに自由にクライアントヒントを活用できます。

Fastly を使用してデバイス固有のエクスペリエンスを提供している方や、これに興味がある方は community.fastly.com</u> にて、ぜひお話を聞かせてください。