Внедряем webp без боли

Недавно начал работу над очередным проектом, и захотелось сразу добавить поддержку webp (раз такая поддержка уже есть в MODX из коробки). Задача несложная, но хотелось сделать все красиво, да так чтобы менеджеру не нужно было дополнительно эти изображения конвертировать.

По началу была выбрана тактика согласно ГОСТу, то есть просто использовать тег picture. Но уже на этом этапе уже начались проблемы:
  1. Верстка — в css были правила такого рода
    div>img {}
  2. Этот тег не поможет, если используется background-image и другие варианты использования изображений в css
  3. Отображение больших изображений в лайтбоксах (fotorama, fancybox ...)
Понимая что на этом проблемы не закончатся, решил поискать такое решение, которое позволит к примеру добавить ту же поддержку webp на уже существующих проектах, не боясь что поедет верстка.

Решение:

В качестве решения были использованы стандартные возможности веб-сервера (nginx или apache) для подмены изображений если у клиентского браузера есть такая поддержка.

Вариант для «контентных» изображений:

Все просто, при сохранении ресурса генерируем с помощью phpThumb нужные изображения с такими же названиями которые используются но в формате webp. Далее веб-сервер сделает все за нас по такой логике:
  1. проверяем если запрос идет на изображения jpg и png
  2. смотрим заголовки браузера на наличие accept: image/webp
  3. если все в порядке ищем рядом изображение с таким же названием только в расширением webp
  4. если изображение найдено, тогда добавляем заголовок Vary Accept
  5. отдаем вместо запрошенного изображения то что в формате webp
Пример конфига apache:
создаем файл .htaccess в папке со статикой (у меня это папка /static/ в корне)
<IfModule mod_rewrite.c>
     RewriteEngine On
     RewriteCond %{HTTP_ACCEPT} image/webp
     RewriteCond %{REQUEST_URI}  (.*)(\.(jpe?g|png))$
     RewriteCond %{DOCUMENT_ROOT}/%1\.webp -f
     RewriteRule .* %1\.webp [L,T=image/webp]
 </IfModule>
 <IfModule mod_headers.c>
     Header append Vary Accept env=REDIRECT_accept
 </IfModule>
 <IfModule mod_mime.c>
   AddType image/webp .webp
 </IfModule>

Пример конфига nginx:
аналогично с предыдущим примером (вся статика лежит в папке /static/ в корне)
location ~* ^(.*)(/static/.*)(jpg|jpeg|png)$  {
        set $webp  $1$2webp;
        set $rootFile "${document_root}${webp}";
        if ($http_accept ~* "webp"){set $test  A;}
        if (-f $rootFile) {set $test "${test}B";}
        if ($test = AB) {
                add_header Vary Accept;
                rewrite (.*) $webp break;
        }
}

Вариант для изображений minishop2:

Для начала нам нужно в источнике файлов minishop2 добавить генерацию изображений в этом формате таким образом.
Нужно определится с размерами которые нам нужны. В качестве примера у меня используется только 1 размер превью с таким конфигом
{"small":{"w":500,"h":500,"q":90,"zc":"1","bg":"000000"}}

я его дублирую, при этом к названию добавляю «webp» и указываю нужный тип файла для генерации, остальные параметры оставляю как есть, и у нас получается вот такой конфиг
{"small":{"w":500,"h":500,"q":90,"zc":"1","bg":"000000"},
"smallwebp":{"w":500,"h":500,"q":90,"zc":"1","bg":"000000","f":"webp"}}

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

Существующие изображения нужно будет перегенерировать, инструкции для этого можно прямо в сообществе найти. Просто воспользуйтесь поиском.

Конфиг для apache:
создаем файл .htaccess в папке /assets/images/ с таким содержимым:
<IfModule mod_headers.c>
    Header append Vary Accept env=REDIRECT_accept
</IfModule>
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{HTTP_ACCEPT} image/webp
    RewriteCond %{REQUEST_URI}  ^(.*)(/images/products/.*)(/.*)(/.*)(jpe?g|png)$
    RewriteCond %{DOCUMENT_ROOT}%1%2%3webp%4webp -f
    RewriteRule ^(.*)$ %1%2%3webp%4webp [L,T=image/webp]
</IfModule>
<IfModule mod_mime.c>
    AddType image/webp .webp
</IfModule>


Конфиг для nginx:
location ~* ^(.*)(/images/products/.*)(/.*)(/.*\.)(jpg|jpeg|png)$  {
        set $ext webp;
        set $webp  $1$2$3$ext$4$ext;
        set $rootFile "${document_root}${webp}";
        if ($http_accept ~* "webp"){set $test  A;}
        if (-f $rootFile) {set $test  "${test}B";}
        if ($test = AB) {
                add_header Vary Accept;
                rewrite (.*) $webp break;
        }
}

