Лучший способ сжать HTML, CSS и JS с mod_deflate и mod_gzip отключен

У меня есть несколько сайтов на общем хосте, на котором запущен Apache 2. Я хотел бы сжать HTML, CSS и Javascript, которые доставляются в браузер. Хост отключил mod_deflate и mod_gzip, поэтому эти параметры недоступны. Однако у меня есть PHP 5, поэтому я мог бы использовать компонент gzip.

В настоящее время я помещаю следующее в файл .htaccess:

php_value output_handler ob_gzhandler

Однако это только сжимает HTML и оставляет CSS и JS.

Есть ли надежный способ прозрачного сжатия вывода CSS и JS без необходимости менять каждую страницу? Я искал Google и предлагал ряд решений, но мне еще нужно заставить их работать. Если кто-то может предложить решение, которое они знают для работы, это было бы очень благодарно получено.

Обратите внимание, что метод 2 в разделе «Окончательная публикация» в Gzipping вашего CSS выглядит как хорошее решение, но я не мог заставить его работать. Кто-нибудь еще преуспел в использовании этого метода?

Извините за задержку – для меня это занятая неделя.

Предположения:

  • .htaccess находится в том же файле, что и compress.php
  • статические файлы, подлежащие сжатию, находятся в static подкаталоге

Я начал свое решение, установив следующие директивы в .htaccess:

 RewriteEngine on RewriteRule ^static/.+\.(js|ico|gif|jpg|jpeg|png|css|swf)$ compress.php [NC] 

Требуется, чтобы ваш провайдер позволял переопределять параметры mod_rewrite в файлах .htaccess . Тогда сам файл compress.php может выглядеть так:

 <?php $basedir = realpath( dirname($_SERVER['SCRIPT_FILENAME']) ); $file = realpath( $basedir . $_SERVER["REQUEST_URI"] ); if( !file_exists($file) && strpos($file, $basedir) === 0 ) { header("HTTP/1.0 404 Not Found"); print "File does not exist."; exit(); } $components = split('\.', basename($file)); $extension = strtolower( array_pop($components) ); switch($extension) { case 'css': $mime = "text/css"; break; default: $mime = "text/plain"; } header( "Content-Type: " . $mime ); readfile($file); 

Разумеется, вы должны добавить в оператор switch больше типов mime. Я не хотел, чтобы решение зависело от расширения файловой системы pecl или любых других библиотек обнаружения магического типа mime – это самый простой подход.

Что касается обеспечения подлинности скрипта – я делаю перевод на реальный путь в файловой системе, поэтому никакие взломы «../../../etc/passwd» или другие пути файлов shellscript не проходят.

Это

 $basedir = realpath( dirname($_SERVER['SCRIPT_FILENAME']) ); $file = realpath( $basedir . $_SERVER["REQUEST_URI"] ); 

сниппет. Хотя я уверен, что большинство путей, которые находятся в другой иерархии, чем $ basedir, будут обрабатываться Apache, прежде чем они даже достигнут скрипта.

Также я проверяю, находится ли результирующий путь внутри дерева каталогов скрипта. Добавьте заголовки для управления кешем, как предложил pilif, и у вас должно быть рабочее решение для вашей проблемы.

Что я делаю:

  • Я размещаю скрипты в js и stylesheets в css , соответственно.
  • В конфигурации Apache я добавляю такие директивы:

     <Directory /data/www/path/to/some/site/js/> AddHandler application/x-httpd-php .js php_value auto_prepend_file gzip-js.php php_flag zlib.output_compression On </Directory> <Directory /data/www/path/to/some/site/css/> AddHandler application/x-httpd-php .css php_value auto_prepend_file gzip-css.php php_flag zlib.output_compression On </Directory> 
  • gzip-js.php в каталоге js выглядит следующим образом:

     <?php header("Content-type: text/javascript; charset: UTF-8"); ?> 
  • … и gzip-cs.php в каталоге css выглядит так:

     <?php header("Content-type: text/css; charset: UTF-8"); ?> 

