Отдаем модные форматы картинок в webp и avif напрямую через nginx в обход разметки



Всем привет!

Я тут работал над одним проектом, в котором очень много контентной и интерфейсной графики, десятки тысяч изображений и, конечно, возник вопрос оптимизации сайта, чтобы удовлетворить требования поисковых систем.

Энтузиазма добавили, появившиеся относительно недавно у гугла, так называемые Core Web Vitals.
Кто не в курсе это пачка технических показателей качества сайта, которые скоро будут включены в алгоритм ранжирования и все тормознутые сайты из-за них, типа, покатятся вниз.
Ну в общем, встала задача оптимизировать картинки, а также сделать так, чтобы не пришлось переписывать кучу html кода, чтобы эти картинки туда вставить.



Как это сегодня делается в HTML:


Тут ничего сверхъестественного, просто кладем пачку картинок по нужному адресу, прописываем все варианты изображений в разных форматах и браузер сам выберет, что ему по силам.

Отличный вариант, с прогрессивным подходом — но как только дело доходит до управления такими картинками из админки, начинаются танцы с бубном, большинство дополнений как и штатные тв-поля с типом изображение дают выбирать одно изображение. И получается, чтобы указать все 3 варианта, нужно либо делать 3 тв-шки, либо мутить в разметке что-то подобное с одной тв-шкой



Я тут показал очень простой вариант, предполагающий, что картинка будет точно в .jpg, а ведь могут загрузить и png и jpeg и.т.д И код выше превращается в мешанину регулярок, а когда картинок много — получается кромешный ад.

Потом приходит в голову вынести эту логику в сниппет, чтобы он занимался всем этим, и потом использовать его как модификатор.

Но на этом моменте я уже не выдержал)

Итак решаем задачу «по умному», но сразу скажу, что этот вариант подойдет только для тех, кто имеет доступ к конфигурации web-сервера nginx, то есть большинство шаред-хостингов тут сразу пролетают, так как у них вечно все гайки закручены и раскрутке не подлежат.

Итак шаг первый:

Сначала надо убедиться, что наш веб-сервер способен понимать файлы с расширением .avif и .webp

Обычно в основном конфиге nginx есть директива include которая подключает файлик содержащий все типы файлов с которыми может работать веб-сервер. Как правило он называется mime.types

Проверяем в нем наличие наших форматов картинок, если их нет добавляем



Шаг второй

Теперь надо добавить проверку заголовка поддержки форматов от браузера, он называется accept и выглядит вот так


Список поддерживаемых форматов будет зависеть от браузера, в данном случае это хром
Проверять этот заголовок мы будем также в основном конфиге в контексте http {}
Добавляем туда



Этот кусок конфига создаст 2 переменные $webp_suffix и $avif_suffix, они будут содержать расширение файла, если браузер его поддерживает или пустую строку если нет.

Шаг третий

Теперь нужно задействовать наши переменные и делать мы это будем уже в конфиге конкретного хоста в контексте server{}



Добавляем нужный локейшн с регуляркой, где фильтруем только картинки в формате jpg и png и дальше начинаем «клеить строки».
set $base $1;
Сначала создается переменная $base которая будет содержать путь до файла без расширения
Пример: /assets/images/test

Далее создаем ещё одну переменную $webp_uri которая формируется из склейки предыдущей переменной + переменной, которую мы делали на прошлом шаге — $webp_suffix
set $webp_uri $base$webp_suffix;
Это даст нам строку: /assets/images/test.webp

Далее ещё одну переменную по такому же принципу, но приклеиваем другую переменную — $avif_suffix
set $avif_uri $base$avif_suffix;
Это даст нам строку: /assets/images/test.avif

add_header X-uri "$base $webp_uri $avif_uri";
Дальше идет тестовый заголовок, я его добавил вместо того, чтобы выводить инфу о переменных в лог файл, удобно для быстрого тестирования. Этот заголовок выведет всё, что содержится в предыдущих переменных. Его потом нужно будет удалить, когда всё проверите.

add_header Vary Accept;
Следом ещё один заголовок — он для кэширования на прокси-серверах, его здесь не буду разбирать.