Примеров генерации превью в разных форматах при сохранении ресурса также можно найти. Но дополнительно скоро выйдет статья по организации рабочего пространства для менеджеров и там будет затронута тема работы с phpThumb в том числе для этих целей.
PG
PG
21 июня 2019, 02:15
modx.pro
24
12 769
+18
Поблагодарить автора Отправить деньги

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

Василий Наумкин
21 июня 2019, 06:21
+3
инструкции для этого можно прямо в сообществе найти.
Или даже в документации.
    PG
    PG
    22 июня 2019, 00:37
    0
    Спасибо, давно уже туда не заглядывал.
    Konstantin
    21 июня 2019, 07:02
    0
    Как я понял, это работает для {$thumb}, а если выводишь {$image} то нет?
      PG
      PG
      22 июня 2019, 00:36
      0
      Я об этом даже и не думал, просто не использую оригиналы изображения в выводе. Менеджеры часто грузят слишком большие изображения. Даже если учесть что при загрузке minishop2 может ужать до указанных размеров, я просто создаю для них отельные превью и ограничиваю их размер.
      Но к примеру можно написать плагин который будет проводить эту процедуру с оригинальными изображениями.
        Konstantin
        24 июня 2019, 07:37
        0
        Да это ладно, у меня почему-то не сгенерировались фото в webp, просто пустые директории

        Сначала думал не отображаются из-за формата, но нет, они просто отсутствуют :-)
          PG
          PG
          28 июня 2019, 16:53
          0
          причиной может быть отсутствие расширения webp в этих системных настройках 'upload_files','upload_images'
          подробнее тут
      Николай
      21 июня 2019, 08:06
      +1
      Мне очень понравился вот этот скрипт на jquery github.com/verlok/lazyload
      Вот пример:
      <picture>
      <source type="image/webp" media="(min-width: 1200px)" data-srcset="img/slide-dom1-500.webp">
      <source media="(min-width: 1200px)" data-srcset="img/slide-dom1-500.png">
      <source type="image/webp" data-srcset="img/slide-dom1-300.webp 1x, img/slide-dom1-788.webp 2x">
      <source data-srcset="img/slide-dom1-300.png 1x, img/slide-dom1-788.png 2x">
      <img alt="Двух этажный каркасный дом" class="lazy slide-dom1" data-src="img/slide-dom1-500.png">
      </picture>
      Этот пример был взят с моего сайта, в котором требовалось что бы под разное разрешения была разная картинка и плюс если человек заходит через смартфон с высоким разрешением, то отображается картинка более высокого разрешения :)
        Александр Мельник
        21 июня 2019, 12:50
        0
        Можно немного не в тему вопрос, но раз уж я увидел разговор об ленивой загрузке изображений и кого-то есть опыт работы с ней…
        Я не могу понять двух вещей.
        — так или иначе ленивая загрузка основана на передаче картинки через какой-то дата атрибут. Но разве уже отменили стандарт W3c и его требования к хтмл тегам? Разве может быть тег img без атрибута src ??? Это ведь ошибка.
        — разве будут поисковые системы индексировать изображения без src? Мне кажется, что с точки зрения поискового робота сайт вообще будет лишен изображений. Ведь выводом изображения теперь занимается JS скрипт, который существует только в браузере. А поисковый робот заходит не с браузера, а значит ничего кроме ошибок в стандарте он не увидит.
        Или я ошибаюсь?
          iWatchYouFromAfar
          21 июня 2019, 13:28
          +2
          В src пихают обычно прелоадер или плейсхолдер, а путь к изображению в дата data-src подхватывают и гуглбот и яндексбот.

          Например: yandex.ru/support/images/indexing.html
          В гугле можете проверить через search console.
            Александр Мельник
            21 июня 2019, 13:42
            +1
            Спасибо, я не знал о том, что яндекс индексирует изображение если оно задано через атрибут data-src. Это прямо новость, спасибо.
            Хотя на этой же странице есть довольна странная надпись.
            Фоновые изображения и картинки, раскрывающиеся при помощи скрипта, обычно не индексируются.
            И хотя все это конечно дикое нарушение стандарта.
            www.w3schools.com/tags/tag_img.asp
            The tag has two required attributes: src and alt.
            И даже если открыть спецификацию более современного тега picture там тоже указано что атрибут src у img обязателен
            www.w3schools.com/tags/tag_picture.asp
              iWatchYouFromAfar
              21 июня 2019, 14:21
              +1
              В data-src изображение есть по умолчанию, следовательно ничего туда через JS не раскрывается. Наоборот, этот атрибут удаляется (например в blazy плагине), а изображение подставляется в src. Но это если viewport дошел до картинки и она загрузилась, а так, все изображения боты будут кушать из дата атрибута.

              В src, как я уже сказал, подставляется заглушка (прелоадер), который удаляется как только изображение подгрузилось. Но вот как бот среагирует что во многих src стоит одна и та же заглушка, я не знаю. По сути это не должно ни на что влиять.
                Александр Мельник
                21 июня 2019, 16:36
                0
                Вы все говорите правильно. Это хорошо что боты научились кушать картинку из дата атрибутов. Этого я не знал и вы дали ссылку, спасибо.
                Но я говорил немного о другом. о том что такое применение тегов img противоречит стандарту.
                По крайней мере пример, указанный Николаем совершенно не содержит атрибута src.
                Да я понимаю, что этот атрибут потом появиться, но тут важно понимать, что js скрипт срабатывает и проставит атрибут только в браузере, то есть для пользователя все будет ок. Но поисковый бот не выполняет js скрипты, а значит он будет видеть тег img вот таким
                <img alt="Двух этажный каркасный дом" class="lazy slide-dom1" data-src="img/slide-dom1-500.png">
                Без атрибута src и соответственно посчитает это ошибкой.
                Но может мои знания уже устаревшие и поисковые системы не обращают внимание на ошибки в w3c.
                  Николай Савин
                  21 июня 2019, 20:39
                  0
                  Смотрите — здесь две стороны медали. Да безусловно — стандарты, есть стандарты и поисковые боты скорее всего все таки заметят несоответствие стандартом. Но само по себе это не является фактом ранжирования. Это может лишь служить сигналом об общем качестве сайта и проверке на другие HTML ошибки.

                  Но важно понимать и другую сторону. Изображения webp весят существенно меньше, а значит страница загружается гораздо быстрее. Ну а скорость загрузки страницы — очевидный фактор ранжирования.

                  Итого скорость загрузки и вес страницы кладут на обе лопатки отсутствие обязательных атрибутов.
                    Николай
                    21 июня 2019, 23:15
                    0
                    Плагин verlok'a позволяет использовать src, но для лучшего рейтинга в google предлагает использовать картинку низкого качества
                      iWatchYouFromAfar
                      22 июня 2019, 01:45
                      0
                      del
                        iWatchYouFromAfar
                        22 июня 2019, 01:46
                        0
                        для Александр Мельник

                        Пишу еще раз — поставьте заглушку в src, дабы не ругался валидатор. Я например по совету @Petr Golovatyy поставил svg-loader иконку. Контент не прыгает, а если и прыгает то 70% пользователей этого не заметят, другим на эту мелочь думаю будет пофиг. К тому же, это касается только контентных изображений.

                        P.S.
                        Вообще Петр прям вовремя написал этот пост, потому что вопрос с отдачей web/jpg картинок именно в контенте, вопрос довольно сложный для меня. Если для миниатюр можно заранее задать разметку picture и прописать все атрибуты, но с контентом так не получится. Ну и вариант Петра через конфиги просто идеален.
                      mngatoff
                      22 июня 2019, 13:51
                      0
                      И хотя все это конечно дикое нарушение стандарта.
                      стандарт говорит, что атрибуты должны БЫТЬ, а не быть ЗАПОЛНЕННЫМИ. к примеру, если ты не поставишь атрибут alt, валидатор ругнется. но если поставишь пустой alt —
                      <img src="" alt="">
                      — он ругаться не будет.

                      хотя может быть с src история другая — надо пробовать.
                Гущин Сергей
                Гущин Сергей
                21 июня 2019, 23:30
                0
                А вот этот момент "при сохранении ресурса генерируем с помощью phpThumb нужные изображения с такими же названиями " как вы реализовали?
                  PG
                  PG
                  22 июня 2019, 00:44
                  0
                  Вы вероятно не дочитали статью до конца. В скором времени планирую выделить на это отдельную статью
                  vectorserver
                  25 июня 2019, 12:39
                  0
                  Не проще поставить PageSpeed Module он все это умеет делать и даже конвертировать изображения в webp, если браузер поддерживает его!?
                  developers.google.com/speed/pagespeed/module/?hl=ru

                  Пример работы этого модуля: golodnaya-panda.ru/
                    Alex
                    25 июня 2019, 17:33
                    0
                    пардон, исправьте «не сложная» на слитно, так лучше))
                      Александр Мельник
                      26 июля 2019, 14:05
                      0
                      Поделюсь здесь вот такой ссылкой — гугл продумал ленивую загрузку, вшитую в движок браузера.
                      И кстати здесь все же в статье жирными буквами написано то, что я и говорил — поисковик не индексирует изображения, добавленные через скрипты ленивой загрузки.
                      ru.megaindex.com/blog/lazy-loading-seo
                        Димыч
                        26 июля 2019, 20:31
                        0
                        Lazy=loading еще не поддерживается ни одним браузером
                        То, что где-то кто-то что-то выделил жирными буквами, еще не значит, что так оно и есть.
                        И Гугл и Яндекс индексируют изображения с ленивой загрузкой. Это вижу как лично, так и саппорт поисковиков об этом пишет. В любом случае, нужно проверять конкретные случаи, т.к. технология (скрипты) применяются разные. Есть доп.инструменты, которые могут решить проблему и улучшить индексацию — гуглите «schema.org для картинок».
                          Александр Мельник
                          26 июля 2019, 21:42
                          0
                          Ответ принимается. Спасибо.
                          Ради интереса, можете дать ссылку на сайт с ленивой загрузкой картинок, ссылку на гугл фото из этого сайта и на скрипт, которым ленивая загрузка создана? Правда не иронизирую. Реально интересно.
                            Димыч
                            26 июля 2019, 22:53
                            0
                            Похоже, я был излишне оптимистичен по поводу нормальной индексации. Пробежался по своим сайтам — не всегда все фото индексируются.
                            Вот, например
                            graviruemvse.ru/laser/wood/
                            Гугл
                            Яндекс
                            Причем, Яндекс даже лучше с этим делом справляется.
                            Большие фото там по data-src="..." лежат, превьюшки под src
                            Попробую карту для фото делать.

                            Скрипт lazysizes
                              Александр Мельник
                              26 июля 2019, 23:09
                              0
                              Спасибо. Признаюсь что в данную минуту не буду просматривать ссылки, которые вы дали, но я рад что вы признали, что не все так «хорошо».
                              А теперь представьте, у меня есть заказчик, который торгует картинами. Сервис по продаже картин. Ориентирован не на Россию, то есть яндекс поиск нам глубоко пофиг.
                              Основной упор это гугл, площадки типа алиэксперсс, тао бао и так далее.
                              И будет ли работать ли тут «простая» ленивая загрузка через атрибут data и js скрипт?
                              Чем больше я вникаю в эту тему, тем чаще вижу ответ от специалистов, что нет — не будет. И это полностью соответствует моему пониманию этого процесса.
                                Александр Мельник
                                28 июля 2019, 10:28
                                0
                                а я не увидел на предложенном вами сайте graviruemvse.ru/laser/wood/ эффекта ленивой загрузки.
                                У меня загрузились все изображения сразу.
                          Юрий
                          29 декабря 2019, 21:41
                          0
                          предлагаю свой вариант ленивой загрузки с сохранением индекса, при использовании элемента
                          <picture>
                          https://github.com
                            Andrew
                            08 марта 2020, 05:34
                            0
                            создаем файл .htaccess в папке со статикой (у меня это папка /static/ в корне)
                            подскажите пожалуйста, а если у меня такой папки нету, где создавать файл .htaccess?
                              Andrew
                              08 марта 2020, 09:36
                              0
                              Так понимаю в папке /static/ должны лежать изображения +сгенерированные? Или как, что это за папка?)
                                Andrew
                                09 марта 2020, 08:34
                                0
                                Вариант для изображений minishop2 попробовал, не работает.
                                С этим кодом вообще перестаёт выводить изображение и едет корзина
                                location ~* ^(.*)(/images/products/.*)(/.*)(/.*\.)(jpg|jpeg|png)$  {
                                        set $ext webp;
                                        set $webp  $1$2$3$ext$4$ext;
                                        set $rootFile "${document_root}${webp}";
                                        if ($http_accept ~* "webp"){set $test  A;}
                                        if (-f $rootFile) {set $test  "${test}B";}
                                        if ($test = AB) {
                                                add_header Vary Accept;
                                                rewrite (.*) $webp break;
                                        }
                                }
                                И код для apache не срабатывает у меня, но хоть выдодится обычный формат изображений и не едет корзина).
                                У кого нибудь получилось внедрить webp без боли?)
                                  Сергей
                                  16 мая 2020, 11:25
                                  0
                                  Подскажите какой конфиг apatch для ms2gallery?
                                  Нужно ли что-то вносить, включать на сервере.
                                  Фото создались без проблем, но вот как активировать, что бы нужные выбирались не могу разобраться, сейчас просто банально .jpg используется и все.
                                    Андрей
                                    01 октября 2020, 21:09
                                    0
                                    Может кто поделиться вариантом реализации данного решения для контентных изображений?
                                    Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                                    34