以前、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でテストしたのですが、デフォルトの設定では、以下のようにroot
がlocation /
に記述されていたので、
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を使うなどトリッキーな方法しかなさそうです。
こちらが近いかなーと思いましたが、
**拡張子が置換されたファイルの有無を確認する(そのパス文字列を構成する)**というロジックが実現できませんでした。
こちらの記事で見つけました。
次のような設定で拡張子の置換ができるようですが、拡張子追加の記述の方がわかりやすいです。
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
を追加する方法がオススメなのでした。