HTTP 304客户端缓存优化的神奇作用和用法 Unknown 2009/08/19

| |

HTTP 304: Not Modified,与服务器的缓存设置非常有关。

Last-Modified

这个头标是一个响应头标,表示客户端(通常指浏览器)所请求资源在服务器端的最后修改时间。通常情况下客户端在接受这个头标后,在以后对这个资源的请求会附带一个'If-Modified-Since'请求头标,而这个头标是想告诉服务器上次客户端所请求资源的最后修改时间。如下图中的 Last-Modified: Sat, 07 Mar 2009 13:28:57 GMT,这是一个HTML页面的来自服务器的response header信息。

html-header-info

对于一些图像、css、js等静态文件资源,配置好了的apache服务器会理解这些If-Modified-Since请求头标,将头标里的时间和文件的最后修改时间进行比较并作出响应。如果二者相等则发送一个"304 Not Modified"来告诉客户端所请求资源并未修改,让客户端放心使用缓存中的资源,否则的话会重新发送一个新的资源和新的Last-Modified的头标。

但是对于一个动态的PHP脚本,我们即使在脚本加入了header('Last Modified: '.$time)来发送一个Last Modified响应头标,当客户端附带'If-Modified-Since'在次请求时apache服务器不会进行处理,这需要我们自己用$ _SERVER['HTTP_IF_MODIFIED_SINCE']来获取'If-Modified-Since'的值自己来进行判断处理。

header-if-modified-since

而且,上面提到的配置好的apache服务器会检查If-Modified-Since变量,而有些.css啊,.js啊,apache服务器似乎并不检查。这都需要我们人工检查。

在这之前,先让我们谈谈浏览器的一些工作原理:

在先前至少有过一次有效访问后,在以后对同一URI资源的请求中,浏览器只进行两种动作:

(1)直接在缓存中去获取内容。如果先前有效访问的响应头包含 Expires, max-age的话,“打开新窗口”、“输入URI回车”、“前一页”、“后一页”这些浏览器行为不会使浏览器在Expires, max-age设置的有效期时间内去访问服务器,而是在缓存中去获取内容,但是'"刷新'"或"重载"例外。

(2)访问服务器,根据服务器响应来获取内容。这种情况发生在设置no-cache等头标要求不缓存,或者是设置了 Expires,max-age但浏览器行为是“刷新”“重载”时候。'Last-Modified'、'ETag'、'must-revalidate' 等有些特殊,不直接受浏览器行为影响,它们必须访问服务器后,再由服务器判断是直接发送新的资源,还是发送一个304 Not Modfied让浏览器使用缓存中的资源。

目的

尽可能减少服务器向客户端重复发送资源,尤其是图片,Flash,CSS,JS等。

原理

1、强行判断$_ENV['If-Modified-Since']是否等于$file_mtime,如果相等,是发送HTTP 304 Not Modified,告诉浏览器,你直接用你缓存中的好了。当然了,如果两者不相等,或缓存中的文件被删除了,就直接发送新的内容。
2、$_ENV['If-Modified-Since']变量存在的前提是,在前一次有效的访问过程中,发送了形如Last-Modified: Sat, 07 Mar 2009 13:28:57 GMT的Header。

方法:

1、先配置.htaccess,强行将扩展名为.jpg|.png|.gif|.css|.js|.swf的文件,转交给gzip.php处理。参考《Bo-blog的一种更简单更实用的gzip优化方法》。
2、检查要访问的URI是否是一个文件,用is_file($file)判断,如果不是,Header至404 Error。
3、检查gzip.php得到的访问URI是否满足.jpg|.png|.gif|.css|.js|.swf的要求,如果不符合,Header至403 Forbidden。
4、检查$_ENV['If-Modified-Since']是否等于$file_mtime,如果相等,发送304 Not Modified。
5、如果第4步的结果是不相等的,判断是否是.css|.js,如果是,使用gzip发送。否则如果是.jpg|.png|.gif|.swf,不使用gzip发送。

这样做的结果与好处:除非服务器端文件更新(导致Last-Modified值变化),或是客户端缓存被清除,否则不管你是刷新还是重载,我都会发送304 Not Modified,指示你直接用你那边的缓存就可以了,不必到我这里来取。<-这对一些大的文件资源,如图像,JS,CSS等,非常有用!!!本来要发送一个100k的图像的,我现在只要发送500个字节左右的数据,告诉你直接用缓存就OK了,多好啊!

现在我们来拿数据来比较一下实际的差异。304优化后,用户的下载量仅需要88k,则原来怎么也要在460k以上,节约了66%~80%的流量。但所占的时间,效果不是很明显,整个也差不多几秒。同时每次尝试,得到的累计时间也不相同。

