CloudFront FunctionsでWordPressからWebP形式の画像を配信する

2021年12月9日

こんにちわ@・ェ・)めー。

ブログやWebサイトを高速化する方法として次世代フォーマットでの画像配信を行うというものがあります。おなじみのJPEGやPNG形式の画像から、WebPという次世代の形式の画像にすることで同じ画質でも画像のサイズが小さくなり、より早く転送できるというわけです。このブログもPageSpeedInsightの指摘に出てきたのでCloudFront Functionsを使ってWordPressのWebP対応を行ってみようとおもいます。

CloudFrontを使っているWordPressでWebP対応をするには

CloudFrontを利用していない場合

まずはCloudFrontを利用していない構成を理解します。

CloudFrontを使っていないWordPressはEWWW Image Optimizerプラグインを使うと比較的簡単にWebP対応を行うことができます。このプラグインはApacheのmod_rewriteを使ってWebP対応を実現しています。これが何をやっているかと以下のようなフローになります。

ブラウザ

画像(JPG・PNG・GIF)をリクエストするよ

WordPress

ブラウザがWebPに対応していれば
WebP形式の画像がリクエストされたことにして
WebP形式の画像を返すよ

ここで重要なのはWordPressのサーバーは従来の画像のリクエストが来たら、WebP形式の画像がリクエストされたことにしてWebP形式の画像を返していることです。「されたことにして」というのがポイントです。.htaccessに追加したリライトルールによってリクエストされた画像のURLに変更を行っているんですね。

このフローはCloudFrontのようなCDNを使っている場合に状況が変わってきます。

CloudFrontを利用している場合

CloudFrontのようなCDNを利用している場合はどうなるかというと……

ブラウザ

画像をリクエストするよ

CloudFront

画像のレスポンスを返すよ

WordPress

リクエストまだかな

このようにブラウザからのリクエストをCloudFrontが受け付けてCloudFrontが画像のレスポンスを返すため、そもそもリクエストがWordPressに届きません。ApacheやWordPressの設定を変えてもリクエストが届かないので、WordPressからはどうすることもできないんですね。

CloudFrontはリクエストされた画像やCSSなど静的なコンテンツを高速に返すことに特化しているので今まではここに手を入れることができませんでした。

これを解決するアプローチは少なくとも2通り考えられます。「最初からクライアントがWebP形式の画像をリクエストする」か「CloudFrontがうまいことよろしくやる」です。今回は後者を採用することにしました。

CloudFront Functionsとは

2021年5月にリリースされたばかりの新しいAWSのサービス、CloudFront Functionsを使ってWebP対応を行うことにしました。

CloudFront Functionsが何なのかというとCloudFront上でJavaScriptのコードを実行できるサービスです。既存のサービスで似たようなものにLambda@Edgeというものがありますが、CloudFront FunctionsはLambda@Edgeよりも機能と制限が厳しい代わりに高速で料金も安いという特徴があります。特に実行時間の制限は厳しく、すべての処理を1msで終わらせる必要があります。

目的に対してちょうどいいサービスだし、まだ使っている人が少なくて面白そうだったのでお勉強も兼ねてやってみようかなといったところです。

両者の詳しい違いについてはDevelopersIOの記事が参考になりました。

WebP対応の手順

前提事項

アクセスしてくるブラウザは全てWebP形式の画像に対応しているものとします。IEや一部のブラウザはWebPに対応していませんが潔く切り捨てることにします。

ブラウザWebP対応状況備考
IE×対応されることは無いでしょう
Edge
Chrome
Firefox
Safari(Mac)macOS 11 Big Surから対応
Safari(iOS)iOS14から対応
Chrome(Android)

ブラウザごとの詳細な対応状況は以下のページを参照してください。

各ブラウザのWebP対応状況

事前準備:プラグインのインストール

まずはJPGやPNG画像をアップロードした際に自動的にWebP形式の画像を作成するようにします。これはEWWW Image Optimizerプラグインで実現します。プラグインをインストールして以下の設定にします。

タブ項目
基本WebP変換ON
JS WebPリライトOFF
<picture> WebPリライトOFF
高度な設定定期的な最適化OFF

EWWW Image OptimizerプラグインにはWebP対応の機能がありますが、説明したとおりCloudFrontがある場合は使うことができません。このプラグインは自動的にWebP形式の画像を作成することに使います。

事前準備:既存画像の一括最適化

多くの場合、WordPressは既に運用中でメディアライブラリに複数の画像がアップロード済みの状況かと思います。メディアの一括最適化から「最適化されていない画像をスキャンする」を選択して一括で既存画像のWebP版を作成しておきます。

CloudFront Functionsの登録

CloudFrontでWebP形式に対応する画像はメディアライブラリ内にある画像に限定してWordPress本体、テーマやプラグイン内蔵の画像は対象外にします。

私のブログの場合はメディアの設定で「アップロードしたファイルを年月ベースのフォルダーに整理」をONにしているので画像のURLはこんな感じ(/wp-content/uploads/2021/05/abc.jpg)になります。そのためuploads/数字4桁のURLでJPGもしくはPNGのときはWebP形式の画像がリクエストされたことにする、というコードにしています。

function handler(event) {
    var request = event.request;    
    var uri = request.uri;
    
    if (uri.match(/uploads\/\d{4}/) && (uri.endsWith('jpg') || uri.endsWith('png'))) {
        console.log(uri);
        
        request.uri += ".webp";
    }
    
    return request;
}

このコードをAWSのCloudFront Functionsに登録し、WordPressで使用しているディストリビューションのビヘイビア「wp-content/*」にビューワーリクエストとして紐付けます。

動作確認とテスト

テストその1:デベロッパーツールで確認する

ブラウザのデベロッパーツールを開き、ネットワークタブを選択します。ブログを再読込し、画像のリクエスト・レスポンスを確認します。

Request URLでJPGもしくはPNG形式の画像のとき、Response Headersのcontent-typeが「image/webp」になっていれば成功です。あわせてCloudWatchにCloudFront Functionsのログが出力されていることも確認します。

テストその2:コマンドで確認する

Macなどでcurlコマンドが使用できる場合はターミナルから下記の要領で確認することができます。

curl -i 'https://www.example.com/wp-content/uploads/xxxx/xx/xxx.jpg'

デベロッパーツールと確認する箇所は同じ。content-typeが「image/webp」になっていれば成功です。

PageSpeed Insights

PageSpeed Insightsでブログの再計測を行います。改善できる項目に「次世代フォーマットでの画像の配信」が出てこないか、メディアライブラリ内の画像の指摘がなければ成功です。

WebP生成前のCloudFrontのキャッシュに注意

画像をアップロードした際にEWWW Image OptimizerプラグインがバックグラウンドでWebP版の画像を生成します。この時、まだWebP版の画像が生成される前にブラウザから画像へのリクエストが送られるとCloudFrontが「画像が存在しないレスポンス」をキャッシュしてしまいます。そのためWebP版の画像が生成された後に画像にアクセスしようとしてもCloudFrontから返ってくるのは「画像が存在しないレスポンス」となってしまいます。

これを解決するにはキャッシュのTTLまで待つか、CloudFrontにInvalidationsのリクエストを投げてキャッシュを無効化すると改めてWebP版の画像にアクセスすることができます。メディアライブラリに画像をアップロードする場合はこの状態になりませんでしたが、記事の編集画面に直接画像をドロップすると発生しました。

また、今回紹介した方法では管理画面と公開用のドメインが同一である場合、管理画面の画像もCloudFront Functionsの処理対象となります。アップロードした画像が表示されない場合は各種AWSのログを参照することをおすすめします。