расставляй правильно приоритеты и не отвлекайся на мелочи

Сжатие gzip для JavaScript и CSS

В последние время многие озаботились оптимизацией и убыстрением загрузки веб страниц, и это правильно. Аппетиты растут, но всему есть предел, уже никого, я думаю, не волнует вопрос: «Жать или не жать?», ответ однозначен — «Сжимать необходимо».

Я уже писал про оптимизацию и сжатие CSS и JavaScript, где вскольз упоминал про возможность сжатия на лету через поддержку gzip со стороны сервера и клиента. Вот про эту тему поговорим сегодня более подробнее.

Все современные браузеры, включая даже самый «любимый» IE6, могут работать с gzip содержимым. Когда Вы посылаете запрос на отображение веб страницы, браузер говорит серверу, что он может принимать сжатое содержимое. В заголовке запроса это выглядит как: Accept-Encoding: gzip,deflate.
В ответ, сервер может выдавать сжатое содержимое текстовых данных, которыми являются HTML страницы (в том числе и сгенерированные динамически), CSS файлы, файлы JavaScript и т.п.

Веб сервер, а мы будем говорить про Apache, должен уметь делать gzip сжатие и определять, для какого отправляемого содержимого он будет это делать.

Вообще странно, что в Интернет полно советов по использованию gzip сжатия через какие-то «магические» действия, с применением Rewrite Engine и скриптов обработки, генерящих сжатое содержимое CSS и JavaScript, мне несовсем понятны подобные действия (за исключенем, пожалуй, каких-то ограничений хостинг провайдера) по излишней изобретательности и лишней нагрузки на сервер, когда сам Apache специально заточен под работу с gzip.

В Apache, отправкой сжатого содержимого занимается модуль mod_deflate, который может быть подключен как в Windows (LoadModule deflate_module modules/mod_deflate.so), так и, естественно, скомпилирован в Linux (—enable-deflate).

А вот определением того, что жать, а что нет, должны заниматься правила фильтрации сжимаемого содержимого по запросу клиентского браузера или его MIME типу. Более доступно, это можно описать следующим правилом: Если получаемый от клиента запрос содержит разрешение на сжатие — Accept-Encoding: gzip,deflate или отправляемое содержимое несет в себе лишь текстовую информацию, то отправляемый контент однозначно необходимо жать. До Apache версии 2.1 этим занималось семейство директив AddOutputFilter, а с версии 2.1 предпочтение отдается более гибкому модулю mod_filter. Вот его мы и рассмотрим.

Как пример, один из вариантов, когда мы хотим сжимать отправляемое (resp=) содержимое, MIME тип которого (Content-Type) содержит в себе слова text или javascript, выглядит вот так:

FilterDeclare gzip CONTENT_SET
FilterProvider gzip deflate resp=Content-Type $text/
FilterProvider gzip deflate resp=Content-Type $javascript
FilterChain gzip

Мне же по душе вариант, когда клентский браузер сам говорит (req=), что он может принимать сжатое содержимое (Accept-Encoding содержит слово gzip):

FilterDeclare gzip CONTENT_SET
FilterProvider gzip deflate req=Accept-Encoding $gzip
FilterChain gzip

Рассмотрим фильтр более подробно:
— Первая строка определяет произвольное имя фильтра (gzip) и его тип, т.е. с чем мы далее будем работать (CONTENT_SET).
— Вторая, определяет обработчика того, с чем мы работаем по заданному условию. В нашем случае обработчиком будет deflate, вызываемый в том случае, если в заголовке запроса (req=), в параметре Accept-Encoding присутствует слово gzip.
— И третьей строкой мы запускаем в действие составленный нами фильтр с именем gzip в любом из контекстов сервера.

По моему все просто и доступно (контекст директив доступен через .htaccess). А теперь результат:
Как пример: размер packed версии jQuery 1.2.3, равный 29848 байт, после обработки gzip составил меньше 15K (14833 байта, можно сделать его еще меньше, до 14652 байт, отрезав комментарии).

Как я говорил, такая схема работает на ура даже с IE6, в чем вы можете убедиться просмотрев заголовки запросов и ответов через ieHTTPHeaders

