最先端なAVIF画像メインなウェブサイトを作る with Nginx

絵を発表
©いらすとや.

この記事は、この記事に書かれている設定で動いているNginxが使われています。この記事はAVIF(AV1 Image File Format)画像対応ブラウザではAVIF画像が表示され、そうでないブラウザではおそらくWebP画像が表示されます。

WebP画像は従来のJpeg画像よりファイルサイズが大幅に小さくでき、透明色を使用可能で可逆/非可逆対応で使いやすい画像形式でしたが、AVIFは似たような特徴でWebP形式よりさらに画像サイズを小さくできることが期待されます。次世代画像形式の中では有望視されているようです。使わない手はないかなということで。

NginxでAVIF画像の対応 1
https://caniuse.com/?search=webpメジャーなブラウザでWebP画像への対応。
2019年に「最先端なWebPメインなウェブサイトを作る with Nginx」を書いた頃はAppleのSafariブラウザがWebPに対応していなくてウェブサイトでWebP画像を使うために苦労させられたが、2022年現在はメジャーなモダンブラウザは全て対応済み。(IEはもう亡き者という扱いで)

NginxでAVIF画像の対応 2
https://caniuse.com/?search=avifメジャーなブラウザでWebP画像への対応。
3年前のWebPのときと同じくまたしてもAppleのSafariがAVIF非対応。ほんとクソ。何故かChromiumベースのEdgeが非対応なのは気になるが。

状況としてはデジャブかよというくらい2019年にウェブサイトでWebP画像を主に表示しようとしていた当時とそっくり。
最先端なWebPメインなウェブサイトを作る with Nginx」の方法は汎用性が高いのでほぼそのまま別の新しい画像形式にも対応できる。無い知恵絞ってがんばって作ったかいがあったというもの。
なお、「がとらぼ」の中の人はpictureタグとsourceタグで複数のファイルを書き連ねるのが好きではないので苦労しているが、pictureタグを使うのに抵抗がなければこの記事のような苦労はしなくて済む。

/usr/local/etc/nginx/mime.types (6行目: 1行追加)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
types {

    中略
    image/gif                        gif;
    image/jpeg                       jpeg jpg;
    image/avif                       avif;
    image/png                        png;
    image/tiff                       tif tiff;
    image/webp                       webp;
    中略
}

設定ファイルのPathやファイルの構成は違うかもしれない。(上はFreeBSDのPKG/PortsでNginxをインストールした場合を想定)

Nginxの仮想ホスト用設定ファイル等に追加 (7〜10行目, 25〜34行目)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#WebP用
map $http_accept $webp {
        default   "";
        "~*webp"  $uri;
}
#AVIF用
map $http_accept $avif {
    default   "";
    "~*avif"  $uri;
}
server {
    listen 192.168.0.1:443 ssl http2;
    server_name             hoge.example.com;

    中略
    #WebP用
    location ~ (.+)\.webp$ {
        set $wo_ext $1;
        set $jpg "${wo_ext}.jpg";
        set $png "${wo_ext}.png";
        more_set_headers 'Vary: Accept-Encoding';
        try_files $webp $jpg $png $uri =404;
        expires max;
    }
    #AVIF用
    location ~ (.+)\.avif$ {
        set $wo_ext $1;
        set $webp "${wo_ext}.webp";
        set $jpg  "${wo_ext}.jpg";
        set $png  "${wo_ext}.png";
        more_set_headers 'Vary: Accept-Encoding';
        try_files $avif $webp $jpg $png $uri =404;
        expires max;
    }
    中略
}

WebP用設定を書いているのであればほぼコピペで増殖させるだけ。WebP画像を使わないのであればWebP用の設定は無しで可。というか、WebP対応ブラウザが殆どの現状ではWebP用の設定はむしろ外した方が良いと思う。

