Сегодня я расскажу вам, как можно очень сильно ускорить загрузку страниц с большим количеством фотографий (каких довольно много в этом блоге). Секрет прост — WebP. Всё дело в правильной настройке сервера.
Я буду рассказывать на примере Hakyll и nginx, но принцип легко перенести и на другие технологии.
Браузер, когда запрашивает с сервера картинку, ничего не знает о её формате. Не важно, что запрашивается, например, http://example.com/image.png
. Такое понятие, как расширение файла, неприменимо в браузере. Оно скорее оставлено для удобства разработки и соответствия файловой системе. Лишь получив от сервера ответ, в заголовке которого будет написано: Content-Type: image/png
, браузер может действительно сказать, что пришла картинка в формате PNG и отобразить её. Конечно, в браузерах предусмотрена защита от ошибок, и зачастую браузер угадывает формат ответа, даже если он был сообщён сервером неверно, но факта отсутствия расширений в URL это не отменяет. А значит, мы можем подсовывать WebP-картинку, которая при сходном качестве гораздо лучше сжата, чем PNG или JPG.
Вначале нужно картинки в WebP сконвертировать. Можно, конечно, делать это на лету, но зачем, ведь операция одноразовая и больше не потребуется. Особенно хорошо тем, у кого сайт создан на основе статического генератора, тогда можно добавить немного правил, которые будут создавать соответствующие WebP-файлы автоматически.
Для конвертации картинок в WebP можно воспользоваться утилитой, предоставляемой Google. Использовать её нужно примерно так:
cwebp image.jpg -o image.jpg.webp
cwebp -lossless image.png -o image.png.webp
Добавляем генерацию WebP файлов в Hakyll:
webpConverterRules :: Rules ()
webpConverterRules = do
match "images/**.jpg" $ version "webp" $ do
route (setExtension "jpg.webp")
compile $ do
filePath <- getResourceFilePath
res <- unixFilterLBS "cwebp" [filePath, "-mt", "-o", "-"] ""
makeItem res
match "images/**.png" $ version "webp" $ do
route (setExtension "png.webp")
compile $ do
filePath <- getResourceFilePath
res <- unixFilterLBS "cwebp" [filePath, "-mt", "-lossless", "-q", "100", "-o", "-"] ""
makeItem res
Тут всё просто. Стоит обратить внимание на функцию version
, которая позволяет создавать несколько выходных файлов на основе одного входного. Чтобы на сайт попал оригинал изображения, у меня в коде уже давно живёт такая функция:
staticFilesRules :: Rules ()
staticFilesRules = do
match "images/**" $ do
route idRoute
compile copyFileCompiler
Никакой магии.
Переходим ко второй части: настройка сервера. Совсем недавно я использовал самописный сервер на основе Snap Framework. Но от его использования пришлось отказаться из-за крайне неудовлетворительной и негибкой поддержки SSL и TLS. Новый сервер — стандартный nginx, его и будем настраивать.
Во-первых, добавляем новый MIME-тип для WebP-файлов в секцию types
:
image/webp webp;
Во-вторых, создаём инструкцию map
, которая будет устанавливать значение переменной $webp_suffix
в зависимости от заголовка HTTP-запроса Accept
:
map $http_accept $webp_suffix {
default "";
"~*webp" ".webp";
}
Если в заголовке есть строка webp
, то этот формат картинок поддерживается, иначе — нет.
Ну и в-третьих, для картинок пишем конструкцию, которая будет подбирать различные варианты подходящих файлов:
location ~* ^/images/.+\.(png|jpg)$ {
root /home/www-data;
add_header Vary Accept;
try_files $uri$webp_suffix $uri =404;
}
Т. е. сначала на диске будет искаться файл с суффиксом $webp_suffix
, затем без него. Ну а если и вторая попытка не удалась, то вернётся ошибка 404. Обратите внимание, что если WebP не поддерживается, то $webp_suffix
будет равен пустой строке и обе попытки найти подходящий файл будут искать один и тот же файл.
Подробнее о настройке nginx можно прочитать тут.
Вот и всё. Объём отдаваемых данных для поста с фотографиями значительно уменьшился при сходном качестве картинки (png вообще кодируется без потерь). Например, один из последних постов — Гамарджоба, Грузия: часть вторая — имеет размер 13,3 Мб. В случае со включённой оптимизацией этот размер уменьшается до 4,9 Мб.
60% посетителей моего блога используют Google Chrome, а значит, их затронет эта оптимизация. Да и процент пользователей этого браузера в мире один из самых больших, так что выгода очевидна.
Хочется что-то добавить или сказать? Я всегда рад обсудить. Пишите на me@dikmax.name.