Expires、Cache-Control、Last-Modified、ETag是RFC 2616(HTTP/1.1)协议中和网页缓存相关的几个字段。前两个用来控制缓存的失效日期,后两个用来验证网页的有效性。要注意的是, HTTP/1.0有一个功能比较弱的缓存控制机制:Pragma,使用HTTP/1.0的缓存将忽略Expires和Cache-Control头。我们这里以Apache2.0服务器为例,只讨论HTTP/1.1协议。

 

首先,我们查看 php 动态网页,没有 expires 过期时间的响应头信息

php 动态网页是无法设置过期时间的,因为是动态的,其过期时间默认为 Expires: Wed, 11 Jan 1984 05:00:00 GMT

 

接着,我们查看 css 静态文件,设置了 expires 过期时间的响应头信息

css 静态文件可以设置过期时间, Expires 和 Cache-Control 中的 max-age 是对应的,其计算起始时间为 Date

这里,还需要注意两个字段:Last-Modified、ETag,以及返回的状态码 Status Code: 200 OK (from disk cache)

 

最后,给出一个Gzip压缩后,又设置了缓存日期的加载请求,效果非常明显、直观

如上图,gzip 是开启了网页压缩,详见米扑博客:Apache 启用 GZIP 压缩网页传输方法

Expires 是开启了过期时间,大量的 js、css 直接从本地浏览器缓存读取,减轻了服务器压力,大大提供了访问速度

 

好了,有了初步认识,下面开始正文啦

Expires

Expires字段声明了一个网页或URL地址不再被浏览器缓存的时间,一旦超过了这个时间,浏览器都应该联系原始服务器。

RFC告诉我们:“由于推断的失效时间也许会降低语义透明度,应该被谨慎使用,同时我们鼓励原始服务器尽可能提供确切的失效时间。”

对于一般的纯静态页面,如html、gif、jpg、css、js,默认安装的Apache服务器,不会在响应头添加Expires这个过期时间字段。Firefox浏览器接受到相应后,如果发现没有Expires字段,浏览器根据文件的类型和“Last-Modified”字段来推断出一个合适的失效时间,并存储在客户端。推测出的时间一般是接受到响应时间后的三天左右。

Apache的 expires_module 模块可以在Http响应头部自动加上Expires字段,在Apache的httpd.conf文件中进行如下配置:

#启用expires_module模块

LoadModule expires_module modules/mod_expires.so

# 启用有效期控制

ExpiresActive On

# GIF有效期为1个月

ExpiresByType image/gif A2592000

# HTML文档的有效期是最后修改时刻后的一星期

ExpiresByType text/html M604800

 

#以下的含义类似

ExpiresByType text/css “now plus 2 month”
ExpiresByType text/js “now plus 2 day”
ExpiresByType image/jpeg “access plus 2 month”
ExpiresByType image/bmp “access plus 2 month”
ExpiresByType image/x-icon “access plus 2 month”
ExpiresByType image/png “access plus 2 month”

注意:

对于 jsp、php等动态页面,如果在页面内部没有通过函数强制加上Expires,例如 header(”Expires: ” . gmdate(”D, d M Y H:i:s”) . ” GMT”),Apache服务器会把Wed, 11 Jan 1984 05:00:00 GMT 作为Expires字段内容,返回给浏览器,即认为动态页面总是失效的,而浏览器仍然会保存已经失效的动态页面。这也是本文开头介绍的 php 动态页面没有过期时间。

可以发现Firefox浏览器总是缓存所有页面,不管失效、不失效还是没有声明失效时间。即使缓存中声明了一个网页的实效日期是1970-01- 01 08:00:00,浏览器仍然会发送该文件在缓存中的Last-Modified和ETag字段。 如果在服务器端验证通过,返回304状态,浏览器就还会使用此缓存。

米扑博客推荐配置参数:

在 httpd.conf 文件末尾,手动添加如下内容:

vim conf/httpd.conf

FileETag None

