CloudFrontでモバイルデバイスのOS判別ができるCloudFront-Is-Android-Viewer/CloudFront-Is-IOS-Viewerヘッダを確認してみた

CloudFrontがオリジンへのリクエストの際に付与するヘッダで、ユーザが使用しているデバイスがモバイルであるかの判定のほか、モバイルデイバスのOS(Android/iOS)についても判定が可能です。実際に動作を確認してみました。
2022.06.26

はじめに

清水です。AWSのCDNサービスであるAmazon CloudFrontでは以前からユーザがコンテンツ表示に使用しているデバイスを判定するCloudFront-Is-Mobile-Viewerヘッダなどが利用可能でした。この機能自体は2014年6月のアップデートで追加されたものです。

当時はCloudFront-Is-Mobile-ViewerCloudFront-Is-Tablet-ViewerCloudFront-Is-Desktop-Viewerの3つのみでしたが2018年11月の段階でCloudFront-Is-SmartTV-Viewerが追加されていました。(追加された具体的なタイミングは不明です。)

これらはリクエスト時のUser-Agentヘッダの値に基づいてCloudFront側でそれぞれtrue/falseに設定されます。オリジン側でこの値を参照することでモバイル用/PC用のコンテンツを出し分けたり、CloudFront側で個別のキャッシュとして扱うということが可能でした。(モバイル/PCの出し分けはオリジン側でUser-Agentヘッダを使用しても実現できますが、CloudFrontでキャッシュさせる場合にはUser-Agentヘッダの転送は適切ではありません。またモバイル/PC判定の処理をCloudFront側にオフロードできるというメリットもあるかと考えます。)

さて、これらCloudFront-Is-*-Viewerヘッダの詳細についてはCloudFront Developer Guideの上記の箇所に記載があったわけですが、先日ふとCloudFront Developer Guideを眺めていたところ別の箇所でも同様のデバイスを特定するヘッダについての記載を確認することができました。そしてなんとこちらは、CloudFront-Is-Android-ViewerCloudFront-Is-IOS-Viewerという、モバイルデバイスのOSを判定するヘッダも追加されています!

「ビューワーのデバイスタイプを特定するためのヘッダー」 - CloudFront HTTP ヘッダーの追加 - Amazon CloudFront

本エントリではこのCloudFront-Is-Android-ViewerヘッダとCloudFront-Is-IOS-Viewerヘッダについて、実際に動作を確認してみたのでまとめてみます。CloudFront Distributionの設定でLegacy cache settingsを使用した場合は「forward all headers」とするとこれらモバイルOS判定のヘッダは利用できない(オリジンに転送されない)ため個別に「add header」で追加する必要があるなど癖?のようなものがあるようですので、Origin request policyの使用を前提として使うのが良いのかなと考えます。

CloudFront-Is-Android-Viewer/CloudFront-Is-IOS-Viewerヘッダはいつから利用可能になっていた?

実際に設定方法・動作確認を行う前に、このCloudFront-Is-Android-ViewerヘッダとCloudFront-Is-IOS-Viewerヘッダ、いつから利用可能になっていたか確認しておきましょう。CloudFrontのWhat's Newのページを確認してみますが、最近のアップデートでそれらしき項目はありません。

それなら、と、Developer Guideの該当ページをINTERNET ARCHIVE Wayback Machineでさかのぼってみましょう。「Adding the CloudFront HTTP headers - Amazon CloudFront」のアーカイブを確認しましたが、最も古いもので2020/10/28のアーカイブが確認できました。この段階からすでに、CloudFront-Is-Android-Viewer/CloudFront-Is-IOS-Viewerヘッダが案内されていますね。

Using the CloudFront HTTP headers - Amazon CloudFront (INTERNET ARCHIVE Wayback Machine)

後述しますが、このモバイルデバイスのOSを判定するCloudFront-Is-Android-Viewer/CloudFront-Is-IOS-Viewerヘッダは「Legacy cache settings」の「forward all headers」とした場合には利用できません。(個別に「add header」すると利用できますが。)またDeveloper Guideの記載箇所としては「ポリシーの使用」の章の「CloudFront HTTP ヘッダーの追加」になっています。(Legacy cache settingsの設定項目に該当するDeveloper Guideの記載箇所「デバイスタイプに基づいてキャッシュを設定する」 には、デバイス判別のヘッダのみの解説でこのモバイルデバイスOSの判別用ヘッダの記載はありません。)このポリシーの使用(Origin request policyとCache policy)についての機能アップデートがあったのが2020年7月末のことです。