Ну а дальше самая главная строка:

try_files $avif_uri $webp_uri $uri @rewrite;
Директива try_files проверяет доступность адресов по порядку и мы ей говорим следующее:
Попробуй найти файл с расширением avif, если такого нет попробуй найти webp, если и такого нет, попробуй обычный формат jpg, png если и их нет отдается обработка в php, но можно и вернуть 404 ошибку, тут кто как настраивает логику работы.

После всего этого, нужно перезапустить nginx, чтобы новые конфиги вступили в силу
И переходим к тестам:
Я сделал фотки во всех нужных форматах



Проверяем, что там нам отдаст браузер


Обратите внимание — слева видно, что запрашивался файл png, но сервер нашел вариант получше и вернул avif

Если я удаляю физически картинку в формате avif, получаем следующее


Теперь получили webp, если и его удалить, то получим изначальную png-шку

Ну и самое главное, теперь в разметке достаточно старого доброго img



Менеджеры в админке выбирают одну картинку, а сервер решает какую сможет показать браузер посетителя.
Вот, собственно, и всё! А как Вы решаете проблемы с картинками, пишите в комментах, буду рад конструктивной критике, вопросам и.т.д

А да, совсем, забыл, чтобы постоянно не удалять или перемещать файлы для тестирования, что там отдаст браузер в итоге, можно использовать удобные инструменты отладки во всех хромоподобных браузерах



Просто ставим галку отключения avif — и браузер вдруг разучивается понимать avif картинки, тоже самое с webp
Так тестировать куда приятнее!
Семён Кудрявцев
26 апреля 2021, 23:44
modx.pro
17
3 208
+21

Комментарии: 8

Антон Тарасов
27 апреля 2021, 08:12
+1
Спасибо! Очень своевременная и важная инструкция.
    Николай Савин
    27 апреля 2021, 08:57
    0
    Познавательно!
      Игорь
      27 апреля 2021, 21:54
      0
      Спасибо! Как я понял, здесь указано как работать с {$image}. Не сочтите за наглость, а как правильно прописать конфигурацию конкретного хоста, при использовании {$thumb}? Возможна ли генерация изображений в формате avif?
        Семён Кудрявцев
        27 апреля 2021, 22:20
        +2
        В статье речь ни о image и ни о thumb, шаблонизатор и доступные плейсхолдеры тут ни при чем. Речь про обработку запросов изображений веб-сервером. Генерировать avif пока нельзя. Насколько знаю phpThumb его пока не поддерживает. Но самому сделать легко, куча сервисов и программ есть. Я их генерирую через nodejs и кладу рядом с оригиналом. То есть MODX не занимается у меня генерацией webp и avif от слова совсем. Все делает нода, формирует эти файлы всегда рядом с тем, что назагружали контент-менеджеры.
          Николай Савин
          28 апреля 2021, 08:41
          0
          Интересно, а скрипт ноды для генерации изображений все время активен на сервере или запускается по расписанию?
          Там какой то потоковый сборщик запущен с нужным пайпом или чисто JS скрипт с библиотекой?
            Семён Кудрявцев
            28 апреля 2021, 09:29
            1
            +4
            Да он висит постоянно, как обычно делают во всяких сборках типа gulp, webpack когда работают с картинками вешают вотчеры, которые колдуют над ними.
            Вот такая же фигня запущена на ноде на серваке, важно только там ноду под тем же пользователем запустить, что и весь сайт, а то проблемы с правами поползут.
            Я использую для наблюдения — github.com/paulmillr/chokidar
            А для генерации любых картинок с любыми настройками — github.com/GoogleChromeLabs/squoosh/tree/dev/cli
            Это консольный аналог этого сервиса — squoosh.app/
              Алексей
              05 мая 2021, 17:36
              0
              а есть пример скрипта как пережимаются в другие форматы изображения?
                Семён Кудрявцев
                05 мая 2021, 18:02
                +1
                Скоро опубликую статью про это, там покажу и скрипт и опишу процесс подробнее.
        Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
        8