<IfModule mod_expires.c>  
    ExpiresActive On  
    ExpiresDefault "accesss plus 600 seconds"
        
    ExpiresByType text/html "accesss plus 600 seconds"  
    ExpiresByType text/css "now plus 1 month"  
    ExpiresByType text/min.css "now plus 1 month"  
    ExpiresByType text/x-component "access plus 1 month"
    ExpiresByType image/jpeg "access plus 1 month"  
    ExpiresByType image/gif "access plus 1 month"  
    ExpiresByType image/png "access plus 1 month"  
    ExpiresByType image/bmp "access plus 1 month"  
    ExpiresByType image/x-icon "access plus 3 months"  
    ExpiresByType image/svg+xml "access plus 3 months"  
    ExpiresByType audio/ogg "access plus 1 month"  
    ExpiresByType video/mp4 "access plus 1 month"  
    ExpiresByType video/ogg "access plus 1 month"  
    ExpiresByType video/webm "access plus 1 month"  
    ExpiresByType video/x-flv "access plus 1 month"
    ExpiresByType application/json "accesss plus 600 seconds"  
    ExpiresByType application/xml "accesss plus 600 seconds"  
    ExpiresByType application/pdf "access plus 1 month"
    ExpiresByType application/javascript "now plus 30 days"  
    ExpiresByType application/x-javascript "now plus 30 days"  
    ExpiresByType application/x-font-ttf "access plus 1 month"
    ExpiresByType application/font-woff "access plus 1 month"
    ExpiresByType application/font-woff2 "access plus 1 month"
    ExpiresByType application/x-shockwave-flash "access plus 1 month"  
</IfModule>

 

Cache-Control

Cache-Control字段中可以声明多些元素,例如no-cache, must-revalidate, max-age=0等。这些元素用来指明页面被缓存最大时限,如何被缓存的,如何被转换到另一个不同的媒介,以及如何被存放在持久媒介中的。但是任何一个 Cache-Control指令都不能保证隐私性或者数据的安全性。“private”和“no-store”指令可以为隐私性和安全性方面提供一些帮助,但是他们并不能用于替代身份验证和加密。

Apache的mod_cern_meta模块允许文件级Http响应头部的控制,同时它也可以配置Cache-Control头(或任何其他头)。响应头文件是放在原始目录的子目录中,根据原始文件名所命名的一个文件。具体用法请参阅Apache的官方网站。

其中Cache-Control : max-age表示失效日期。如果没有启动mod_cern_meta模块,Apache服务器会把Expires字段中的日期换算成以秒为单位的一个 delta值,赋值给max-age。如果启动mod_cern_meta模块,并且配置了max-age值,Apache会将这个覆盖Expires字段。同时,max-age隐含了Canche-Control: public,这样浏览器接受到的Cache-Control : max-age和Expires值就是一致的。

如果失效日期Cache-Control : max-ag=0或者是负值,浏览器会在对应的缓存中把Expires设置为1970-01-01 08:00:00。

 

Last-Modified

Last-Modified和ETag是条件请求(Conditional Request)相关的两个字段。如果一个缓存收到了针对一个页面的请求,它发送一个验证请求询问服务器页面是否已经更改,在HTTP头里面带上 ETag 和 If Modify Since 头。服务器根据这些信息判断是否有更新信息,如果没有,就返回HTTP 304(Not Modify);如果有更新,返回HTTP 200和更新的页面内容,并且携带新的 ETag 和 Last-Modified 

使用这个机制,能够避免重复发送文件给浏览器,不过仍然会产生一个HTTP请求。

一般纯静态页面本身都会有Last-Modified信息,Apache服务器会读取页面文件中的Last-Modified信息,并添加到http响应头部。

对于动态页面,如果在页面内部没有通过函数强制加上Last-Modified,例如header(”Last-Modified: ” . gmdate(”D, d M Y H:i:s”) . ” GMT”),Apache服务器会把当前时间作为Last-Modified,返回给浏览器。

无论是纯静态页面还是动态页面,Firefox浏览器巧妙地按照接受到服务器响应的时间设置缓存页面的Last-Modified,而不是按照http响应头部中的Last-Modified字段。

 

ETag

既然有了Last-Modified,为什么还要用ETag字段呢?因为如果在一秒钟之内对一个文件进行两次更改,Last-Modified就会不正确。因此,HTTP/1.1利用Entity Tag头提供了更加严格的验证。

Apache服务器默认情况下,会对所有的静态、动态文件的响应头添加ETag字段。

在Apache的httpd.conf文件中可以通过FileETag指令配置该选项。FileETag指令配置了当文档是基于一个文件时用以创建 Etag(entity tag)响应头的文件的属性。在Apache 1.3.22及以前,ETag的值是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。 如果一个目录的配置包含了‘FileETag INode MTime Size’而其一个子目录包含了‘FileETag -INode’那么这个子目录的设置(并会被其下任何没有进行覆盖的子目录继承)将等价于‘FileETag MTime Size’。

例如,本文开头 Expires 图2中,设置‘FileETag MTime Size’后,得到的 ETag: "426e-57377c80fbf1a-gzip"

在多台负载平衡的服务器环境下,同一个文件会有不同的etag或者文件修改日期,浏览器每次都会重新下载。设置‘FileETag None’可以使响应头不再包含ETag字段。

设置‘FileETag None’后,得到的 ETag 字段将会隐藏消失,不再显示出来

 

Apache提供了mod_expires.so模块,可以轻松的设置expires值,以下是配置的范例:

