Оптимізація продуктивності в Drupal: стиснення сторінок, файлів CSS і JavaScript за допомогою GZIP

  1. Рішення проблеми стиснення JS і CSS за допомогою GZIP
  2. Зміни в .htaccess
  3. Зміни в index.php
  4. Чого ж я домігся?
Версія для друку

Нещодавно постало питання про оптимізацію завантаження одного з моїх сайтів. Крім стандартних дій по збільшенню продуктивності в 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 перед усіма іншими виконуваними рядками наступний блок коду:

<? if (substr_count ($ _ SERVER [ 'HTTP_ACCEPT_ENCODING'], 'gzip')) // перевіряється, чи підтримує браузер стиснення gzip або x-gzip {if (! is_file ($ _ GET [ "q"])) // перевіряється, є чи адреса схожим на файл {ob_start ( "ob_gzhandler"); // запускається обробка gzip для стиснення html-коду сторінок сайту header ( "Content-Type: text / html; charset: UTF-8"); header ( "Cache-Control: must-revalidate"); header ( "Expires:". gmdate ( "D, d MYH: i: s", time () + 60 * 60). "GMT"); } Else if (preg_match ( "/^.* \. (Js | css) (& \ w +)? $ /", $ _SERVER [ "QUERY_STRING"], $ ext)) // перевіряється, чи є адреса схожим на файли js або css {// частину наступного шматка коду запозичена з функції drupal_page_cache_header (), і необхідна для кешування стислих файлів браузером. $ Last_modified = gmdate ( 'D, d MYH: i: s', filectime ($ _ GET [ "q"])). ' GMT '; $ Etag = ' "'. Md5 ($ last_modified). '"'; $ If_modified_since = isset ($ _ SERVER [ 'HTTP_IF_MODIFIED_SINCE'])? stripslashes ($ _ SERVER [ 'HTTP_IF_MODIFIED_SINCE']): FALSE; $ If_none_match = isset ($ _ SERVER [ 'HTTP_IF_NONE_MATCH'])? stripslashes ($ _ SERVER [ 'HTTP_IF_NONE_MATCH']): FALSE; if ($ if_modified_since && $ if_none_match && $ if_none_match == $ etag && $ if_modified_since == $ last_modified) {header ( 'HTTP / 1.1 304 Not Modified'); header ( "Expires: Sun, 19 Nov 1978 5:00:00 GMT"); header ( "Etag: $ etag"); } Else {ob_start ( "ob_gzhandler"); $ Myme = array ( "css" => "text / css", "js" => "text / javascript",); header ( "Content-Type:". (($ myme [$ ext [1]])? $ myme [$ ext [1]]: "text / html"). "; charset: UTF-8"); header ( "Last-Modified: $ last_modified"); header ( "ETag: $ etag"); header ( "Expires: Sun, 19 Nov 1978 5:00:00 GMT"); header ( "Cache-Control: must-revalidate"); print file_get_contents ($ _ GET [ "q"]); } Exit; }}?>

Цей шматок коду я вставив перед рядком:

<? require_once './includes/bootstrap.inc'; ?>

Якщо включено стиснення сторінок на сторінці "Продуктивність" в налаштуваннях Drupal, то стискати сторінки самостійно не має сенсу. І тому потрібно залишити тільки код для стиснення JS і CSS файлів. Однак я все-таки вважаю за краще відключати стиснення сторінок Друпалом (так як воно якось дивно працює) і стискаю їх за допомогою свого скрипта.

<? if (substr_count ($ _ SERVER [ 'HTTP_ACCEPT_ENCODING'], 'gzip')) // перевіряється, чи підтримує браузер стиснення gzip або x-gzip {if (preg_match ( "/^.* \. (js | css) (& \ w +)? $ / ", $ _SERVER [" QUERY_STRING "], $ ext) && is_file ($ _ GET [" q "])) // перевіряється, чи є адреса схожим на файли js або css {// частину наступного шматка коду запозичена з функції drupal_page_cache_header (), і необхідна для кешування стислих файлів браузером. $ Last_modified = gmdate ( 'D, d MYH: i: s', filectime ($ _ GET [ "q"])). ' GMT '; $ Etag = ' "'. Md5 ($ last_modified). '"'; $ If_modified_since = isset ($ _ SERVER [ 'HTTP_IF_MODIFIED_SINCE'])? stripslashes ($ _ SERVER [ 'HTTP_IF_MODIFIED_SINCE']): FALSE; $ If_none_match = isset ($ _ SERVER [ 'HTTP_IF_NONE_MATCH'])? stripslashes ($ _ SERVER [ 'HTTP_IF_NONE_MATCH']): FALSE; if ($ if_modified_since && $ if_none_match && $ if_none_match == $ etag && $ if_modified_since == $ last_modified) {header ( 'HTTP / 1.1 304 Not Modified'); header ( "Expires: Sun, 19 Nov 1978 5:00:00 GMT"); header ( "Etag: $ etag"); } Else {ob_start ( "ob_gzhandler"); $ Myme = array ( "css" => "text / css", "js" => "text / javascript",); header ( "Content-Type:". (($ myme [$ ext [1]])? $ myme [$ ext [1]]: "text / html"). "; charset: UTF-8"); header ( "Last-Modified: $ last_modified"); header ( "ETag: $ etag"); header ( "Expires: Sun, 19 Nov 1978 5:00:00 GMT"); header ( "Cache-Control: must-revalidate"); print file_get_contents ($ _ GET [ "q"]); } Exit; }}?>

Чого ж я домігся?

Завдяки цим удосконаленням я домігся стиснення здебільшого передаються з сайту даних. Застосовувати цей механізм до медіа-файлів типу картинок або відео не тільки марно, але й небезпечно для продуктивності сервера.

Крім того я зміг включити кешування стислих даних браузером. Зрештою всі ці дії дозволили стиснути HTML-код, JS і CSS файли сукупно в три рази, а вага головної сторінки зменшити вдвічі.

Наприклад, ця сторінка (де знаходиться ця стаття) була оптимізована таким чином:

Без стиснення З стисненням GZIP Зменшення розміру файлів HTML 37 КБ 10 КБ 370% CSS 90 КБ 22 КБ 400% JavaScript 465 КБ 150 КБ 310% за розміром сторінки (з картинками) 654 КБ 244 КБ 268%

PS Тестування передачі даних між сервером і браузером проводилося за допомогою розширень Web Developer і Firebug для браузера Mozilla Firefox.

Php?
Js | css) (\?. *)?
Php?
Quot;GMT"); } Else if (preg_match ( "/^.* \. (Js | css) (& \ w +)?
Quot;'; $ If_modified_since = isset ($ _ SERVER [ 'HTTP_IF_MODIFIED_SINCE'])?
Stripslashes ($ _ SERVER [ 'HTTP_IF_MODIFIED_SINCE']): FALSE; $ If_none_match = isset ($ _ SERVER [ 'HTTP_IF_NONE_MATCH'])?
Inc'; ?
Lt;?