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
Длинно:
Если вебмастер хочет экономить трафик свой и клиента, обычно он включает в своём апаче сжатие. То есть, берет mod_deflate или mod_gzip, и они сами всё за него делают: смотрят, умеет ли клиент gzip, если умеет, то сжимают данные и подставляют нужные заголовки. Все довольны, все смеются.
Но у такого подхода есть парочка минусов. Первый: сжатие производится динамически. То есть, каждый раз, когда сервер отдает, например, prototype.js, сжималка заново выполняет свою работу. Не то, чтобы это чрезмерно грузит сервер, но неаккуратненько как-то ©
Второй минус заключается в том, что в mod_deflate нельзя отключить режим передачи chunked (disclaimer: точнее, я пока что не нашел, как это сделать). Mod_gzip это как будто умеет, но он не так проверен временем. Чем грозен режим 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? Не, так мешать уровень протокола и уровень отображения я не согласен : ) Разбираться с заголовками — это работа скорее апача. Конкретнее, mod_rewrite. Он, правда, не слышал о HTTP_ACCEPT_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 ~ нормальная работа в том случае, если клиент туп; например, не видит он картинок, но сайтом пользоваться может, или нет у него яваскрипта, но это ему почти не мешает.