LoadModule expires_module modules/mod_expires.so

ExpiresActive On

ExpiresDefault "access plus 300 seconds"  # 默认300秒过期

<Directory "/var/www/html">   #文件所在目录

    Options FollowSymLinks MultiViews

    AllowOverride All

    Order allow,deny

    Allow from all

    ExpiresByType text/html "access plus 1 day"  #设置cache时间为1天

    ExpiresByType text/css "access plus 1 day"

    ExpiresByType text/javascript "access plus 1 day"

    ExpiresByType image/gif "access plus 1 day"

    ExpiresByType image/jpg "access plus 1 day"

    ExpiresByType image/png "access plus 1 day"

    ExpiresByType application/x-shockwave-flash "access plus 1 day"

</Directory>

对于同一个操作对象,ExpiresByType比ExpiresDefault优先级高,当expires和max-age同时存在时,max-age的优先级会高于expires.

当你不知道某个文件的MIME类型时,你可以通过浏览器去看httpwatch去看MIME类型.

如果要对某个目录里的某个文件设置Expires头,可以用<filesmatch "正则"></filematch>来匹配.

设置缓存也可以通过mod_headers模块修改cache-control来实现.

header set cache-control "max-age="3600".

mod_expires 实例:

<Directory /opt>

  ExpiresActive On

  ExpiresDefault "accesss plus 3600 seconds"   如果是1秒,后面也是seconds

  ExpiresByType application/octet-stream "accesss plus 1 months"  这是对特殊文件类型bin缓存1个月

  <FilesMatch ^data.swf$> 针对opt目录下data.swf设置Expire值

   ExpiresActive On

   ExpiresDefault "accesss plus 60 seconds"

  </FilesMatch>

</Directory>

 

mod_headers实例:

<Directory /opt>

  header set cache-control "max-age=3600"

  <FilesMatch ^data.swf$>

      header set cache-control "max-age=60"

  </FilesMatch>

</Directory>

 

Apache开启deflate(Gzip)和Expires提升网页加载速度

在Apache的配置文件中找到下面行,将注释#去掉,重启。

LoadModule deflate_module modules/mod_deflate.so  

LoadModule expires_module modules/mod_expires.so  

LoadModule header_module modules/mod_header.so 

在http.conf后面加上这一段进行定制:(如非必要,用默认的就好了)

