我在运行Apache 2的共享主机上有几个站点。我想压缩传递给浏览器的HTML,CSS和Javascript。主机已禁用mod_deflate和mod_gzip,因此这些选项已经用完。不过我可以使用PHP 5,所以我可以使用它的gzip组件。
我目前将以下内容放在我的.htaccess文件中:
php_value output_handler ob_gzhandler
但是,这只会压缩HTML并省略CSS和JS。
是否有一种可靠的方式透明地压缩CSS和JS的输出而无需更改每一页?我搜索过谷歌并提供了一些解决方案,但我还没有找到一个解决方案。如果有人可以提出他们知道可行的解决方案,那将非常感激。
注意, The Definitive Post on Gzipping your CSS 中的方法2 看起来是一个很好的解决方案,但我无法让它发挥作用。还有其他人成功使用过这种方法吗?
答案 0 :(得分:7)
对于延迟感到抱歉 - 这对我来说是忙碌的一周。
假设:
.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 fileinfo
扩展或任何其他神奇的mime类型检测库 - 这是最简单的方法。
至于保护脚本 - 我会对文件系统中的实际路径进行转换,因此不会破解“../../../etc/passwd”或其他shellcript文件路径。< / p>
那是
$basedir = realpath( dirname($_SERVER['SCRIPT_FILENAME']) );
$file = realpath( $basedir . $_SERVER["REQUEST_URI"] );
片段。虽然我非常确定其他层次结构中除了$ basedir之外的大多数路径都会在它们到达脚本之前由Apache处理。
另外,我检查生成的路径是否在脚本的目录树中。 像pilif建议的那样添加缓存控制的标题,你应该找到解决问题的方法。
答案 1 :(得分:4)
我的所作所为:
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>
js
目录中的gzip-js.php如下所示:
<?php
header("Content-type: text/javascript; charset: UTF-8");
?>
... css
目录中的gzip-cs.php如下所示:
<?php
header("Content-type: text/css; charset: UTF-8");
?>
这可能不是最优雅的解决方案,但它肯定是一个简单的解决方案,只需要很少的改动并且效果很好。
答案 2 :(得分:1)
您可以通过mod_rewrite试试运气。
创建一个脚本,该脚本将本地静态文件名作为输入,例如, $_SERVER['QUERY_STRING']
并以压缩形式输出。许多提供商不允许使用mod_rewrite
文件配置.htaccess
,或者完全禁用它。
如果您之前没有使用重写,我建议您使用优秀的初学者指南,例如this one。 这样,您可以使apache将所有静态文件请求重定向到php脚本。例如,style.css将被重定向到 compress.php?style.css 。
对于您接受的输入始终非常谨慎,或者您手上有XSS
漏洞!
答案 3 :(得分:1)
浏览器会尝试各种技巧来最小化带宽,HTTP协议中有很多方法可以做到这一点,所有这些都是由apache处理的 - 如果你只是服务于本地文件。
如果你不是,那么这是你的责任。
至少看一下所有当前浏览器都支持的ETag和If-Modified-Since机制,似乎是查询服务器获取更新内容的最强大的方法。
使用If-Modified-Since-Header向浏览器提供CSS文件的一种可能方法是这样的(关闭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-header来检查自浏览器发出之日起服务器上的实际文件是否已更改。如果是这样,则发送文件,否则返回304 Not Modified,浏览器不必重新下载整个内容(如果它足够智能,它也会将解析后的CSS保留在内存中)。
还有另一种机制,涉及服务器为每个内容发送一个唯一的ETag-Header。客户端将使用If-None-Match标头发送回来,允许服务器不仅决定上次修改的日期,还决定内容本身。
这只会使代码更复杂,所以我把它留了出来。 FF,IE和Opera(也可能是Safari)都会在收到带有Last-Modified标头的内容时发送If-Modified-Since标头,所以这样可以正常工作。
另请注意,某些版本的IE(或其使用的JScript-Runtime)仍然存在GZIP传输内容的问题。
喔。我知道这不是问题的一部分,但Acrobat在某些版本中也是如此。我在使用gzip传输编码提供PDF时遇到了白屏的案例和案例。
答案 4 :(得分:1)
当用户请求CSS和JavaScript文件时,您可以动态地抓取它们,而不是动态gzip。只要Apache使用正确的标题为它们服务,你就是金色的。
例如,在Mac OS X上,在命令行上压缩文件非常简单:
gzip -c styles.css > styles-gzip.css
可能不是那种适合你的工作流程。