Это может быть не самое изящное решение, но оно, безусловно, является простым, которое требует нескольких изменений и хорошо работает.

что бы вы ни делали, будьте осторожны с кешированием на стороне клиента:

Браузеры делают всевозможные трюки, чтобы попытаться свести к минимуму полосу пропускания, и есть много способов в протоколе HTTP, чтобы сделать это, все из которых обрабатываются apache – если вы просто обслуживаете локальный файл.

Если вы этого не сделаете, это ваша ответственность .

Посмотрите, по крайней мере, на ETag и механику If-Modified-Since, поддерживаемую всеми текущими браузерами, и, по-видимому, являются наиболее надежным способом запроса сервера на обновленный контент.

Возможный способ использования CSS-файла для браузеров с использованием If-Modified-Since-Header – это что-то вроде этого (пустые заголовки для отключения любых кеширующих заголовков PHP отправляются по умолчанию):

 $p = 'path/to/css/file' $i = stat($p); if ($_SERVER['HTTP_IF_MODIFIED_SINCE']){ $imd = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']); if ( ($imd > 0) && ($imd >= $i['mtime'])){ header('HTTP/1.0 304 Not Modified'); header('Expires:'); header('Cache-Control:'); header('Last-Modified: '.date('r', $i['mtime'])); exit; } } header('Last-Modified: '.date('r', $i['mtime'])); header('Content-Type: text/css'); header('Content-Length: '.filesize($p)); header('Cache-Control:'); header('Pragma:'); header('Expires:'); readfile($p); 

Код будет использовать заголовок if-modified-since, который браузер отправляет, чтобы проверить, изменился ли фактический файл на сервере с даты, указанной браузером. Если это так, файл отправляется, в противном случае возвращается 304 Not Modified, и браузеру не нужно повторно загружать весь контент (и, если он достаточно интеллектуальный, он также поддерживает анализируемый CSS в памяти).

Существует еще один механик, в котором сервер отправляет уникальный заголовок ETag для каждой части контента. Клиент отправит это обратно, используя заголовок If-None-Match, позволяющий серверу решать не только дату последней модификации, но и самого содержимого.

Это только делает код более сложным, хотя я так и оставил его. FF, IE и Opera (возможно, Safari тоже) отправляют заголовок If-Modified-Since, когда они получают контент с прикрепленным заголовком Last-Modified, поэтому это работает нормально.

Также имейте в виду, что определенные версии IE (или используемое JScript-Runtime) все еще имеют проблемы с переданным GZIP контентом.

Ой. И я знаю, что это не вопрос, но Acrobat также в некоторых версиях. У меня были случаи и случаи белых экранов при работе с PDF-файлами с кодировкой передачи gzip.

Вместо gzipping «на лету», когда пользователи запрашивают файлы CSS и JavaScript, вы можете их заранее скопировать. Пока Apache обслуживает их с правильными заголовками, вы золотые.

Например, в Mac OS X gzipping файла в командной строке так же просто, как:

 gzip -c styles.css > styles-gzip.css 

Возможно, это не рабочий процесс, который работает для вас.

Вы можете испытать удачу с mod_rewrite .

Создайте сценарий, который принимает локальное статическое имя файла как входное, например, $_SERVER['QUERY_STRING'] и выводит его в сжатой форме. Многие провайдеры не позволяют настраивать mod_rewrite с файлами .htaccess или полностью отключить его.

Если вы раньше не использовали переписывание , я рекомендую руководство для начинающих, как, вероятно, это . Таким образом, вы можете заставить apache перенаправить все запросы на статический файл на php-скрипт. style.css будет перенаправлен на compress.php? style.css, например.

Как всегда будьте предельно осторожны на входе, которое вы принимаете, или у вас есть эксплойт XSS на ваших руках!