Недавно встал вопрос об оптимизации загрузки одного из моих сайтов. Помимо стандартных действий по увеличению производительности в Drupal: минимизации кода CSS и JavaScript, установки необходимых настроек на странице «Производительность», я решил уменьшить размер отдаваемых браузеру файлов и страниц сайта с помощью их сжатия.
После посещения множества сайтов и чтения кучи статей и комментариев я выяснил, что сжатие можно проводить двумя способами: с помощью средств сервера Apache или средствами PHP. Просмотрев настройки сервера на оптимизируемом сайте, оказалось, что на сервере не установлен модуль mod_gzip и mod_deflate, а значит использовать первый способ с Apache не приходится.
Пришлось снова лезть в Google и искать ответы на вопрос сжатия с помощью PHP. Все приведенные примеры сжатия были либо не применимы для Drupal, либо просто ужасны в реализации. В конце концов было сформировано решения на базе одного из методов.
Решения проблемы сжатия JS и CSS с помощью GZIP
Для того, чтобы включить сжатие JS и CSS файлов в Drupal, необходимо изменить два файла: .htaccess
и index.php
.
Изменения в .htaccess
В .htaccess нужно изменить стандартный для Drupal раздел переадресации на скрипт index.php
. Вместо обычных двух строчек:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
нужно вставить следующий код:
RewriteCond %{REQUEST_FILENAME} !-f [OR]
RewriteCond %{REQUEST_FILENAME} ^.*\.(js|css)(\?.*)?$
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
Вообще весь код выполняет некоторую проверку запрашиваемого браузером адреса и при ее успешном прохождении производит переадресацию на файл index.php
.
Теперь я объясню второй блок кода. В первой строке проверяется является ли запрашиваемый адрес файлом. Если адрес является существующим на сервере файлом, то в целом все условие становится ложным и переадресации на index.php
не происходит.
По умолчанию следующие друг за другом условия RewriteCond соединяются логическим AND. В нашем случае нам необходимо использовать логическое OR, чтобы файлы js и css прошли по условию во второй строке. Для этого я поставил флаг [OR]
в конце первой строки. Такой флаг объединяет логическим ИЛИ текущую и следующую строку. В третьей строке отсеиваются директории сервера.
Зачем нужна четвертая строка, если уже есть первая я так и не понял. Буду признателен, если кто-то объяснит это в комментарии к статье.
Пятая строка производит перенаправление на файл index.php
при выполнении предшествующих ей условий. При этом вся часть адреса после имени домена передается в параметр q
.
Изменения в index.php
После обработки сервером директив в файле .htaccess
управление передается файлу index.php
. В нем и будет происходить сжатие файлов и страниц сайта. Для осуществления этой жизненно необходимой процедуры нужно добавить в файл index.php
перед всеми остальными выполняемыми строчками следующий блок кода:
02 |
if (substr_count( $_SERVER [ 'HTTP_ACCEPT_ENCODING' ], 'gzip' )) |
03 |
// проверяется, поддерживает ли браузер сжатие gzip или x-gzip |
05 |
if (! is_file ( $_GET [ "q" ])) |
06 |
// проверяется, является ли адрес похожим на файл |
08 |
ob_start( "ob_gzhandler" ); // запускается обработка gzip для сжатия html-кода страниц сайта |
09 |
header ( "Content-Type: text/html; charset: UTF-8" ); |
10 |
header ( "Cache-Control: must-revalidate" ); |
11 |
header ( "Expires: " . gmdate ( "D, d M Y H:i:s" , time() + 60 * 60) . " GMT" ); |
13 |
else if (preg_match( "/^.*\.(js|css)(&\w+)?$/" , $_SERVER [ "QUERY_STRING" ], $ext )) |
14 |
// проверяется, является ли адрес похожим на файлы js или css |
16 |
// часть следующего куска кода позаимствована из функции drupal_page_cache_header(), и необходима для кэширования сжатых файлов браузером. |
17 |
$last_modified = gmdate ( 'D, d M Y H:i:s' , filectime ( $_GET [ "q" ])) . ' GMT' ; |
18 |
$etag = '"' . md5( $last_modified ) . '"' ; |
20 |
$if_modified_since = isset ( $_SERVER [ 'HTTP_IF_MODIFIED_SINCE' ]) ? stripslashes ( $_SERVER [ 'HTTP_IF_MODIFIED_SINCE' ]) : FALSE; |
21 |
$if_none_match = isset ( $_SERVER [ 'HTTP_IF_NONE_MATCH' ]) ? stripslashes ( $_SERVER [ 'HTTP_IF_NONE_MATCH' ]) : FALSE; |
23 |
if ( $if_modified_since && $if_none_match && $if_none_match == $etag && $if_modified_since == $last_modified ) |
25 |
header( 'HTTP/1.1 304 Not Modified' ); |
26 |
header( "Expires: Sun, 19 Nov 1978 05:00:00 GMT" ); |
27 |
header( "Etag: $etag" ); |
31 |
ob_start( "ob_gzhandler" ); |
34 |
"js" => "text/javascript" , |
36 |
header ( "Content-Type: " .(( $myme [ $ext [1]])? $myme [ $ext [1]]: "text/html" ). "; charset: UTF-8" ); |
37 |
header( "Last-Modified: $last_modified" ); |
38 |
header( "ETag: $etag" ); |
39 |
header( "Expires: Sun, 19 Nov 1978 05:00:00 GMT" ); |
40 |
header( "Cache-Control: must-revalidate" ); |
41 |
print file_get_contents ( $_GET [ "q" ]); |
Этот кусок кода я вставил перед строкой:
2 |
require_once './includes/bootstrap.inc' ; |
Если включено сжатие страниц на странице «Производительность» в настройках Drupal, то сжимать страницы самостоятельно не имеет смысла. И поэтому нужно оставить только код для сжатия JS и CSS файлов. Однако я все-таки предпочитаю отключать сжатие страниц Друпалом (так как оно как-то странно работает) и сжимаю их с помощью своего скрипта.
02 |
if (substr_count( $_SERVER [ 'HTTP_ACCEPT_ENCODING' ], 'gzip' )) |
03 |
// проверяется, поддерживает ли браузер сжатие gzip или x-gzip |
05 |
if (preg_match( "/^.*\.(js|css)(&\w+)?$/" , $_SERVER [ "QUERY_STRING" ], $ext ) && is_file ( $_GET [ "q" ])) |
06 |
// проверяется, является ли адрес похожим на файлы js или css |
08 |
// часть следующего куска кода позаимствована из функции drupal_page_cache_header(), и необходима для кэширования сжатых файлов браузером. |
09 |
$last_modified = gmdate ( 'D, d M Y H:i:s' , filectime ( $_GET [ "q" ])) . ' GMT' ; |
10 |
$etag = '"' . md5( $last_modified ) . '"' ; |
12 |
$if_modified_since = isset ( $_SERVER [ 'HTTP_IF_MODIFIED_SINCE' ]) ? stripslashes ( $_SERVER [ 'HTTP_IF_MODIFIED_SINCE' ]) : FALSE; |
13 |
$if_none_match = isset ( $_SERVER [ 'HTTP_IF_NONE_MATCH' ]) ? stripslashes ( $_SERVER [ 'HTTP_IF_NONE_MATCH' ]) : FALSE; |
15 |
if ( $if_modified_since && $if_none_match && $if_none_match == $etag && $if_modified_since == $last_modified ) |
17 |
header( 'HTTP/1.1 304 Not Modified' ); |
18 |
header( "Expires: Sun, 19 Nov 1978 05:00:00 GMT" ); |
19 |
header( "Etag: $etag" ); |
23 |
ob_start( "ob_gzhandler" ); |
26 |
"js" => "text/javascript" , |
28 |
header ( "Content-Type: " .(( $myme [ $ext [1]])? $myme [ $ext [1]]: "text/html" ). "; charset: UTF-8" ); |
29 |
header( "Last-Modified: $last_modified" ); |
30 |
header( "ETag: $etag" ); |
31 |
header( "Expires: Sun, 19 Nov 1978 05:00:00 GMT" ); |
32 |
header( "Cache-Control: must-revalidate" ); |
33 |
print file_get_contents ( $_GET [ "q" ]); |
Чего же я добился?
Благодаря этим усовершенствованиям я добился сжатия большей части передаваемых с сайта данных. Применять этот механизм к медиа-файлам типа картинок или видео не только бесполезно, но и опасно для производительности сервера.

Кроме того я смог включить кэширование сжатых данных браузером. В конце концов все эти действия позволили сжать HTML-код, JS и CSS файлы совокупно в три раза, а вес главной страницы уменьшить вдвое.
Например, эта страница (где находится эта статья) была оптимизирована следующим образом:
|
Без сжатия |
Со сжатием GZIP |
Уменьшение размера файлов |
HTML |
37 КБ |
10 КБ |
370% |
CSS |
90 КБ |
22 КБ |
400% |
JavaScript |
465 КБ |
150 КБ |
310% |
Страница целиком (с картинками) |
654 КБ |
244 КБ |
268% |
P.S. Тестирование передачи данных между сервером и браузером проводилось с помощью расширений Web Developer и Firebug для браузера Mozilla Firefox.
Источник: w3pro.ru