gzip, статика и graceful degradation*

Придумал мазу. Если что, она из области «создать себе проблему и героически её забороть».

Кратко:

для приближения к нирване достаточно рядом с js|css контентом класть его .gz-копии и этот .htaccess

RewriteEngine on
RewriteRule ^(.*\.gz)$ $1 [L]
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteRule ^(.*)$ $1.gz
AddEncoding x-gzip .gz
AddType application/x-javascript .js
AddType text/css .css

Длинно:

Если вебмастер хочет экономить трафик свой и клиента, обычно он включает в своём апаче сжатие. То есть, берет moddeflate или modgzip, и они сами всё за него делают: смотрят, умеет ли клиент gzip, если умеет, то сжимают данные и подставляют нужные заголовки. Все довольны, все смеются.

Но у такого подхода есть парочка минусов. Первый: сжатие производится динамически. То есть, каждый раз, когда сервер отдает, например, prototype.js, сжималка заново выполняет свою работу. Не то, чтобы это чрезмерно грузит сервер, но неаккуратненько как-то ©

Второй минус заключается в том, что в moddeflate нельзя отключить режим передачи chunked (disclaimer: точнее, я пока что не нашел, как это сделать). Modgzip это как будто умеет, но он не так проверен временем. Чем грозен режим chunked? Да особенно ничем, кроме того, что всенародно нелюбимый браузер™ не всегда его понимает. Если протокол https, данные сжаты, а режим передачи chunked, IE может попытаться использовать файл, не разжимая его.

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

Натолкнула меня на идею вот эта статейка. Именно там я увидел фразу «Here since the Javascript is static, which is true for most AJAX applications these days, we can pre-compress which will reduce latency and CPU overhead.» То есть, мы радостно отдаём браузеру уже готовый .gz файл и идём готовить что-то изысканное из двух зайцев.

Но как быть с теми клиентами, которые не понимают сжатия? Естественно, для них нужен свой, несжатый файл, который нужно отдавать убогим. Только непонятно, когда который из двух файлов подключать. Смотреть в шаблоне на заголовки HTTP? Не, так мешать уровень протокола и уровень отображения я не согласен : ) Разбираться с заголовками — это работа скорее апача. Конкретнее, modrewrite. Он, правда, не слышал о HTTPACCEPT_ENCODING, зато спокойно скушает такую конструкцию: %{HTTP:Accept-Encoding}. Немного мучений, и имеем на выходе такую конструкцию для .htaccess:

RewriteEngine on
RewriteRule ^(.*\.gz)$ $1 [L]
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteRule ^(.*)$ $1.gz

Человеческим языком говоря: при запросе не .gz файла приписывать к имени запрашиваемого .gz, если клиент понимает gzip.

Тут встает другая проблема: апач честно говорит клиенту, что отдает не яваскрипт и даже не css, а гзип. IE, конечно, заголовки Content-Type игнорирует, но это не повод расслабляться. Кроме того, откуда браузеру знать, что отправленные ему байты перед употреблением нужно разжать? («Сжатый файл» и «сжато при передаче» — разные вещи все-таки.) Тут нас выручит mod_mime:

AddEncoding x-gzip .gz
AddType application/x-javascript .js
AddType text/css .css

Небольшое пояснение: если вы не знали, в представлении апача у файла может быть несколько расширений. Например, у prototype.js.gz их два: js и gz. Этим мы и пользуемся: если у файла есть расширение gz, то отправляем заголовок Content-Encoding: gzip. А две другие строчки нужны для того, чтобы перекрыть апачевское умолчание «отдавать для .gz файлов Content-Type: application/x-gzip». Мы укажем верный Content-Type.

Естественно, удобнее всего будет, если вы автоматизируете процесс создания .gz-копий. Заодно очень полезно склеивать все однотипные файлы в один (значительно уменьшается число запросов к серверу), и вырезать оттуда всё лишнее: пробелы, комментарии и т.п. Это несложно, jsmin и Vitamin вам в руки.

В результате всех мучений имеем в пять раз меньший объём css+js.

*graceful degradation ~ нормальная работа в том случае, если клиент туп; например, не видит он картинок, но сайтом пользоваться может, или нет у него яваскрипта, но это ему почти не мешает.

Артемий Трегубенко,
,