Лучший способ сжать HTML, CSS и JS с отключенными mod_deflate и mod_gzip - PullRequest
8 голосов
/ 07 сентября 2008

У меня есть несколько сайтов на общем хосте, на котором работает 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 в Окончательное сообщение о сжатии вашего CSS выглядит хорошим решением, но я не смог заставить его работать. Кому-нибудь еще удалось использовать этот метод?

Ответы [ 5 ]

7 голосов
/ 10 сентября 2008

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

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

  • .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);

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

Что касается защиты скрипта - я делаю перевод в реальный путь в файловой системе, чтобы не проходили никакие взломанные пути '../../../etc/passwd' или другие пути файла шеллскрипта.

Это

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

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

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

4 голосов
/ 07 сентября 2008

Что я делаю:

  • Я помещаю скрипты в js и таблицы стилей в 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");
    ?>
    

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

1 голос
/ 21 декабря 2009

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

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

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

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

1 голос
/ 08 сентября 2008

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

Браузеры делают все возможное, чтобы свести к минимуму пропускную способность, и в протоколе 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-updated-since-header, отправляемый браузером, чтобы проверить, изменился ли фактический файл на сервере с даты, указанной браузером. Если это так, файл отправляется, в противном случае возвращается 304 Not Modified, и браузер не должен повторно загружать весь контент (и, если он достаточно интеллектуален, он также сохраняет проанализированный CSS в памяти).

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

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

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

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

1 голос
/ 07 сентября 2008

Вы можете попытать счастья с mod_rewrite .

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...