SMARTCAMP Engineer Blog

スマートキャンプ株式会社(SMARTCAMP Co., Ltd.)のエンジニアブログです。業務で取り入れた新しい技術や試行錯誤を知見として共有していきます。

CloudflareのImage Resizingを使ってサクッと画像最適化をする方法

スマートキャンプ、エンジニアの入山です。

皆さんが運営されているWebサイトには、画像が何枚使われていますか?

また、その画像たちは最適化(表示領域に対して画像のサイズや画質が適切に設定)されていますか?

Webページの評価基準としてCore Web Vitals(CWV)が重要視されている昨今においては、表示速度(Performance)と向き合う機会が増え、配信する画像のサイズや画質にも気を配る必要が出てきました。

しかし、Webサイト上に数多く存在する画像を自前で最適な状態に管理して配信するのは簡単ではないため、対応できていないケースも多いのではないでしょうか。

弊社も画像の最適化には長らく対応できていない状況でしたが、CDNの機能を活用することで比較的簡単に改善できました。

本記事では、CDN(Cloudflare)での画像最適化(Image Resizing)について、紹介したいと思います!

CDNでの画像最適化の概要

CDNでの画像最適化は、Webサイトの画像をCDNのエッジ上で動的に加工・変換する機能です。
CDNで元画像を動的に変換して配信することにより、画像の管理コストはそのままにマルチデバイス対応などで必要な画像の加工・変換を簡単に実現できるメリットがあります。

画像最適化の主な機能としては、以下が挙げられます。

  • サイズ変更
  • 画質調整
  • 圧縮
  • WebPやAVIF形式への変換

機能名称や詳細な仕様は異なりますが、CloudflareやAkamaiなど、ほとんどのCDNで提供されています。

弊社ではCDNにCloudflareを採用しているため、本記事ではCloudflareでの画像最適化の設定方法を紹介します。

Cloudflareでの画像最適化設定

Cloudflareで画像最適化(Image Resizing)を利用するには、以下2つの方法が存在します。

  1. 各画像URLに画像最適化のパラメタを付与する方法
  2. Cloudflare Workersで画像へのリクエストをハンドリングして一括適用する方法

1の方法は、各画像URLにパラメタを含む形式で設定を行なうため、画像ごとに最適なパラメタを指定しやすいですが、各画像URLの修正が必要となります。詳細は公式ドキュメントを参照してください。

// 形式
https://ZONE/cdn-cgi/image/OPTIONS/SOURCE-IMAGE

// サンプル(後述のWorkersと同じオプションを指定)
https://example.com/cdn-cgi/image/fit=scale-down,width=400,height=300,quality=90,format=auto/image.jpg

2の方法は、Cloudflare Workersを使って設定を行ない、画像リクエストをエッジ上で処理するため、既存コードの修正なしで画像最適化を広範囲に一括適応できます。

今回は、2の方法で設定していきます。

利用条件

前述2の方法には、以下が必要となります。
(※ 2021/10/28時点の情報です。最新の情報は、公式サイトを参照してください。)

  • Proプラン($20/月)以上の契約
  • 利用料金
    • Image Resizing
      • $9/5万リクエストごと(最初の5万リクエストまでは無料)
    • Workers
      • $5/月(1000万リクエストまで)+ $0.5/100万リクエスト毎

以前はBusinessプラン($200/月)以上でしか使えなかったのですが、現在はProプランから利用できるようになっており、導入しやすくなっています。

Image Resizing機能の有効化

Image Resizingを利用するには、Cloudflareの管理画面から機能の有効化が必要です。

  1. CloudflareダッシュボードのSpeedを押す
  2. 最適化タブを押す
  3. Image Resizing項目を有効化

f:id:mt_iri:20211026151202p:plain

機能が無効化されていたり、前項のプラン条件を満たしていない場合は、次項でWorkersを実装しても画像が最適化されないため、注意が必要です。

Workersの設定

Cloudflare Workersは、CDNの各エッジでスクリプトを実行できるサーバーレスなサービスです。

今回は、画像へのリクエストをWorkersでハンドリングして、Image Resizingで最適化したものを返す仕組みとなります。

1. Workersの作成

以下の通り遷移し、Workerを作成します。

  1. CloudflareダッシュボードのWorkersを押す
  2. Workers項目のWorkersを管理するを押す
  3. Workersタブ内のWorkerを作成を押す

f:id:mt_iri:20211026223611p:plain

スクリプトを記述するコンソールが表示されるので、Image Resizingを適用するためのスクリプトを記述します。 以下は、スクリプトのサンプルです。

addEventListener("fetch", event => {
  // リクエストループ防止
  if (/image-resizing/.test(event.request.headers.get("via"))) {
    return fetch(event.request)
  }

  // 適応対象画像を拡張子で判定
  if (event.request.url.match(/\jpg$|jpeg$|png$/i)) {
    return event.respondWith(handleRequest(event.request))
  } else {
    return fetch(event.request)
  }
})

