Lightsail の CloudFront での EWWW Image Optimizer による webp 画像配信
PageSpeed Insights で「画像は次世代フォーマットで配信しろ」って怒られたので webp フォーマットで配信してみました。
「webp wordpress」でググると、プラグイン「EWWW Image Optimizer」を使うのが良さそう。既存の画像の変換や webp が対応していないクライアントへの配慮も出来ているとのことで導入してみたが、どうも CloudFront 配下では「webp が対応していないクライアントへの配慮」がうまく機能しておらず、ちょっと設定を弄って機能させてみました。
Lightsail の CloudFront で「EWWW Image Optimizer」を正常に動かす設定
以下の方法で webp に対応していないクライアントには jpg,png などの画像を配信します。それぞれの設定の理由は後述しています。
- RewriteRule に 301 リダイレクトを追加する。
- オリジンに転送させるヘッダーに “accept" を追加する。
RewirteRule に 301 リダイレクトの追加
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_FILENAME} (.*)\.(jpe?g|png|gif)$
RewriteCond %{REQUEST_FILENAME}\.webp -f
RewriteCond %{QUERY_STRING} !type=original
RewriteRule (.+)\.(jpe?g|png|gif)$ %{REQUEST_URI}.webp [T=image/webp,L,R=301]
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(jpe?g|png|gif)$">
Header append Vary Accept
</FilesMatch>
</IfModule>
AddType image/webp .webp
オリジンへの転送ヘッダーに “accept" の追加
なんで正常に動かない?
accept ヘッダーが削除される
クライアントが webp に対応しているかどうかの判断は、オリジンの apache がリクエストヘッダー “accept" の値を見て行います。しかし CloudFront はデフォルトではリクエストヘッダー “accept" を削除してしまい、オリジンには “accept" が届きません。そのためオリジンの apache はクライアントが webp に対応しているか判断できず、いずれのクライアントにも jpg,png を配信してしまいます。
そこでまずは CloudFront にヘッダー “accept" をオリジンに転送させるようにします。これでオリジンの apache はクライアントが webp に対応しているか判断出来ます。
CloudFront が webpファイルの画像を返してくれない
しかしそれだけでは十分ではありません。CloudFront のキャッシュにも問題があります。クライアントはあくまで jpg,png の画像ファイルをリクエストしてきます。そのリクエストに対して CloudFront にキャッシュがある場合は、当然ですが jpg,png の画像ファイルを返します。キャッシュに webp ファイルがあったとしてもクライアントは webp ファイルをリクエストしてきているわけではないので webp ファイルは返しません。
webp ファイルを返すようにするには、クライアントに webp ファイルをリクエストして貰う必要があります。それには色々な方法があるとは思いますが、ここでは「EWWW Image Optimizer」で用意してくれている apache の設定を少し弄る形で対応しています。それが上述した 301 リダイレクトです。
もうこんな処理は不要…?
ここまで話して来たわけですが、これは webp が対応していないクライアントがいる前提の話です。ちょっと前は safari が対応していなく、モバイルファーストのこの時代で iphone ユーザーが多い日本で safari に対応していないフォーマットを配信するというのはインパクトがデカかったわけです。しかしそんな safari も既に対応しています。
Added WebP image support.
Safari 14 Release Notes
というわけなので、webp に対応していないクライアントのことはそこまで考える必要がないと思います。
いやしかし
既に jpg,png などの古い形式の画像ファイルがある場合は話は別です。既存のファイルを webp に変換してくれるプラグインは沢山ありますが、各記事の画像のパスまで変換してくれるプラグイン(ある程度のダウンロード数とメンテ頻度のある中で)は見つけることは出来ませんでした。
webp に変換後に一つ一つ記事の中の画像ファイルを差し替えることも出来なくはないですが、非常に手間で漏れもあるかもしれません。それを上述した 301 リダイレクトの処理で解決出来ます。その際にクライアントが webp 対応の可否の判断もふっ飛ばして良いかもしれません。その判断が不要であれば CloudFront の変更も不要になります。
念の為 webp 対応判断の処理を省いた apache の設定が以下です。accept ヘッダーが不要なので CloudFront の設定はデフォルトのままです。
<IfModule mod_rewrite.c>
RewriteEngine On
#RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_FILENAME} (.*)\.(jpe?g|png|gif)$
RewriteCond %{REQUEST_FILENAME}\.webp -f
RewriteCond %{QUERY_STRING} !type=original
RewriteRule (.+)\.(jpe?g|png|gif)$ %{REQUEST_URI}.webp [T=image/webp,L,R=301]
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(jpe?g|png|gif)$">
Header append Vary Accept
</FilesMatch>
</IfModule>
AddType image/webp .webp