在禁用 mod_deflate 和 mod_gzip 的情况下压缩 HTML、CSS 和 JS 的最佳方法
Posted
技术标签:
【中文标题】在禁用 mod_deflate 和 mod_gzip 的情况下压缩 HTML、CSS 和 JS 的最佳方法【英文标题】:Best way to compress HTML, CSS & JS with mod_deflate and mod_gzip disabled 【发布时间】:2010-09-08 02:13:58 【问题描述】:我在运行 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 看起来是一个不错的解决方案,但我无法让它发挥作用。有其他人成功使用此方法吗?
【问题讨论】:
只是好奇 - 为什么主机公司禁用了 mod_deflate 和 mod_gzip?!实际上,他们感兴趣的是让它们打开! 【参考方案1】:很抱歉耽搁了——这对我来说是忙碌的一周。
假设:
.htaccess
与 compress.php
在同一个文件中
要压缩的静态文件在static
子目录下
我从在 .htaccess 中设置以下指令开始我的解决方案:
RewriteEngine on
RewriteRule ^static/.+\.(js|ico|gif|jpg|jpeg|png|css|swf)$ compress.php [NC]
要求您的提供程序允许您覆盖.htaccess
文件中的mod_rewrite
选项。
那么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' 或其他 shellscript 文件路径不会通过。
就是这样
$basedir = realpath( dirname($_SERVER['SCRIPT_FILENAME']) );
$file = realpath( $basedir . $_SERVER["REQUEST_URI"] );
sn-p。虽然我很确定在 $basedir 之外的其他层次结构中的大多数路径甚至在到达脚本之前都会被 Apache 处理。
我还检查生成的路径是否在脚本的目录树中。 按照 pilif 的建议添加缓存控制的标头,您应该有一个可行的解决方案来解决您的问题。
【讨论】:
谢谢,我会试一试。我当然希望 SOF 有电子邮件提醒,以便在问题更新时告诉您。 嘿,有了这个,我将如何使用它。我把它保存为compress.php 对吗?我使用包含吗? 这个脚本假定它是由 apache 重写规则运行的,并被赋予一个文件名作为 URI 参数。 我明白了。但是,如果我更新我的文件,它就不起作用。这是否意味着旧的被压缩了?我更新了我的 CSS,得到了一个包含内容但没有样式的页面。 更新文件是什么意思?您无需对要压缩的文件进行任何操作 - 您只需将此脚本放在服务器上并适当地设置重写规则。【参考方案2】:我做什么:
我将脚本放在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");
?>
这可能不是最优雅的解决方案,但它肯定是一个简单的解决方案,只需要很少的更改并且运行良好。
【讨论】:
我刚刚尝试过,不幸的是我无法让它工作。当我按照建议添加指令时,出现内部服务器错误。【参考方案3】:无论你做什么,都要小心客户端的缓存:
浏览器会使用各种技巧来尝试最小化带宽,并且 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 时,我遇到过白屏的案例。
【讨论】:
您是否有针对 GZIP 内容的 IE/JScript 问题的 src/url?我记得旧版本的 IE(例如 IE6 之前的版本)不支持 gzip,但这只是意味着它们提供了较慢的未压缩内容。如果 IE 仍然存在问题,我当然想知道! 我遇到过 IE 无法解析使用 gzip 传输的外部 JS 文件的情况。 Acrobat 插件的某些版本(甚至是当前版本)也无法处理压缩,尽管他们声称可以。【参考方案4】:当用户请求 CSS 和 JavaScript 文件时,您可以提前对它们进行 gzip,而不是即时 gzip。只要 Apache 为它们提供正确的标头,您就是黄金。
例如,在 Mac OS X 上,在命令行上 gzip 文件非常简单:
gzip -c styles.css > styles-gzip.css
但可能不是适合您的工作流程。
【讨论】:
【参考方案5】:您可以通过mod_rewrite 试试运气。
创建一个以本地静态文件名作为输入的脚本,例如通过$_SERVER['QUERY_STRING']
并以压缩形式输出。许多提供商不允许使用 .htaccess
文件配置 mod_rewrite
,或者将其完全禁用。
如果你之前没有使用过rewrite,我推荐一个很好的初学者指南,比如this one。 这样,您可以使 apache 将对静态文件的所有请求重定向到 php 脚本。例如 style.css 将被重定向到 compress.php?style.css。
一如既往地非常谨慎对待你接受的输入,否则你手上有XSS
漏洞利用!
【讨论】:
@macbirdie,非常感谢这个建议。您能否提供或链接到这种方法的任何示例?我也有兴趣了解一些有关保护此类脚本的一般技术。以上是关于在禁用 mod_deflate 和 mod_gzip 的情况下压缩 HTML、CSS 和 JS 的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章
Apache 未发送 304 响应(如果启用了 mod_deflate 和 AddOutputFilterByType)