ウェブサイトは、ブラウザが WebP をサポートしているかどうかに基づいて、対応する形式の画像を自動的に読み込むようにすることができます。コードで判断すると、非常に手間がかかるため、すべての画像の参照方法を変更する必要があります。簡単で効率的な方法はありますか?答えはあります。それは Nginx の設定を変更することです。
主なアイデア#
WebP 形式の画像をブラウザがサポートしているかどうかを判断する方法は、1×1 ピクセルの WebP 画像をフロントエンドで読み込んで幅を取得する方法だけでなく、リクエストヘッダの Accept フィールドを確認する方法もあります。このフィールドにimage/webpが含まれている場合、ブラウザは WebP をサポートしていることを意味し、そうでなければサポートしていません。
WebP をサポートしていない IE のリクエストヘッダの Accept フィールド:
WebP をサポートしている Firefox のリクエストヘッダの Accept フィールド:
WebP をサポートしている Chrome のリクエストヘッダの Accept フィールド:
これに基づいて、Nginx の設定ファイルを変更し、拡張子が **.jgp、.jpeg、.pngで終わるリクエスト ** かつリクエストヘッダの Accept フィールドにimage/webpが含まれているリクエストに対して WebP ファイルの判断を行います。この判断は Lua ファイルに記述します。Lua ファイルでは、対応する WebP ファイルが存在するかどうかをまず判断し、存在する場合は直接 WebP ファイルにリダイレクトし、存在しない場合は libwebp メソッドを呼び出して WebP ファイルを生成し、その後にリダイレクトします。
このアイデアを試してみましょう。
変更の準備#
Nginx で Lua モジュールをインストールする#
省略
サーバーで libwebp をインストールする#
参考:Nginx+Lua+libwebp 实现服务器图片自动转 WebP
Nginx.conf#
if 文では複数の条件を同時に判断することができないため、変数を定義し、if 文で変数の値を変更し、最後にその変数の値に基づいてすべての条件を満たしているかどうかを判断します。
location /img {
    set  $cwebp_flag 0;
    if ($uri ~ \.(png|jpg|jpeg)$) {
        set  $cwebp_flag '${cwebp_flag}1';
    }
    if ($http_accept ~ image/webp) {
        set  $cwebp_flag '${cwebp_flag}1';
    }
    if ($cwebp_flag = 011) {
        content_by_lua_file  lua/imgProcess.lua;
    }
}
imgProcess.lua#
このファイルの主な役割は、対応する WebP ファイルが存在するかどうかをまず判断し、存在する場合は直接 WebP ファイルにリダイレクトし、存在しない場合は libwebp メソッドを呼び出して WebP ファイルを生成し、その後にリダイレクトすることです。
function file_exists(name)
	local f=io.open(name,"r");
    if f~=nil then io.close(f) return true else return false end
end
 
local originalFile = ngx.var.request_filename;
local newFile = ngx.var.request_filename .. ".webp";
if not file_exists(newFile) then
  if not file_exists(originalFile) then
    ngx.exit(404);
    return;
  end
  os.execute("cwebp -q 75 " .. originalFile  .. " -o " .. newFile); 
  if file_exists(newFile) then
    ngx.header.vary = 'accept, accept-encoding';
    ngx.header.x_webp = 'generate';
    ngx.header.real_source_url = ngx.var.uri .. ".webp";
    ngx.header.content_type = "image/webp";
    return ngx.exec(ngx.var.uri .. ".webp");
  else
    ngx.exit(500);
    return;
  end
else
  ngx.header.vary = 'accept, accept-encoding';
  ngx.header.x_webp = 'read';
  ngx.header.real_source_url = ngx.var.uri .. ".webp";
  ngx.header.content_type = "image/webp";
  return ngx.exec(ngx.var.uri .. ".webp");
end
テスト#
WebP をサポートしていない IE と WebP をサポートしている Chrome で同じ画像のアドレスを開くと、サーバーから返されるレスポンスの内容が異なることがわかります。
WebP をサポートしている Chrome のレスポンスの内容は WebP 形式の画像です:
一方、WebP をサポートしていない IE のレスポンスの内容は通常の jpg 画像です: