Edited at

nginxにおけるWebP画像の選択的レスポンスの設定方法

以前、Apache(.htaccessファイル)について書きました。

.htaccessによるWebPの選択的レスポンスとその問題点と改善案

今回はnginxについてです。


拡張子追加の場合(image.jpg→image.jpg.webp)

以前の記事で、.webpという拡張子は元の拡張子を置換するのではなく置き換える方がよいと思うと書きました。


  • path/to/image.jpg → path/to/image.webp ← こちらの置換方式より

  • path/to/image.jpg → path/to/image.jpg.webp ← こちらの追加方式の方がいい

nginxでは、追加方式の場合驚くほど簡単にWebP画像の出し分けを記述することができます。

こちらを参考にしました。

Recipe: serve WebP with nginx conditionally

# serverディレクティブの外で

# Acceptリクエストヘッダにimage/webpが含まれていたら$webp_suffix変数に".webp"を代入
# 含まれていない場合、$webp_suffixは""(空文字列)
map $http_accept $webp_suffix {
default "";
"~*image/webp" ".webp";
}

server {
# …中略

# 大文字小文字問わず.pngか.jpgで終わるリクエストについて
location ~* \.(png|jpe?g)$ {
# VaryヘッダとしてAcceptを返す
add_header Vary Accept;
# WebP対応ブラウザでは、image.jpg.web、image.jpgを探索
# 非対応のブラウザでは、image.jpg、image.jpgを探索
# いずれもない場合は404を返す
try_files $uri$webp_suffix $uri =404;
}
}

mapディレクティブはプログラミング言語でいうCASE~WHENのような働きをします。

server内部に記述すると設定エラーになりました。

try_filesは、先頭からファイルを探索して最初にヒットしたファイルを返すというものです。

Acceptリクエストヘッダがあるときに、末尾に.webpが付いたファイルを探索するというロジックがスマートに記述されています。

なお、自分はDocker版nginxでテストしたのですが、デフォルトの設定では、以下のようにrootlocation /に記述されていたので、

    location / {

root /usr/share/nginx/html;
index index.html index.htm;
}

次のようにネストして記述しました。

    location / {

root /usr/share/nginx/html;
index index.html index.htm;

location ~* \.(png|jpe?g)$ {
add_header Vary Accept;
try_files $uri$webp_suffix $uri =404;
}
}


拡張子置換の場合(image.jpg→image.webp)

こちらは合理的な方法を発見できませんでした。

Luaを使うなどトリッキーな方法しかなさそうです。

こちらが近いかなーと思いましたが、

WordPressでwebpを使って画像サイズ削減

拡張子が置換されたファイルの有無を確認する(そのパス文字列を構成する)というロジックが実現できませんでした。

こちらの記事で見つけました。

https://www.keycdn.com/blog/convert-to-webp-the-successor-of-jpeg

次のような設定で拡張子の置換ができるようですが、拡張子追加の記述の方がわかりやすいです。

location ~ ^(/path/to/your/images.+)\.(jpe?g|png)$ {

if ( $http_accept ~* webp ) {
set $webp "A";
}
if ( $request_filename ~ (.+)\.(png|jpe?g)$ ) {
set $file_without_ext $1;
}
if ( -f $file_without_ext.webp ) {
set $webp "${webp}E";
}

if ( $webp = AE ) {
add_header Vary Accept;
rewrite ^(.+)\.(png|jpe?g)$ $1.webp break;
}
}

nginxではifを使った時点で黄色信号っぽいですね。公式自らこんなエントリーを上げるとは…If Is Evil

この理由もあって、.jpgなどの拡張子を.webpに置き換えるのではなく、拡張子にさらに.webpを追加する方法がオススメなのでした。