<ifmodule mod_deflate.c>  
AddOutputFilterByType DEFLATE text/html text/plain text/css application/x-httpd-php text/javascript application/x-javascript text/css  
AddOutputFilter DEFLATE js css         #压缩js,css文件  
# Don't compress images                                           #对照片文件不进行压缩处理  
SetEnvIfNoCase Request_URI (?:gif|jpe?g|png)$ no-gzip dont-vary  
SetEnvIfNoCase Request_URI (?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-vary  
SetEnvIfNoCase Request_URI .pdf$ no-gzip dont-vary  
#SetEnvIfNoCase Request_URI .(css|js)$ no-gzip dont-vary  
# Make sure proxies don't deliver the wrong content  
Header append Vary User-Agent env=!dont-vary  
DeflateCompressionLevel 6  
SetOutputFilter DEFLATE  
</ifmodule>  
<IfModule mod_expires.c>  
ExpiresActive On  
ExpiresByType text/css "now plus 1 month"  
ExpiresByType application/x-javascript "now plus 5 day"  
ExpiresByType image/jpeg "access plus 1 month"  
ExpiresByType image/gif "access plus 1 month"  
ExpiresByType image/bmp "access plus 1 month"  
ExpiresByType image/x-icon "access plus 1 month"  
ExpiresByType image/png "access plus 1 month"  
ExpiresByType application/x-shockwave-flash "access plus 1 month"  
</IfModule>  

#使用mod_headers  
#在.htaccess文件中添加如下内容可以实现缓存:  
<ifmodule mod_headers.c>  
# htm,html,txt类的文件缓存一个小时  
<filesmatch "/.(html|htm|txt)$">  
header set cache-control "max-age=3600"  
</filesmatch>  
# css, js, swf类的文件缓存一个星期  
<filesmatch "/.(css|js|swf)$">  
header set cache-control "max-age=604800"  
</filesmatch>  
# jpg,gif,jpeg,png,ico,flv,pdf等文件缓存一年  
<filesmatch "/.(ico|gif|jpg|jpeg|png|flv|pdf)$">  
header set cache-control "max-age=29030400"  
</filesmatch>  
</ifmodule> 

这样可以压缩一般网页中会用到的html、xml、php、css、js等格式档案输出,虽然会占用掉服务器处理器的一点点处理器时间,浏览者在接收网页数据时也会消耗极短暂的一点点处理器时间,不过却可以大幅减少数据传输量,减少网络带宽被吃掉的情形。

DeflateCompressionLevel 9是指压缩程度的等级,从1到9,9是最高等级。据了解,这样做最高可以减少8成大小的传输量(看档案内容而定),最少也能够节省一半。

DeflateCompressionLevel 预设可以采用 6 这个数值,以维持耗用CPU处理器效能与网页压缩质量的平衡。

 

Apache 手册 节选,或查看 Apache 官网文档:Apache 2.4 Module mod_expires

ExpiresDefault ExpiresByType 指令同样能够用易懂的语法格式进行定义:

ExpiresDefault "<base> [plus] {<num> <type>}*"

ExpiresByType type/encoding "<base> [plus] {<num> <type>}*"

 

其中<base>是下列之一:

  • access
  • now (等价于'access ')
  • modification

plus 关键字是可选的。<num>必须是整数[可以被atoi() 接受的],<type>是下列之一:

  • years
  • months
  • weeks
  • days
  • hours
  • minutes
  • seconds

例如,下列3个指令都表示文档默认的有效期是一个月:

ExpiresDefault "access plus 1 month"

ExpiresDefault "access plus 4 weeks"

ExpiresDefault "access plus 30 days"

 

有效期可以通过增加"<num> <type>"子句进一步调整:

ExpiresByType text/html "access plus 1 month 15 days 2 hours"

ExpiresByType image/gif "modification plus 5 hours 3 minutes"

 

注意,如果你使用基于最后修改日期的设置,"Expires:"头将不会 被添加到那些并非来自于磁盘文件的内容。这是因为这些内容并不存在"最后修改时间"的属性。

HTTP头的Expires与Cache-control

1. 概念

Cache-control用于控制HTTP缓存(在HTTP/1.0中可能部分没实现,仅仅实现了Pragma: no-cache)

数据包中的格式:

Cache-Control: cache-directive

cache-directive可以为以下:

request时用到:

| "no-cache"
| "no-store"
| "max-age" "=" delta-seconds
| "max-stale" [ "=" delta-seconds ]
| "min-fresh" "=" delta-seconds
| "no-transform"
| "only-if-cached"
| "cache-extension"

response时用到:

| "public"
| "private" [ "=" < "> field-name < "> ]
| "no-cache" [ "=" < "> field-name < "> ]
| "no-store"
| "no-transform"
| "must-revalidate"
| "proxy-revalidate"
| "max-age" "=" delta-seconds
| "s-maxage" "=" delta-seconds
| "cache-extension"
部分说明:
根据是否可缓存分为
Public  指示响应可被任何缓存区缓存。
Private  指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的
部分响应消息,此响应消息对于其他用户的请求无效。
no-cache  指示请求或响应消息不能缓存(HTTP/1.0用Pragma的no-cache替换)
根据什么能被缓存
no-store  用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。
根据缓存超时
max-age  指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
min-fresh  指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
max-stale  指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以
接收超出超时期指定值之内的响应消息。
Expires 表示存在时间,允许客户端在这个时间之前不去检查(发请求),等同max-age的
效果。但是如果同时存在,则被Cache-Control的max-age覆盖。
格式:
Expires = "Expires" ":" HTTP-date
例如
Expires: Thu, 01 Dec 1994 16:00:00 GMT (必须是GMT格式

 
2. 应用
通过HTTP的META设置expires和cache-control
<meta http-equiv="Cache-Control" content="max-age=7200" />
<meta http-equiv="Expires" content="Mon, 20 Jul 2009 23:00:00 GMT" />
上述设置仅为举例,实际使用其一即可。这样写的话仅对该网页有效,对网页中的图片或其他请求无效,并不会做任何cache。
这样客户端的请求就多了,尽管只是检查Last-modified状态的东西,但是请求一多对浏览速度必定有影响。
如果要对文件添加cache可以通过apache的mod_expire模块,写法为
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 1 days"
</IfModule>
记得ExpiresActive设为On,我起先没设置On,似乎怎样YSlow都查不到缓存机制。这样添加的话就是默认所有的。
如果要针对个别MIME类型则可以:
ExpiresByType image/gif "access plus 5 hours 3 minutes"
见 Apache Module mod_expires
另外,当点击浏览器上的刷新,客户端发送的请求中均是max-age=0,表示validate操作,发送请求到服务器
要求检查cache,再更新cache,一般得到的是304 Not Modified,表示没变动。

 

 

参考文档:

实用 .htaccess 用法大全

WordPress:开启gzip压缩方法

Apache 启用 GZIP 压缩网页传输方法

Apache 2.4 Module mod_expires

前端优化设置较长的expires

Apache中关于页面缓存的设置

负载均衡环境中和如何设置Expires和Etag