最先端な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にチェックしてエクスポートする。

文字コードポイントシフトはGoogle検索さんと相性が悪いの?

疑問
©いらすとや.

先日、文字コードシフト式広告ブロックでGoogleさんのインデックスが文字化けする問題で追加のテスト記事を掲載した。この記事ではアンチ広告ブロックの仕組みは外して文字コードシフトだけが機能するようになっている。
そして、Googleさんにその記事がインデックス登録されたようなので確認した。

コードシフトとGoogleインデックス 1
まず、Search ConsoleのURL検査ツールで「ライブテスト」を確認。そのスクリーンショットを確認すると記事本文は正常にレンダリングされている。つまり、文字コードシフトの仕組みが機能していても意図したとおりに表示されている。

コードシフトとGoogleインデックス 2
Googleさんのインデックスにこの記事が登録されたことが示されている。

コードシフトとGoogleインデックス 3
Googleさんでこのページを検索してみた。たしかに登録されていた。
しかし、本文の抜粋部分は文字化けしている。

コードシフトとGoogleインデックス 4
このGoogleさんのインデックスのキャッシュ状態を確認してみた。キャッシュの「フルバージョン」では記事の本文部分は文字化けしていない。つまり、文字コードシフトの仕組みが正しく機能して意図したとおりに表示されている。

コードシフトとGoogleインデックス 5
同じくGoogleさんのインデックスのキャッシュだが、「テキストのみのバージョン」
こちらは記事の本文部分が文字化けしている。つまりコードシフトの戻しができていない。これがインデックスに登録されているのかしら?

Googleさんがインデックスを作成するとき、インデックス用に内部的に持つ何かしらのデータでは文字シフトを正常に行っている場合を考える。つまり、検索結果に文字化けした本文を表示しても、内部的には文字化けしていない本文を持っている可能性を仮定した。

コードシフトとGoogleインデックス 6
テスト用の記事の中でキーワードになりそうな文字列をGoogleさんで検索してみた。今回は「ページ表示後の嫌がらせ」を使ってみた。他所のサイトがヒットすると邪魔なので「がとらぼ」というキーワードも組み合わせてみた。
「ページ表示後の嫌がらせ」というキーワードそのものを含むページはヒットしなかった。

コードシフトとGoogleインデックス 7
「ページ表示後の嫌がらせ」を文字コードポイントシフトした「ベ・シ衧礹律ね嫋かよず」を検索してみた。
こちらは見事にヒットした。

Googleさんはページをレンダリングした状態で表示されるコンテンツからインデックスしているのではなく、HTMLソースからインデックスしているというのが正しいみたい。
つまり、文字コードポイントをシフトしたコンテンツはGoogleさんと相性が悪いといえそう。

2022年8月11日追記:
3年前にGoogleさんが公表した方針?として、Javascriptを使って表示するページは正常に処理されない可能性があるので クローラー向けにはダイナミックレンダリングによりレンダリングしてコンテンツを渡すような処理を勧めていました。これが最近の変更により「ダイナミックレンダリングは回避策の1つではあるけど推奨される解決策ではない」になりました。ダイナミックレンダリングを使おうから方針が大幅に修正されたことになります。Google検索セントラル「ダイナミックレンダリングを実装する」
これによると、検索エンジンのクローラーに対しては普通の閲覧者向けとは別の静的HTMLなどのコンテンツを出し分けるのが良いとのこと。とはいえ、クローキング扱いされると面倒なことになるのでGoogleさんの指示に従い適切に出力するよう十分に注意が必要です。
Google検索セントラル「動的な配信」このドキュメントから適切な出力を行う方法を探すと現在もダイナミックレンダリングに誘導されるのでこの辺り早急に関連するドキュメントを更新してくれないと困る。

Googleの検索用クローラーはChromeベースなのである程度はJavascriptを使ったコンテンツを処理できると聞いていたがぶいぶ違う雰囲気?

Up