async function handleRequest(request) {
  const url = new URL(request.url)

  const imageRequest = new Request(url, {
    headers: request.headers,
  })

  // リクエスト元ブラウザがWebPに対応していればwebpに変換
  const acceptHeader = Object.fromEntries(request.headers).accept
  const format = acceptHeader.match(/image\/webp/) ? 'webp' : ''

  // 画像最適化のオプション指定
  const options = {  
    cf: {
      image: {
        fit: "scale-down",
        width: 400,
        height: 300,
        quality: 90,
        format : format
      }
    }
  }

  const response = await fetch(imageRequest, options)

  if (response.ok || response.status == 304) {
    return response
  } else {
    // 画像最適化がエラーになった場合は元画像にリダイレクト
    return response.redirect(imageRequest, 307)
  }
}

上記スクリプトの画像最適化オプションは、以下の代表的な項目に絞っています。

  • fit
    • 画像サイズ変更時のモード
  • width / height
    • リサイズ後のサイズ上限(fitの指定に従って、両方のサイズが設定値に収まるまでリサイズ)
  • quality
    • 画質設定
  • format
    • 変換画像の出力形式

この他にも数多くの画像最適化オプションが存在し、細かく指定が可能となっています。詳細は、公式ドキュメントを参照してください。

また、ブラウザやデバイスなどで設定値を動的に変更したい場合は、formatのようにoptionsの前段で変数化し、可変にすることで柔軟に対応できます。

コンソールでスクリプトを記述後、保存してデプロイするを押すとWorkerが作成・デプロイされます。

なお、Workerは作成・デプロイしただけでは適用されない仕様となっており、この時点で既存のWebサイトには影響ありません。

次項で適用するURL(ルート)を指定することで、初めてリクエストがWorkerで処理されるようになります。

2. Workersの適用(ルーティング)

以下の通り遷移し、Workerを実行するルートを追加します。

  1. CloudflareダッシュボードのWorkersを押す
  2. Workers項目のルートを追加を押す

f:id:mt_iri:20211027004341p:plain

ルート項目には、スクリプトを実行したいURLを指定します。
アスタリスク(*)で複数URLの一括指定も可能です。以下は、ルートの設定例です。

example.com/images/*

Worker項目には、前項で作成したWorkerを指定します。

最後に保存を押すとルートとWorkerが紐付けされ、CDNの各エッジで画像最適化が実行される様になります。

Workerとルートの紐付けは複数設定できるため、同様に適用したいURLを追加すれば設定完了です。

画像最適化の効果

画像変換の例

今回作成したWorkerによって画像が最適化された場合、画像がどのように変換されるのかを一例としてご紹介します。

以下は弊社BOXILで実際に配信されている画像で、Webページ上では318×254のサイズで表示されるようになっています。

before after
画像 f:id:mt_iri:20211027023635p:plain f:id:mt_iri:20211027023652p:plain
形式 JPEG WebP
サイズ 1024×576 400×225
容量 130kB 8.7kB

318×254の表示領域に対して1024×576の画像が読み込まれていましたが、Image Resizingのリサイズオプションで指定したwidthの400を基準に、400×225まで画像サイズが縮小されています。

また、画像サイズの縮小とquality:90の画質調整によって、容量も130kBから8.7kBまで小さくなっており、画像形式もWebPに変換されています。

私見ですが、318×254で両方の画像を表示した場合でも、目視で見分けはつきませんでした。

なお、quality値の適正値を検証する際は、SSIMという画質評価指標が存在するため、そちらを利用するのが良いと思います。

Lighthouseの評価

Lighthouseの評価指標の中にProperly size imagesという項目があり、配信されている画像のサイズが適切であるかが評価されています。

弊社BOXILでの例ですが、画像最適化の適用によりProperly size imagesの評価は、以下のようになりました。

before after
f:id:mt_iri:20211027032909p:plain f:id:mt_iri:20211027032929p:plain

警告(赤色)だった評価が正常(緑色)まで改善されており、画像最適化の効果が実感できます。

画像最適化を適用した他のページでも同様の改善効果が得られました。

まとめ

今回の記事では、CDNでの画像最適化について紹介しました!

Cloudflareでの設定方法を取り上げましたが、他のCDNでも同様の機能が提供されているため、一度試してみてはいかがでしょうか?
見境なく適用すると利用料金が高くなってしまう可能性があるものの、手軽に画像最適化を行なう手段の一つとして有効だと思います。

また、Cloudflareでは直近でCloudflare Imagesがリリースされていたり、PolishMirageといった画像最適化も提供されています。

近年ではCDNの高機能化が進んでおり、画像だけでなくコンテンツ配信に関連する機能が多く提供されています。
あらためてCDNを調べてみると、有用な発見があるのではないでしょうか。参考になれば幸いです!

宣伝

スマートキャンプ株式会社では現在Webアプリケーションエンジニアを積極採用中です! 興味を持たれた方は以下のリンクをご覧ください!

smartcamp.co.jp