Сжатие CSS файлов

Во время верстки сайтов часто приходится иметь дело с большим количеством CSS файлов, например, отдельные стили для описания цветов, фонов элементов, отдельные стили для мобильных устройств и т.п. Зачастую количество файлов .css бывает хорошо за десяток. Однако для оптимизации производительности сайта настойчиво рекомендуется минимизировать количество загружаемых файлов и по возможности включить их сжатие. Как это сделать достаточно просто рассказывает эта заметка.

Поскольку мне приходится делать это буквально на каждом сайте, я озадачился написать  достаточно простое и легко переносимое решение, и, наконец, сегодня до этого дошли руки.

Итак, идея довольно простая:zip_icon

  • Сваливаем все css файлы в одну папку на сайте, например, /css/
  • C помощью скрипта, который стоит дефолтовым документом в этой папке, проверяем время изменения файлов
  • На основе этого времени реализуем правильную реакцию на запрос GET If-Modified-Since, чтобы задействовать кэш браузера и возможных прокси
  • Обязательно разрешаем кэширование, при условии валидации в запросе (см. пункт выше)
  • Сливаем все файлы в CSS в один поток и сжимаем его gzip
  • Что получилось, то и отдаем пользователю (304 Not Modified или gzip поток)

В общем, эта нехитрая идея реализуется вот таким скриптом index.php

<?php
/*
 * Сборка CSS файлов в один и передача его с сжатом виде клиенту
 */

// Расширение файлов, с которым работаем
define('FILE_EXT', '.css');
// Тип содержимого
define('CONTENT_TYPE', 'text/css');
// Политика кэширования
define('CACHE_CONTROL', 'public, max-age=86400, must-revalidate');

// Ищем все файлы по расширению, стоем спискок файлов, проверяем дату последней модификации
$myFiles = array();
$lastModified = 0;
if ($handle = opendir('.')) {
    while (false !== ($file = readdir($handle))) 
	{
        // Это мой файл?
		if (strpos($file, FILE_EXT) !== false)
		{
			// Запомним файл
			$myFiles[] = $file;
			// Дата его модификации
			$currentFileModification = filemtime($file);
			if ($currentFileModification > $lastModified)
				$lastModified = $currentFileModification;
		}
    }
    closedir($handle);
}



// Если файлов нет, возвращаем 404
if (count($myFiles) == 0)
{
	header('HTTP/1.0 404 Not Found');
	exit;
}

// Если был запрос If-Modified-Since, обрабатываем его
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) 
{
	// Разбираем заголовок If-Modified-Since и формируем timestamp
	$if_modified_since = strtotime(preg_replace('/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE']));

	if ($if_modified_since > $lastModified)
	{
		// Изменений не было
		header('HTTP/1.1 304 Not Modified');
		header('Cache-Control: ' . CACHE_CONTROL);
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModified) . ' GMT');
		exit;
	}
}

// Передаем заголовки
header('Content-Type: ' . CONTENT_TYPE);
header('Cache-Control: ' . CACHE_CONTROL);
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModified) . ' GMT');


// Старт сжатия
ob_start("ob_gzhandler");

// Порядок передачи -- по алфавиту по именам файлов
asort($myFiles);

// Передаем файлы
foreach($myFiles as $file)
	include($file);

// Вывод результата
ob_end_flush();
?>

Теперь наши CSS подключаются невероятно просто:

<link rel="stylesheet" type="text/css" href="/css/">

Результат, как видно, налицо!

  • Более чем 3х кратное сжатие (7,37 Kb сжимается до 2 Kb)
  • Активное использование кэша браузера
  • Минимальное число обращений к серверу

Первый запрос:

Первый запрос CSS

Второй запрос (другая страница):

Второй запрос CSS

Этот простой способ можно также применить и для сжатия нескольких JS-файлов.

Вот пример скрипта для скачивания.