[08.04.2008] Обновлено:

Прошел месяц, пора подводить итоги. Что-ж, результат замечательный, сжатие работает на ура и выигрыш в его использовании весьма ощутим, особенно на медленных дайловых соединениях. Все это дело тестировалось на сервере с четырьмя десятками разношерстных веб ресурсов. Специально обзванивал клиентов работающих через прокси сервера и друзей увлекающихся серфингом с различными веб браузерами. В итоге, с лучшей стороны себя зарекомендовало следующее звено фильтров, его я и рекомендую:

FilterDeclare gzip CONTENT_SET
FilterProvider gzip deflate resp=Content-Type $text/
FilterProvider gzip deflate resp=Content-Type $javascript
FilterProvider gzip inflate req=Accept-Encoding !$gzip
FilterChain gzip

Как видно добавилась еще одна полезная строчка контролирующая разрешение на сжатие со стороны клиента, т.е. если клиент не может разжимать (req=Accept-Encoding !$gzip), то отдаем ему несжатый контент (inflate).

И еще, насчет замечаний в комментариях про сложность проблемы и необходмиость поддержки древних (или странных) клиентов. Ситуация напоминает мне баянистое высказывание: «Он был настолько скуп, что лазил по порно сайтам с отключенными картинками» :), помоему проблема несколько преувеличена и скорее должна быть проблемой клиента, чем «плясками» на сервере, тем более странный контингент незначителен.

14 комментариев на статью:
  1. sillysunnybear:

    боюсь разочаровать, но проблема несколько более глубокая, чем заявлено. Предложенное решение описано здесь
    http://webo.in/articles/habrahabr/07-gzip-all/

  2. bridennis:

    Спасибо, протестирую, сделаю выводы.

    Хотя странно, если браузер сам говорит, что он может: Accept-Encoding: gzip,deflate, а сам не может выполнить корректную gzip обработку, это просто «замечательный» браузер какой-то…

  3. Anonymous:

    Обратный процесс как реализовать ?
    Вытаскиваю страницу сервером для дальнейшей её обработки, а она сжата «qzip». В итоге получить корректный код — не удаётся. «Донор» — не хочет реагировать на подмену Хедера, и постоянно выдаёт данные в зжатом виде. Как получить/распаковать эти данные на своем сервере ? (Очень нужно)
    (Apache + Perl)
    О процессах сжатия много чего написано, а вот о том, как распаковать на Apache….

  4. bridennis:

    Не совсем проникся Вашей задачей, но навскидку вижу два решения:
    -Либо регулировать клиентский запрос через Accept-Encoding, что-бы Apache возвращал inflate содержимое.
    -Либо на принимающей стороне скрипта делать gzinflate (к сожалению не знаю как это называется в Perl)

  5. Золотой:

    Попробовал последний вариант на своем сайте. Все работает. YSlow не ругается на несжатые js файлы. Спасибо.

  6. sammerset:

    А как вообще определить, используется сжатие на сервере уже или нет?

  7. sammerset:

    К примеру этот сайт? — http://www.secumart.com.ua

  8. Dmitriy:

    Хоть тема и старая, но не ответить не могу. Спасибо огромное автору за столь оригинальное решение, работает на УРА на всех браузерах (IE 5.0+, Opera 7+, FF2+, Chrome, Safari). Этот способ намного удобнее и легче, чем описанный на http://webo.in/articles/habrahabr/07-gzip-all/.

    2 sammerset. Проверить работу сжатия можно на сайте http://www.pipeboost.com/

  9. liveintv:

    Спасибо за описание ! А действительно как узнать есть ли сжатие на сервере?:

  10. 4idroid:

    Ну наконец то получилось!

  11. Иван:

    Даа, тут без ста грамм не разберешься. Придется пока заморозить эту проблему

  12. SEO admin:

    Благодаря автору смог включить у себя сжатие!
    Век бы не знал что это такое и как сделать.
    реально ускорилось благодаря сжатию !
    Рекомендую всем!

Ответить
Обязательные поля помечены *