また同じタイミング(上記アップデートの直後)でこのOrigin request policyで利用できる詳細なジオロケーションヘッダも追加されていました。

これらから、CloudFront-Is-Android-Viewer/CloudFront-Is-IOS-Viewerヘッダについても同じようにOrigin request policyが利用可能になったタイミングで追加されていたのかな、と推測しています。

Origin request policyでCloudFront-Is-Android-Viewer/CloudFront-Is-IOS-Viewerヘッダを使ってみる

それでは実際に、Origin request policyを使ってCloudFront-Is-Android-Viewer/CloudFront-Is-IOS-Viewerヘッダの設定を行い動作を確認していきます。今回CloudFrontに設定するオリジンはEC2インスタンスとし、内部でApache HTTP ServerとPHPを動作させておきます。また以下のファイルindex.phpを配置しておきました。デフォルトルート(/)にアクセスすれば、アクセス時のリクエストヘッダ情報が参照できる状態です。

index.php

<?php

foreach (getallheaders() as $name => $value) {
    echo "<p>";
    echo "$name: $value";
    echo "</p>\n";
}

?>

Origin request policyの作成

CloudFront側の設定、まずはOrigin request policyを作成していきます。CloudFrontのマネジメントコンソール、PoliciesのページからOrigin requestタブの[Create origin request policy]ボタンで進みます。NameとDescriptionを適切に設定します。

Origin request settingsの項目ではHeadersで「All viewer headers and the following CloudFront headers」を選択しました。(All viewer headersはに似ですが、CloudFront headersを選択する必要があります。)Add headerでOrigin requestに含めるCloudFront headersを選択します。デバイス(含むOS)判定に使用可能な以下6つを選択しました。

  • CloudFront-Is-Mobile-Viewer
  • CloudFront-Is-Tablet-Viewer
  • CloudFront-Is-SmartTV-Viewer
  • CloudFront-Is-Desktop-Viewer
  • CloudFront-Is-IOS-Viewer
  • CloudFront-Is-Android-Viewer

[Create]ボタンで作成します。

Distributionの作成

続いて上記Origin request policyを使用するDistributionを作成していきます。ポイントとなる「Cache key and origin requests」の項目のみまとめます。

「Cache key and origin requests」の項目で「Cache policy and origin request policy (recommended)」を選択します。Cache policyは今回は検証目的ということで「CachingDisabled」を選択しました。キャッシュを有効とする場合には、キャッシュキーに今回確認するCloudFront-Is-*-Viewerのヘッダを適切に含めるようにしましょう。Origin request policyで先ほど作成した、CloudFront-Is-*-ViewerをCloudFront headersに含めたもの指定します。(ここでは「Custom-DeviceDetectionHeaders」。)

CloudFront-Is-Android-Viewer/CloudFront-Is-IOS-Vieweヘッダの値を確認してみる

CloudFront Distributionが作成できたら、このドメイン名にアクセスしてCloudFront-Is-Android-Viewerヘッダ、CloudFront-Is-IOS-Vieweヘッダの値を確認してみましょう。

まずはMac版Google Chromeブラウザでアクセスしてみます。CloudFront-Is-Android-Viewer/CloudFront-Is-IOS-Vieweの2つのヘッダを確認することはできますが、当然ながらどちらもfalseとなります。(もちろん、CloudFront-Is-Desktop-Viewertrueでデバイス判定自体は正しく動作していますね。)

続いてなぜか発売日に購入したiPhone 13 ProのSafariブラウザでアクセスしてみました。しっかりとCloudFront-Is-IOS-Viewetrueになっていますね。またCloudFront-Is-Mobile-Viewerヘッダもtrueになっています。

さてこのCloudFront-Is-Android-Viewer/CloudFront-Is-IOS-VieweヘッダはUser-AgentヘッダをもとにCloudFront側で設定している、とDeveloper Guideに記載があります。Androidデバイスの準備ができなかったため、curlコマンドでUser-Agentヘッダを偽装した場合の挙動を確認してみました。

