AVIF・WebPでサムネイル画像を配信して、ブラウザでのパフォーマンスを大幅に改善した話

  • URLをコピーしました!

こんにちは、普段PR TIMES STORY(以下STORY)の開発リーダーをしている岩下(@iwashi623)です。

今回はSTORYのサムネイル画像の配信フォーマットを変更することによるパフォーマンス改善を行ったので、そちらについて書いていこうと思います。

目次

現状の問題点

STORYでは配信のサムネイルにユーザーからアップロードした画像を3サイズ(width800, 400, 200)にリサイズした画像を使用していました。

リサイズした画像は表示する箇所ごとに適したサイズのサムネイルを選択して、画像を配信していました。

そのような状況のSTORYで、Lighthouseを使ってパフォーマンスを計測した結果が以下です。一枚目がmobile(スマホ)、二枚目がDesktop(PC)の結果となります。

パフォーマンスの数値が悪いことがわかります。

また、パフォーマンスの原因が悪い詳細な理由(ボトルネック)については以下のようになっています。

特に「Serve images in next-gen formats」の項目がパフォーマンスを悪化させている原因だということがわかります。

こちらの項目には、

Image formats like WebP and AVIF often provide better compression than PNG or JPEG, which means faster downloads and less data consumption.

(訳: WebPやAVIFなどの画像フォーマットは、PNGやJPEGよりも圧縮率が高いことが多いため、ダウンロードの高速化とデータ消費量の削減が可能です)

と書いてあり、配信しているサムネイル画像のフォーマットがよくないことがわかります。

以前PR TIMESはFastlyのImage Optimizerを使用して、サムネイル画像にWebPを使用することでプレスリリースのサムネイル画像の高画質化と軽量化を達成しました。

あわせて読みたい
新卒エンジニアがプレスリリース画像の画質改善に取り組んだ話 こんにちは、21新卒エンジニアの柳です。 この度、プレスリリースのサムネイル画像とプレスリリース詳細ページ内で掲載されている画像の画質改善を行いました。 今回行...

そのため、STORYも同様の仕組みを使ってサムネイル画像の軽量化を達成しようと考えました。

改善方法

まず、リサイズした複数種類のサムネイル画像生成をやめて、前述したブログで述べられているように大きいサイズのサムネイル画像を一枚だけS3にアップロードするようにしました。

Fastly Image Optimizerの設定は基本的にはPR TIMESと同様の設定を行いましたが、一部差異があるため、今回は差異の部分について述べていきます。

AVIFとWebPの両方を使用する

PR TIMESでは、サムネイル画像の配信にWebPを採用しましたが、STORYではAVIFを基本的に使用しています。

AVIFもまた、WebPと同様に新しい静止画のフォーマットです。JPEGやPNGと比較して低いqualityにしても画像の劣化が少なく、結果として小さいファイルサイズで画像を配信できます。

AVIFとWebPの違いは

  • WebPはGoogleが開発した画像フォーマット
  • AVIFはAmazon・Netflix・Google・Microsoft・Mozilla等の幅広い企業が共同で開発したWebPよりも新しい画像フォーマット

のようになっています。

FastlyのImage OptimizerはAVIFへの変換をサポートしているため、今回AVIFでのサムネイル画像配信を採用しました。具体的には、

  • AVIF対応ブラウザにはAVIFを返す
  • AVIF非対応かつWebP対応ブラウザにはWebPを返す
  • AVIF・WebPともに非対応ブラウザにはJPEGを返す

のような設定を実装しました。

Image Optimizerが有効になっている時、autoパラメーターを使って画像へのリクエストにauto=webpauto=avifのようなクエリパラメーターを付与すると、自動でWebPやAVIFへの変換を行ってくれます。

しかしながら、AVIF・WebPのどちらか一方ではなく、今回のケースのようにAVIFとWebPの両方を使い分けたいことがあると思います。その場合、ブラウザのacceptヘッダーによって処理を分岐するために、vcl_recvに

if (querystring.get(req.url, "auto") == "avif,webp") {
  if (req.http.Accept ~ "\bimage/avif\b") {
      set req.url = querystring.set(req.url, "auto", "avif");
  } elsif (req.http.Accept ~ "\bimage/webp\b") {
      set req.url = querystring.set(req.url, "auto", "webp");
  }
}

のように記述する必要があるので注意が必要です。

上記のような設定をすると、auto=avif,webpのようなクエリパラメーターを加えて画像のリクエストを送信した時、acceptヘッダーに基づいて条件に合致する画像をレスポンスすることができます。

また、今回はJPEG・WebP・AVIFで画像のqualityもそれぞれ変更できるようにしたいと思いました。画像フォーマットごとに指定できる仕組みがあると、リリース後にフォーマットごとにqualityの値を調整できるためより便利だと考えたからです。

