Наткнулся на кучу интересных статей на webo.in и зачитался. Решил применить описанное там на реальном проекте. Вот что получилось. Проектик маленький — сайт моих друзей Bookcare. Они делают обложки для книг, а их сайт — мой «проект выходного дня».
Теперь по сути:
Основная часть контента — это изображения обложек, которые хранятся в формате jpeg. Значит действия над этой частью оптимизации сводятся к оптимизации самих картинок и настройке кеширования.
Для оптимизации картинок я использовал jpegtran, однако проблема с ним была в том, что он не может обработать сразу всю папку — только по одному файлу. Другая проблема, что использовать входной файл в качестве выходного (просто перезаписать оптимизированный файл в старый) невозможно.
Я решил эту проблему небольшим скриптиком и, хотя я не очень силён в шелл-скриптах, он работает и достаточно удобно производить замены. Итак, я создал в домашней папке скрипт jp.sh:
А затем выполнял простую gоследовательность:
После этого в папке с картинками уже лежат оптимизированные джепеги, и создаётся новая папка raw/, содержащая исходные файлы. Папочка raw/ очень пригодилась, когда оказалось, что скриптик мой не отрабатывал на файлах с именами, содержамищи не латиницу (файлы просто обнулялись). Сейчас всё работает.
Итак, после того, как картинки удобным образом оптимизированы, надо было настроить кеширование. Я добавил выдачу Etag'ов. Это просто и описано на webo.in. Делается добавлением одной строки в htaccess
Вообще-то надо еще настроить выдачу заголовков Expires, но это тоже делается несложно.
После разборок с каринками, они стали весить на 34% меньше, что на странице Каталога ускорило загрузку почти на столько же, так как основной контент — это именно картинки.
Далее я прошёлся по хтмльной выдаче. Вёрстка не моя, по этому, я пока не полез в оптимизацию разметки, но базовые фичи сделал: убрал переносы строк, табы и повторяющиеся пробелы.
Далее интересней. Захотелось, чтобы все (даже пхпшные) странички выдавали LastModified и правильно откликались на If-Modified-Since.
Взглянул на главную страницу и понял, что там выводятся только новости и последние поступления (из динамического контента). Соответственно получил дату последней новости, дату последнего поступления, получил максимальную и обработал следующим образом:
Надо отметить, что время в Last-Modified надо указывать всегда в GMT, то есть по гринвичу.
Теперь страничка загружается один раз, а далее мы видим ответ 304 Not Modified;
Еще одно важное замечание — заголовок Last-Modified надо выдавать всегда, иначе страница будет загружаться через раз, так как в ответе 304 не было указано Last-Modified. Такое поведение было замечено за FF3.
Затем добрался до выдачи css в виде архивов. Тут меня не устроило решение, которое было дано на webo.in, так как на серваке bookcare.ru нет mod_headers. Я использовал достаточно спорный метод, но в рамках данной задачи, я считаю, вполне законный:
Соответственно, например main.css у нас изначально лежит в виде архива, а main_css.nozip — это обычная версия файла.
Если эти строки лежат в .htaccess в отдельной папке, то никак не влияют на файлы вне этой папки, так что в таком виде этот приём только полезен. При минимальных действиях на сервере получается то, что должно получаться.
Вот, наверное всё, если я чего-нибудь не запамятовал.
UPD1: Всё-таки запамятовал. После вырезания всех лишних символов из хтмл-выдачи отдаётся она в gzipованом виде с коэффициентом сжатия 9.
UPD2: Переписал шелловый скрипт. Теперь всё работает хорошо и с любыми именами
UPD3: Обнаружилась проблема с выдачей css в виде архивов. Исправил код .htaccess
UPD4: По совету юзера Roxis публикую код на пхп, который выбаёт правильные заголовки без привязки к mod_php
P.S. Прошу сильно не пинать, это мои первые шаги по мосту клиентской оптимизации. Но за замечания буду только благодарен.
Теперь по сути:
Основная часть контента — это изображения обложек, которые хранятся в формате jpeg. Значит действия над этой частью оптимизации сводятся к оптимизации самих картинок и настройке кеширования.
Для оптимизации картинок я использовал jpegtran, однако проблема с ним была в том, что он не может обработать сразу всю папку — только по одному файлу. Другая проблема, что использовать входной файл в качестве выходного (просто перезаписать оптимизированный файл в старый) невозможно.
Я решил эту проблему небольшим скриптиком и, хотя я не очень силён в шелл-скриптах, он работает и достаточно удобно производить замены. Итак, я создал в домашней папке скрипт jp.sh:
jpegtran -copy none -optimize -perfect "$1" > "оsh_conv_$1";\
mv "$1" raw/
mv "оsh_conv_$1" "$1"
А затем выполнял простую gоследовательность:
# mkdir raw
# find *.jpg -depth 0 -type f -iregex ".*\.jpg" -exec ~/s.sh {} \;
После этого в папке с картинками уже лежат оптимизированные джепеги, и создаётся новая папка raw/, содержащая исходные файлы. Папочка raw/ очень пригодилась, когда оказалось, что скриптик мой не отрабатывал на файлах с именами, содержамищи не латиницу (файлы просто обнулялись). Сейчас всё работает.
Итак, после того, как картинки удобным образом оптимизированы, надо было настроить кеширование. Я добавил выдачу Etag'ов. Это просто и описано на webo.in. Делается добавлением одной строки в htaccess
FileETag MTime Size
Вообще-то надо еще настроить выдачу заголовков Expires, но это тоже делается несложно.
После разборок с каринками, они стали весить на 34% меньше, что на странице Каталога ускорило загрузку почти на столько же, так как основной контент — это именно картинки.
Далее я прошёлся по хтмльной выдаче. Вёрстка не моя, по этому, я пока не полез в оптимизацию разметки, но базовые фичи сделал: убрал переносы строк, табы и повторяющиеся пробелы.
Далее интересней. Захотелось, чтобы все (даже пхпшные) странички выдавали LastModified и правильно откликались на If-Modified-Since.
Взглянул на главную страницу и понял, что там выводятся только новости и последние поступления (из динамического контента). Соответственно получил дату последней новости, дату последнего поступления, получил максимальную и обработал следующим образом:
// $md - Штамп даты последнего изменения
$modified = gmdate("D, d M Y H:i:s",$md)." GMT";// Приводим к HTTPшному формату - "Mon, 20 Dec 2004 09:34:19 GMT";
$hdr = '';
$headers = apache_request_headers();
foreach ($headers as $header => $value) {
if (eregi('If-Modified-Since', $header)) {$hdr = $value;}
}
if ($hdr === $modified) {
header ("HTTP/1.1 304 Not Modified ");
header ("Last-Modified: ". $modified);
header ("Expires:");
header ("Cache-Control:");
exit();
}
header ("Last-Modified: $modified");
header ("Expires:"); // обнуляем ненужные заголовки
header ("Cache-Control:");
* This source code was highlighted with Source Code Highlighter.
Надо отметить, что время в Last-Modified надо указывать всегда в GMT, то есть по гринвичу.
Теперь страничка загружается один раз, а далее мы видим ответ 304 Not Modified;
Еще одно важное замечание — заголовок Last-Modified надо выдавать всегда, иначе страница будет загружаться через раз, так как в ответе 304 не было указано Last-Modified. Такое поведение было замечено за FF3.
Затем добрался до выдачи css в виде архивов. Тут меня не устроило решение, которое было дано на webo.in, так как на серваке bookcare.ru нет mod_headers. Я использовал достаточно спорный метод, но в рамках данной задачи, я считаю, вполне законный:
RewriteEngine On
AddEncoding gzip .css
RewriteCond %{HTTP:Accept-encoding} !gzip [OR]
RewriteCond %{HTTP_USER_AGENT} Konqueror
RewriteRule ^(.*)\.css$ $1_css.nozip [L]
Соответственно, например main.css у нас изначально лежит в виде архива, а main_css.nozip — это обычная версия файла.
Если эти строки лежат в .htaccess в отдельной папке, то никак не влияют на файлы вне этой папки, так что в таком виде этот приём только полезен. При минимальных действиях на сервере получается то, что должно получаться.
Вот, наверное всё, если я чего-нибудь не запамятовал.
UPD1: Всё-таки запамятовал. После вырезания всех лишних символов из хтмл-выдачи отдаётся она в gzipованом виде с коэффициентом сжатия 9.
UPD2: Переписал шелловый скрипт. Теперь всё работает хорошо и с любыми именами
UPD3: Обнаружилась проблема с выдачей css в виде архивов. Исправил код .htaccess
UPD4: По совету юзера Roxis публикую код на пхп, который выбаёт правильные заголовки без привязки к mod_php
$hdr = isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])?$_SERVER['HTTP_IF_MODIFIED_SINCE']:'' ;
if ($hdr === $modified) {
header ("HTTP/1.1 304 Not Modified ");
header ("Last-Modified: $modified");
header ("Expires:");
header ("Cache-Control:");
exit();
}
header ("Last-Modified: $modified");
header ("Expires:");
header ("Cache-Control:");
* This source code was highlighted with Source Code Highlighter.
P.S. Прошу сильно не пинать, это мои первые шаги по мосту клиентской оптимизации. Но за замечания буду только благодарен.