まずはUser-Agentヘッダを指定しなかったときのcurlコマンドの実行結果です。デスクトップ端末として判定されていることがわかります。

% curl https://d1twxxxxxxxxxx.cloudfront.net/
<p>Host: d1twxxxxxxxxxx.cloudfront.net</p>
<p>User-Agent: curl/7.64.1</p>
<p>X-Amz-Cf-Id: cH_OxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxLQ==</p>
<p>Connection: Keep-Alive</p>
<p>Via: 2.0 d8e9xxxxxxxxxxxxxxxxxxxxxxxx9592.cloudfront.net (CloudFront)</p>
<p>X-Forwarded-For: 2xxx:xx:xxxx:xxx:xxxx:xxxx:xxxx:xxxe</p>
<p>Accept: */*</p>
<p>CloudFront-Is-Mobile-Viewer: false</p>
<p>CloudFront-Is-Tablet-Viewer: false</p>
<p>CloudFront-Is-SmartTV-Viewer: false</p>
<p>CloudFront-Is-Desktop-Viewer: true</p>
<p>CloudFront-Is-IOS-Viewer: false</p>
<p>CloudFront-Is-Android-Viewer: false</p>

続いて、curlコマンドのオプションで-H "User-Agent: Android"をつけてみました。実際はUser-Agentヘッダにより詳細な情報(Androidのバージョンやブラウザ種別など)が記載されるかと思いますが、シンプルにAndroidの文字列で以下のようにAndroid OS使用と判定されました(CloudFront-Is-Android-Viewer: true)。またモバイル判定のCloudFront-Is-Mobile-Viewerヘッダもtrueとなっています。

% curl https://d1twxxxxxxxxxx.cloudfront.net/ -H "User-Agent: Android"
<p>Host: d1twxxxxxxxxxx.cloudfront.net</p>
<p>User-Agent: Android</p>
<p>X-Amz-Cf-Id: ouDTxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxOA==</p>
<p>Connection: Keep-Alive</p>
<p>Via: 2.0 e6b8xxxxxxxxxxxxxxxxxxxxxxxx55ee.cloudfront.net (CloudFront)</p>
<p>X-Forwarded-For: 2xxx:xx:xxxx:xxx:xxxx:xxxx:xxxx:xxxe</p>
<p>Accept: */*</p>
<p>CloudFront-Is-Mobile-Viewer: true</p>
<p>CloudFront-Is-Tablet-Viewer: false</p>
<p>CloudFront-Is-SmartTV-Viewer: false</p>
<p>CloudFront-Is-Desktop-Viewer: false</p>
<p>CloudFront-Is-IOS-Viewer: false</p>
<p>CloudFront-Is-Android-Viewer: true</p>

もう一つ、curlコマンドのオプションで-H "User-Agent: iPad"をつけてみました。こちらもiOS使用と判定されますね(CloudFront-Is-IOS-Viewer: true)。 またCloudFront-Is-Mobile-ViewerCloudFront-Is-Tablet-Viewerがともにtrueとなっています。(これはDeveloper Guide記載の通り、一部のタブレットデバイスに両方をtrueにするケースですね。)

