※ この記事は 2021/03 時点での情報になり、今後大きく変動する可能性があります。
User-Agent が Google Chrome で凍結・非推奨になる という話題が年明け以降一部の界隈で盛り上がっています。
また、Chrome に限らず、 Firefox, Safari など Chromium 以外のベンダでも User-Agent(以下 UA) の凍結・非推奨が予想されており、近い将来 UA を使わずにデバイスの判定をする世界線は避けて通れなくなりそうです。
UA の代替となる指標
Google は UA を凍結・非推奨とする代わりに User-Agent Client Hints (以下 UA-CH) を新たなデバイス等の判定の指標として提案しています。
※ UA-CH は W3C の Draft Community なので、今後仕様が変わる可能性があります。
例えば Chromeから任意の URI にリクエストを行う場合、Webサーバ側では以下のような値が Request Header に含まれて送信されます(chrome://flags
の Experimental Web Platform features
を有効することで試すことが可能です)。
sec-ch-ua: "Chromium";v="86", "\"Not\\A;Brand";v="99", "Google Chrome";v="86"
sec-ch-ua-mobile: ?0
# ?1 が true (mobile である), ?0 が false (mobile でない) を指す
# デフォルトはこの2つの Key/Value 値になります
# 他にもプロパティはあるので詳しくは Document を参照下さい
また、Apache 等の Middleware 側でも Accept-CH などを setHeader することで、自身の開発している Webサーバ からのレスポンスに上記以外の UA-CH を含めることも可能です。
UA-CH で何ができる
UA の代替として使えることが考えられます。
現行だと Chromium ベースに限っての話になりますが、Client, Web Server 側両方とも検知が可能です。
Client 側で UA-CH を取得する
Web API で提供されている Navigator インターフェースより行うことができます。
現在、 navigator.userAgentData
が UA-CH のインターフェースとして活用できます。
/** @see https://github.com/WICG/ua-client-hints#for-example */
const uaData = navigator.userAgentData;
const brands = uaData.brands; // [ {brand: "Google Chrome", version: "84"}, {brand: "Chromium", version: "84"} ]
const mobileness = uaData.mobile; // false
(async ()=>{
// `getHighEntropyValues()` returns a Promise, so needs to be `await`ed on.
const highEntropyValues = await uaData.getHighEntropyValues(
["platform", "platformVersion", "architecture", "model", "uaFullVersion"]);
const platform = highEntropyValues.platform; // "Mac OS X"
const platformVersion = highEntropyValues.platformVersion; //"10_15_4"
const architecture = highEntropyValues.architecture; // "x86"
const model = highEntropyValues.model; // ""
const uaFullVersion = highEntropyValues.uaFullVersion; // "84.0.4113.0"
})();
これにより、ブラウザの特定 及び デバイス判定(Desktop か Mobile) を行うことができます。
UA で文字列を正規表現で舐めて判定していた時代より随分に楽になりそうです。
ただ、userAgentData
の実装は Chromium ベースのブラウザなの(このインターフェースの仕様も半年で微妙に変わっている...ex brands
が3ヶ月前は uaList
だったり)で、他のベンダ(Firefox, Safari)が UA を廃止した時の代替実装がまだどういうものなのか、また、userAgentData を実装するかさえもわからないため、Client 側の判定方針は今後も注視する必要がありそうです。
Ref ) Chromium ベースのブラウザは検証している方がいるので参考までに
Web Server 側で UA-CH を取得する
先程のコードブロックに記載通り、Web Server 側では Request Header に UA-CH の情報が含まれているので、
Client のように Web API を介した情報の取得をせずとも対応が可能なので、Server 側のほうが現行だと対応しやすいかもしれません。
Server はリクエストを受ける側ですので、UA-CH と UA で判定処理をかき分けておけばある程度の状態は把握することができるでしょう。
例えば、「モバイルかどうか」の判定は以下のようなコードで表現できたりします。
type LegacyUA = string | undefined;
type CHUAM = string | string[] | undefined;
const judgeIsMobile = (legacyUA: LegacyUA, CHUAM: CHUAM): boolean => {
let isLegacyMobile = false;
let isCHMobile = false;
/** Legacy User-Agent judgment */
if (legacyUA) {
const isIOS = /iP(hone|(o|a)d)/.test(legacyUA);
const isAndroid = /Android/.test(legacyUA);
isLegacyMobile = isIOS || isAndroid;
}
/**
* Client Hints judgment for Chromium
* CHUAM (sec-ch-ua-mobile) has `?0 (false)` or `?1 (true)` judgment value.
*/
if (typeof CHUAM === 'string') {
const capture = /^\?(?<mobile>\d{1})$/.exec(CHUAM);
const hasMobile = capture?.groups?.mobile || '0';
isCHMobile = Boolean(parseInt(hasMobile));
}
return isLegacyMobile || isCHMobile;
};
/** Usage */
const legacyUA = req.headers['user-agent'];
const CHUAM = req.headers['sec-ch-ua-mobile'];
const isMobile = judgeIsMobile(legacyUA, CHUAM);
/** Use isMobile logics */
// ...
※1 上記の処理を npm パッケージ として使えるように公開してみました。需要があるかわかりませんが、今後UA-CHに動きがあれば更新していきたいと思います。
※2 サーバサイドも CPU Bitness のインターフェースが追加されたり まだまだ動きがありそうです。
UA-CH を効率良く使う
これまでは UA-CH の現行の仕様で何ができるかを考えてみました。
ただ、UA-CH はデフォルトの値だけではなく、Viewport
などにも対応しています。
例えば、HTML に <meta httpEquiv="Accept-CH" content="Viewport-Width, Width" />
を設置して、Web Server 側でリクエストを受け付けるとします。
Request Header には viewport-width
の値が反映された状態でリクエストが来ます。これは便利ですね。
デバイス判定はできないにしても、CSSレイアウトなど Viewport
で振り分けていることって意外と多いので、デバイスの Width などで判定をするなどのビジネルルールでソフトウェアを開発する場合などは、こちらのほうが自分たちで制御しやすいのでいいのでは?とも考えられます。
UA-CH がカバーしているプロパティは、chromium 内のこのソースコードで確認できます。
まとめ
現行の世界線とこれからの世界線がどういうことになりそうかまとめてみます。
Chromium (Chrome など) | Webkit (Safari) | Gecko (Firefox) | |
---|---|---|---|
Client |
|
|
|
Web Server |
|
|
|
現行だと、Chrome はなんとかなりそうですが、Safari, Firefox は注視していく必要がありそうです。ただ、今年は感染症の影響もあり、Chrome 側も UA の本格的な廃止は来年にずれ込むようです。chrome v89 でも凍結はまだそう。
Firefox はこちらのようなポジションを取っており、また、Tor ブラウザも Gecko ベースなので 慎重になっているのかなという考察しています。
フロントエンドは navigator.userAgentData
の I/F が結構な頻度で変わっているのもあり、現行プロダクションレベルでの対応は中々骨が折れそうですが、Web Server 側での分岐処理などは一旦事前に検討しておくことが可能な印象です。BFF など上手く活用できるとクライアントの煩雑な処理はある程度避けられるのかなと考えたりしています。
数年で UA 周りの景色はだいぶ変わるかもしれませんね。