Отдаем модные форматы картинок в 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.avifadd_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
Так тестировать куда приятнее!
Комментарии: 8
Спасибо! Очень своевременная и важная инструкция.
Познавательно!
Спасибо! Как я понял, здесь указано как работать с {$image}. Не сочтите за наглость, а как правильно прописать конфигурацию конкретного хоста, при использовании {$thumb}? Возможна ли генерация изображений в формате avif?
В статье речь ни о image и ни о thumb, шаблонизатор и доступные плейсхолдеры тут ни при чем. Речь про обработку запросов изображений веб-сервером. Генерировать avif пока нельзя. Насколько знаю phpThumb его пока не поддерживает. Но самому сделать легко, куча сервисов и программ есть. Я их генерирую через nodejs и кладу рядом с оригиналом. То есть MODX не занимается у меня генерацией webp и avif от слова совсем. Все делает нода, формирует эти файлы всегда рядом с тем, что назагружали контент-менеджеры.
Интересно, а скрипт ноды для генерации изображений все время активен на сервере или запускается по расписанию?
Там какой то потоковый сборщик запущен с нужным пайпом или чисто JS скрипт с библиотекой?
Там какой то потоковый сборщик запущен с нужным пайпом или чисто JS скрипт с библиотекой?
Да он висит постоянно, как обычно делают во всяких сборках типа gulp, webpack когда работают с картинками вешают вотчеры, которые колдуют над ними.
Вот такая же фигня запущена на ноде на серваке, важно только там ноду под тем же пользователем запустить, что и весь сайт, а то проблемы с правами поползут.
Я использую для наблюдения — github.com/paulmillr/chokidar
А для генерации любых картинок с любыми настройками — github.com/GoogleChromeLabs/squoosh/tree/dev/cli
Это консольный аналог этого сервиса — squoosh.app/
Вот такая же фигня запущена на ноде на серваке, важно только там ноду под тем же пользователем запустить, что и весь сайт, а то проблемы с правами поползут.
Я использую для наблюдения — github.com/paulmillr/chokidar
А для генерации любых картинок с любыми настройками — github.com/GoogleChromeLabs/squoosh/tree/dev/cli
Это консольный аналог этого сервиса — squoosh.app/
а есть пример скрипта как пережимаются в другие форматы изображения?
Скоро опубликую статью про это, там покажу и скрипт и опишу процесс подробнее.
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.