仕組み
7〜10行目でmapを使い、変数$avifに "" (空)をセット、avif対応ブラウザであれば$avifにURI (ここではavif画像のURI)をセットする。
25〜34行目はURIの末尾がavifのとき、つまりURIがavif画像のときに、
まず、変数$wo_ext (without extensionのつもり)にはURIの拡張子の前の部分をセットする。(26行目のワイルドカードと27行目)
これで仮に /path/hoge/example.avif というURIなら$wo_extには /path/hoge/example がセットされる。
28行目、変数$webpには、$wo_ext + ".webp"を入れるので /path/hoge/example.webp がセットされることになる。
29行目、変数$jpgには、$wo_ext + ".jpg"を入れるので /path/hoge/example.jpg がセットされることになる。
30行目、変数$pngには、$wo_ext + ".png"を入れるので /path/hoge/example.png がセットされることになる。
31行目、HTTPレスポンスで'Vary: Accept-Encoding'を通知し、ブラウザに「リクエストされたもの(avif画像とは違うもの)を送信するかも」と伝える。
32行目、try_filesは左から順に試して存在すれば送信する、送信できればそこで終わりことになる。つまり$avifに格納されたURIのファイルが存在すればそれを送信する。それが無ければ$webpに格納されたURIのファイルを送信する。それが無ければ$jpgに格納されたURIのファイルを送信する。それが無ければ$pngに格納されたURIのファイルを送信する。それすら無ければerror 404を返す。
このとき、AVIF画像対応ブラウザであれば$avifにAVIF画像のURIが格納されているので送信第一候補になる。画像ファイルがあればそれが送られるということになる。
AVIF非対応ブラウザであれば$avifは "" (空)なので、つまり32行目は try_files $webp $jpg $png $uri =404;と同等ということになる。つまりAVIF画像のURIは送信候補にならないのでWebP画像、Jpeg画像、PNG画像の順で送信を行おうとすることになる。

この方法はWebP画像の対応のときに無い知恵を絞って作って公開したものの他所のサイトで採用される気配がなさそうなので何か問題ありなのかしら?「がとらぼ」では意図したとおりに機能しているように見えるんだけどなぁ。

これでウェブページに <img src="/hoge/hoge.avif" width="nnn" height="nnn" alt="hoge" /> と書いて、hoge.avif, hoge.webp, hoge.jpg を置く。(拡張子の前は同じ文字列にする 重要)
これで、AVIF対応ブラウザでhoge.avif画像にアクセスが来ればhoge.avifが渡されてhoge.avifというAVIFが表示される。
AVIF非対応でWebP対応のブラウザがhoge.avif画像にアクセスが来ればhoge.webpがhoge.avifという名前で渡されるが正常にWebP画像が表示される。
このときにhoge.webpファイルが無い場合はその次の候補のhoge.jpgがhoge.avifというファイル名で渡されるが正常にJpegファイルが表示される。hoge.jpgファイルも存在しない場合はhoge.pngファイルがhoge.avifというファイル名で渡されるが正常にPNGファイルが表示される。
この例ではAVIFファイルの代替として第一候補がWebP画像になっているが、もしもWebP画像非対応のブラウザからのアクセスを重要視するのであれば28行目は外した方が無難。28行目を外した場合はAVIF非対応ブラウザがアクセスした場合には第一候補としてhoge.jpgがhoge.avifというファイル名で渡されるがJpegファイルが正常に表示される。hoge.jpgが用意されていない場合はhoge.pngがhoge.avifというファイル名で渡されるが正常にPNGファイルが表示される。

NginxでAVIF画像の対応 5
AVIF非対応のEdgeブラウザでこの記事を表示してみた。AVIF画像対応のブラウザならそのままAVIF画像が表示されるがEdgeブラウザでは本来はAVIF画像は表示されない筈。しかし、AVIFの代替でWebP画像が行われるようになっているので画像が表示されている。ファイル名の拡張子はavifだがWebPが表示されている状態。なお、DevToolsで画像の形式の種類の表示はできなかった、残念。

AVIF画像の作成

PhotoshopやGIMPの新しいバージョンはAVIFに対応しているので特に困ることはないかと。
ちょっとした画像を作るためだけに高価なPhotoshopを買うのは馬鹿らしいと思う方は無料で使えるGIMPをどうぞ

NginxでAVIF画像の対応 3
GIMPは2.10.18以降のバージョンでAVIF画像に対応している。WindowsならGIMPの最新版を使えばAVIFに対応すると思われる。Linuxでは標準のリポジトリでは安定版のGIMPが入るのでAVIF非対応のGIMPかもしれない。最新版のGIMPをインストールするためにGIMPのリポジトリを追加すかるFlatpakで最新のGIMPのパッケージをインストールする。上の画像のGIMP 2.10.30は余裕でAVIF画像に対応している。

NginxでAVIF画像の対応 4
画像の「エクスポート」で保存する画像ファイルのファイル名の拡張子を.avifにするか画像の種類をAVIFにすることでAVIF画像を保存できる。画像の品質などを指定できる。ほぼロスレスで保存する場合はNearly losslessにチェックしてエクスポートする。