Image Optimizerのqualityパラメーターは、デフォルトでquality=85,75のように第2引数を渡すことができて、auto=webpパラメーターがある際に、WebPとそれ以外の画像のqualityを指定することができます。しかしながら、auto=avif,webpがリクエストに含まれていたときに、3つの引数を渡してAVIFのみqualityを指定するような設定は現在サポートされていません。そのためVCLを

sub vcl_recv {
  declare local var.parsedRequestQualityParam STRING;
  declare local var.quality STRING;

  set var.parsedRequestQualityParam = regsuball(querystring.get(req.url, "quality"), "%252C", ",");

  if (querystring.get(req.url, "auto") == "avif,webp") {
    if (req.http.Accept ~ "\bimage/avif\b") {
      set req.url = querystring.set(req.url, "auto", "avif");
      set var.quality = regsuball(var.parsedRequestQualityParam, "([0-9]+),([0-9]+),([0-9]+)", "\3");
      set req.url = querystring.set(req.url, "quality", var.quality);

      return(lookup);
    } elsif (req.http.Accept ~ "\bimage/webp\b") {
      set req.url = querystring.set(req.url, "auto", "webp");
    }
  }

  set var.quality = regsuball(var.parsedRequestQualityParam, "([0-9]+),([0-9]+),([0-9]+)", "\1\,\2");
  set req.url = querystring.set(req.url, "quality", var.quality);

  return(lookup);
}

のようにカスタムすることで実現しました。

上記のようなカスタムVCLを使用することで、format=jpeg&auto=avif,webp&quality=85,75,70のようなクエリパラメーターがついたリクエストが来た際、

  • AVIF対応ブラウザには画像をquality70のAVIFに変換して配信
  • AVIF非対応かつWebP対応ブラウザには画像をquality75のWebPに変換して配信
  • それ以外のブラウザには画像をquality85のJPEGに変換して配信

することができます。

AVIFとWebPにqualityの差をつけた理由は、検証段階で同qualityで比較した結果、AVIFの方が高画質であったものの、ファイルの容量が大きくなってしまったためです。AVIFとWebPがおよそ同じようなファイルサイズになるように調整した結果、WebPは75, AVIFは70という数値に落ち着きました。

Surrogate-Keyを用いたCache Purgeの導入

新規でアップロードされる画像に関してはS3にアップロードする際にx-amz-meta-surrogate-keyヘッダーを付与することにしました。これは、Fastlyで画像のキャッシュを消す際、任意のSurrogate-Keyを指定して特定のキャッシュを消す際に利用できるためです。

STORYはLaravel + AWS SDKを用いて画像をアップロードしています。なので、

's3_options' => [
     'CacheControl' => 'public, max-age=31536000',
     'Metadata' => [
       'surrogate-key' => 'hoge piyo bar fuga',
   ]
]

のように、アップロード時のMetadataとしてsurrogate-keyオプションを渡してあげるとx-amz-meta-surrogate-keyヘッダーがアップロードされたオブジェクトに付与できます。

Fastly側はDocumentを参考にして、Surrogate-Keyの設定を行いました。

Fastly Documentation

Surrogate-Keyはアップロード時にスペース区切りで複数付与できるので、

x-amz-meta-surrogate-key="hoge piyo bar fuga"

のように設定できます。この画像のキャッシュはhoge, piyo, bar, fugaのいずれかのKeyを指定したキャッシュパージをAPIまたはFastlyコンソール上から実行することで削除することができます。

改善の効果

まず、パフォーマンス面ですが大幅な向上を達成することができました。

スマートフォン表示のLighthouseの結果は

と、パフォーマンスの項目は25→66と大幅に向上しました。

PCでの表示についても、

パフォーマンスの項目が64→99と大きく向上している事がわかります。

パフォーマンスのボトルネックについても、「Serve images in next-gen formats」から別の箇所に変わりました。

またサムネイル画像の容量は、画像によりますが観測した範囲で

改善前 → content-length: 642954

改善後 → content-length: 23141

のような大きな差が生まれている画像がありました。

さらに小さいサムネイル画像については、今までの個別でリサイズしていた画像よりも大きな横幅の画像を返すようにしたこともあり、画像の容量が減少しているにも関わらず見た目が以下のように改善しました。

さいごに

ファイルサイズを小さくしてパフォーマンスを向上させたり、画像の見た目をきれいにしたりすることはユーザー体験に直結するため今回の変更を加えてよかったなと改めて実感します。

FastlyはVCLをカスタムして各々の柔軟な設定ができたり、設定の変更やキャッシュパージの時間が早かったりと使ってみてとても楽しい技術でした。

今回の改修で使用したFastlyや、その他の様々なサービスを活用したアプリケーションのパフォーマンス改善に興味のある方は、ぜひカジュアル面談でお話できると嬉しいです。

株式会社PR TIMES
カジュアル面談申し込みフォーム - 株式会社PR TIMES 株式会社PR TIMESでは現在00-0. カジュアル面談(全職種OK)を募集しています。
  • URLをコピーしました!

この記事を書いた人

PR TIMES STORYの開発をしています。
AWSのSAA/DVA/SOA/SAPを持ってます。

目次