% curl https://d1twxxxxxxxxxx.cloudfront.net/ -H "User-Agent: iPad"
<p>Host: d1twxxxxxxxxxx.cloudfront.net</p>
<p>User-Agent: iPad</p>
<p>X-Amz-Cf-Id: PSJ0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxeg==</p>
<p>Connection: Keep-Alive</p>
<p>Via: 2.0 1ec5xxxxxxxxxxxxxxxxxxxxxxxx7e8e.cloudfront.net (CloudFront)</p>
<p>X-Forwarded-For: 2xxx:xx:xxxx:xxx:xxxx:xxxx:xxxx:xxxe</p>
<p>Accept: */*</p>
<p>CloudFront-Is-Mobile-Viewer: true</p>
<p>CloudFront-Is-Tablet-Viewer: true</p>
<p>CloudFront-Is-SmartTV-Viewer: false</p>
<p>CloudFront-Is-Desktop-Viewer: false</p>
<p>CloudFront-Is-IOS-Viewer: true</p>
<p>CloudFront-Is-Android-Viewer: false</p>

Legacy cache settings使用の場合のCloudFront-Is-Android-Viewer/CloudFront-Is-IOS-Viewerヘッダの挙動

これらCloudFront-Is-Android-ViewerヘッダとCloudFront-Is-IOS-Viewerヘッダですが、Developer Guideの記載箇所が「ポリシーの使用」の章の「CloudFront HTTP ヘッダーの追加」であることから、Origin request policy(とCache policy)使用時にのみ使える機能であり、Legacy cache settingsでは使用できないのではないか、という予想ができます。この点も実際に確認しておきましょう。(後述しますが、この予想は正解ではありませんでした。)

Legacy cache settingsでforward all headersとした場合

オリジンとなるEC2は先ほどと同様の設定で、「Cache key and origin requests」の項目で「Legacy cache settings」を設定したCloudFront Distributionを作成していきます。「Legacy cache settings」の設定詳細では以下のように、Headersの項目で「All」を選択してすべてのヘッダをオリジンに転送するようにしました。

Distribution作成後、このCloudFrontドメイン名にアクセスしてみます。以下のように4つのデバイス判定情報のヘッダは確認できましたが、CloudFront-Is-Android-ViewerヘッダならびにCloudFront-Is-IOS-Viewerヘッダは確認できませんでした。「Legacy cache settings」で「forward all headers」とした場合はモバイルデバイスのOSを判定するヘッダは利用できないということになります。

Legacy cache settingsで転送するheaderを個別に追加する場合

続いて「Legacy cache settings」の設定、Headersで「Include the following headers」を選択し、オリジンに転送するヘッダを選択した場合の挙動です。「Add header」を選択するとオリジンに転送するヘッダを個別に選択していくことができますが、ここにCloudFront-Is-Android-Viewerヘッダ、CloudFront-Is-IOS-Viewerヘッダがあるので選択してみます。そのほか、デバイス判定の4つのヘッダも加えておきました。

Distributionを作成したら実際にアクセスしてみます。CloudFront-Is-Android-ViewerヘッダならびにCloudFront-Is-IOS-Viewerヘッダがオリジンへのリクエストに含まれていることが確認できました。「Legacy cache settings」で「Add Header」する場合には、該当のHeaderを選択することでモバイルデバイスのOS判定のヘッダも利用できるという結果になりました。

(実は、てっきりこのモバイルデバイスのOS判定用ヘッダ、CloudFront-Is-Android-ViewerヘッダならびにCloudFront-Is-IOS-ViewerヘッダはOrigin request policy使用時のみの機能でLegacy cache settingsでは使えないのでは、と思っていたのですが、「すべてのヘッダを転送」ではなく「個別に転送するヘッダを指定」するかたちであれば使用が可能でした。)

まとめ

CloudFrontのモバイルデバイスOS判定用のヘッダであるCloudFront-Is-Android-Viewer/CloudFront-Is-IOS-Viewerヘッダについて、利用可能になったタイミングを探りつつつ、実際に設定してその動作を確認してみました。ドキュメント(Developer Guide)記載の箇所などからOrigin request policyで利用することが前提になっているかとは思いますが、Legacy cache settingsでもヘッダを個別に追加するかたちであれば利用が可能でした。

PC環境(Desktop-Viewer)よりもモバイル環境(Mobile-Viewer)のほうがOSを意識する場面は多いのかな、と個人的には感じています。(PC環境であればWindows/Macというよりも、ブラウザがGoogle ChromeかFirefoxか、などを意識するかと思います。対して、モバイル環境であれば逆にブラウザよりもOS(iOSかAndroidか)を意識するのかなと思います。)そのためこのCloudFrontのCloudFront-Is-Android-Viewerヘッダ/CloudFront-Is-IOS-Viewerヘッダは便利に活用できるのかな、と考えています。冒頭でも述べましたが、キャッシュヒット率などを考慮するとUser-Agentヘッダをキャッシュキーとして利用するのは適切ではありません。モバイルデバイスのOS判定もCloudFront側に任せてしまいましょう。