ps. Bytes Sent我估计和Request Count有关,向服务器请求资源时,会发现一些内容,见上面的第二张图,Request Headers,有很多内容,差不多1K,所以有一个请求,就差多占掉1K,合起来确实差不多那些量。

  Request Count Bytes Sent Bytes Received Elapsed Time
第一次载入(全部载入) 126 98,342 501,640 23.328 s
强制刷新(无304优化) 125 111,251 546,257 16.562 s
强制刷新(仅优化css和js) 125 112,538 461,056 16.437 s
强制刷新(304优化) 125 112,538 88,110 16.937 s

现在我的博客上的资源访问,除了php以外,似乎其他资源全部已经被这个304 Not Modified功能优化了。

请给这篇日志评个分吧~!

本文评分: 6.8/10 (54 votes)    提示:您还未对本文评分,您可以进行评分并发表您的意见!

加入收藏!

ourac IP
2014/02/22 21:53
现在的浏览器强制刷新的话根本就不会提交If-Modified-Since的,所以你这个方法没有什么用啊
spyrise 回复于 2014/02/27 22:37
那我就不知道了。这个方法本来是用来人工判断,压制不必要的流量的。不过好像现在网络服务的价格便宜了吧?这文章是好几年前的了。
doul IP
2012/10/18 15:06
请问楼主查看报文是用什么工具!
spyrise 回复于 2012/10/27 20:28
fiddler2
ooxx IP
2009/08/22 16:54
[ico41]原来大小写是我打错了

哈哈,终于好了,除了统计外,页面都304了
spyrise 回复于 2009/08/22 19:23
恭喜啊,呵呵!

不过,对于php页面,还是不支持304的吧?也不适合设置304。
ooxx IP
2009/08/22 11:17
由于你给的gzip.php中$_GET[\'URL\']大写,我用小写的当然不能访问啦,才出现403,
我用大写的访问就可以访问了,输出\"images/js/common.js\"

http://www.yourdomain.cn/gzip.php?URL=images/js/common.js


但是加载htaccess:RewriteRule .*.(css|js)$ /gzip.php [L]

css与js就不能正常输出
spyrise 回复于 2009/08/22 17:09
我的源代码中哪里有$_GET['URL']啊,是$_GET['url']啊。不过大小写不重要,你已经明白里面有什么区别了。

.htaccess要放在Bo-blog的根目录下。

什么叫不能正常输出?是什么也不输出?还是输出错误?建议你用fiddler查看一下。

ps. http://spyrise.org/blog/gzip-php-source-code-updated/ 我的源代码中是小写的url,不是URL。

最后,你的.htaccess写错了。这是错误的根源。
ooxx IP
2009/08/22 01:40
错了,原来是“url”大小写的问题,如果是http://www.yourdomain.cn/gzip.php?URL=images/js/common.js

就可以正确访问了,

但是用htaccess的话,还是不能正确加载[ico21]
spyrise 回复于 2009/08/22 10:51
用URL大写的话,是错的。gzip.php检测不到$_GET['url'],而是$_GET['URL'],这两者是不同的。
此时由于找不到$_GET['url'],所以$file=$_ENV['DOCUMENT_ROOT'].$_ENV['REQUEST_URI']。
这样,$file实际上是gzip.php这个文件。

接下来,由于我的程序写得有点问题,当用preg检测扩展名时,检测到的是.js,认为这个是.js文件,然后就输出了。你看看你那样写,是不是输出了gzip.php文件的内容?

具体你可以在is_file($file)之前加一句:echo $file; die();看看此时输出什么结果。

使用.htaccess,不能正确加载的具体表现是?
ooxx IP
2009/08/21 17:40
htaccess设置好了,gzip当然也放在根目录,直接访问http://www.yourdomain.cn/gzip.php?url=images/js/common.js ,就出现403错误
spyrise 回复于 2009/08/21 20:09
你呀,没仔细看我的说明。当$url带的不是绝对目录时,就会报错。

因为$_ENV['DOCUMENT_ROOT']=/home/yourdomain
$_GET['url']=images/js/common.js,你想想此时的$file是什么?不报错才怪呢。

这你个403错误是源代码中的哪个语句看明白了吗?是倒数第3行中的那个403.html。

不过,看来你的php功底不错,已经能看懂大部分了。你快要成功了!
分页: 1/2 第一页 1 2 下页 最后页
发表评论

昵称

网址

电邮

打开HTML 打开UBB 打开表情 隐藏 记住我 [注册]