Web 缓存是什么?为什么要使用缓存?
Web 缓存处于服务器(也称为源服务器)和客户端之间,监视请求并保存响应的副本,比如 HTML 页面,图片和文件等(统称为表述)。如果之后有对同一个 URL 的新请求,它会使用自己保存的内容来响应,而不是再次请求源服务器来获取内容。
使用 Web 缓存主要有下面两个原因:
减少延迟 —— 因为响应请求的内容来自缓存(距客户端较近)而不是源服务器,它会花较少的时间来获得表述并将他们呈现出来。这使得 Web 看起来具有良好的响应速度。
减少网络传输 —— 由于复用了表述,它可以减少客户端使用的带宽总量。如果客户需要为流量付费,这就意味着省钱。缓存会降低对带宽的要求,也降低处理难度。
Web 缓存的种类
浏览器缓存
你在查看现代 Web 浏览器(比如 IE、Safari 或 Mazilla)选项的时候,可能会看到“缓存”设置。这个选项让你配置一部分硬盘空间来保存你看过的表述。浏览器缓存的规则相当简单。它通常会在一次会话(即当前浏览器中第一次调用)中检查表述是否最新。
这个缓存在用户使用“回退”按钮或者点击一个浏览过的链接时会特别有用。而且,如果你在网站的各个页面中浏览相同的图片,他们几乎能马上从缓存中加载出来。
代理缓存
Web 代理缓存的工作原理相同,但规模更大。代理以同样的方式为成百上千的用户服务;大公司和 ISP 常常把代码缓存建立在防火墙之上,也可能是以独立设备的形式存在(也称为中间设备)。
代理缓存即不是客户端的一部分,也不是服务器的一部分,而是在网络之外,必须以某种方式把请求路由过去。其中一种方式是手工修改浏览器代理设备,指定要使用的代码;另一种方式是拦截。拦截式代理会根据其自身的基础网络重定向 Web 请求,不需要在客户端配置,客户端甚至不知道它们的存在。
代理缓存是一种共享缓存,通常不只是一个用户,而是大量用户在使用代理缓存。正因为如此,他们特别擅长降低延迟和网络传输量。这是因为众人都需要的表述会被多次重复使用。
网关缓存
网关缓存又名“反向代理缓存”或“替代缓存”。网关缓存也是一种中介,它他们不是由网络管理员部署以节约带宽,而是由网站管理员自己部署,使其站点更具伸缩性、可靠性以及拥有更好的性能。
很多方法都可以把请求路由到网关缓存,但常见的方法是使用负载均衡器让他们对于客户来说,看起来就跟源服务器一样。
Web缓存对我有坏处么?我为什么要帮助它们?
Web缓存是互联网中误解最深的技术之一。因为代理缓存可以隐藏使用网站的用户,所以网站管理员特别害怕失去对他们的站点的控制,这会使得他们很难去知道是谁在使用他们的站点。
然而不幸的是,即使没有Web缓存,网络上也有非常多的因素可以保证管理员精确的知道一个用户如何使用他们的站点。如果这是你非常关注的问题的话
另一个问题是,缓存可以提供已经失去时效性或者无效的数据给请求方。这篇手册将会演示如何配置你的服务端的服务来控制数据内容的缓存方式。
另一方面,如果你把站点规划的非常好,那么缓存将会使得站点的加载速度更快,同时也会减轻服务端和网络线路的负担。这两者之间的区别是非常显而易见的:一个没有使用缓存的站点可能需要数秒时间来完成页面的加载和展示,而另一个借助了缓存机制的网站可能在瞬间就完成了页面加载和展示。而用户会更喜欢加载速度迅速的站点,同时也会更加频繁的访问这些加载迅速的站点。
内容传递网络 (CDNs) 是一个非常有意思的发展产物,和通常的代理缓存不同的是,CDNs 的网关缓存和被缓存站点的关注点是一致的,所以上述的问题都得到了处理和解决。然而,即便你使用了 CDNs ,你仍然要知道在下游还是会存在代理和浏览器缓存的。
我们可以这样想一下:许多大型的互联网公司为了让他们的用户可以在使用其提供的服务时得到最快速的响应,会斥资数百万美元在世界范围内建立许多服务器机群来复制存储他们的数据内容。其实,缓存也能做这些事情,而且,相对来说缓存离最终的使用用户会更近。最好的一点是,你根本不用为此支付任何费用。
而事实是,不管你愿意与否,代理和浏览器缓存都会被使用在某个环节中。如果你没有正确的配置站点的缓存相关配置,站点数据将会按照默认的缓存管理员的配置被缓存下来。
Web缓存是如何工作的?
所有的缓存都有一系列用来决定什么时候从缓存中提供内容的规则。如果可能的话,其中的一些规则被放置在了协议中(HTTP 1.0和1.1),而另一些则由缓存的管理员(诸如浏览器缓存的用户,或者代理管理员)来设置。
通常情况下,下面列出的这些规则是最常用到的规则集(不用担心你不了解规则的详细嘻嘻,之后会详细地对这些规则作出解释):
如果响应的头部通知缓存不要保存当前响应内容,那么缓存就不会缓存当前响应。
如果是一个授权的或者加密的请求(例如HTTPS),那么共享缓存将不会保存相关数据内容。
在下述场景中,我们认为被缓存的内容是最新的(意味着不需要源服务端的检查就可以被发送给客户端),故而数据内容会直接从缓存中提供且不需要源服务端的校验:
缓存内容由过期时间或者其他的生存期控制机制,且缓存内容仍在生存有效期内; 如果缓存服务近期对外提供了数据内容,且该内容在很久之前就被修改了。如果内容已经过时了,源服务端会要求对其进行验证,或者通知缓存服务这份缓存的内容是否仍然有效。
在类似于网络中断这样的场景中,缓存可以对外提供过时的响应数据而不必和源服务器进行校验和确认。
如果在响应中没有相应的验证器( ETag 或者 Last-Modified 头部),且也没有明确的刷新信息,则这种数据通常但不总是的会被视为不可缓存的数据。
综合来看,刷新和验证是缓存可以正常有效的保存内容的最重要途径。新的数据内容可以可靠的快速的从缓存中得到,与此同时一个经过验证的表述则避免了在没有发生变更的情况下被再次完整的发送出去。
如何(以及如何不)控制缓存
有一些工具可以让网站设计人员和网站管理员来调整缓存对其站点的操作,这可能会使得服务器的配置发生变更,但是这些变更都是有价值的。对于如何在服务器上使用这些工具,在实现这一章节会详细阐述。
HTML Meta 标签和 HTTP 头信息
HTML 作者可以在文档的 <HEAD> 段中放置标签来描述一些属性。其中 meta 标签常用于确保文档不被缓存,或者在一定时间后过期。
meta 标签很容易使用,但效果不怎么样。这是因为只有部分浏览器缓存会遵从约定,代理缓存却不会(代理基本上不会去分析文档中的 HTML)。我们可以在 Web 页面中放置 Pragma: no-cache 这样的 meta 标签,但不要指望他一定会保持刷新。
另一方面,真正的 HTTP 头能让你很好的控制浏览器缓存和代理缓存对表述内容的处理。HTTP 头在 HTML 中看不到,它们通常由 Web 服务自动生成。不过你可以在一定程度上控制他们,这取决于你用的是什么服务器。你会在下面的部分看到 HTTP 头是多么有趣,还会了解到该如何把他们应用到网站上。
如果你的站点托管在 ISP 或者专门的托管服务商那里,他们不允许你自己设置到头重要的 HTTP 头(比如
Expires和Cache-Control),那就勇敢地提出抗议,因为你的工作需要这些功能。
HTTP 头在服务器发送 HTML 这前发送给浏览器,只有浏览器和中间的缓存能够看到。典型的 HTTP 1.1 响应头像下面这样:
HTTP/1.1 200 OK Date: Fri, 30 Oct 1998 13:19:41 GMT Server: Apache/1.3.3 (Unix) Cache-Control: max-age=3600, must-revalidate Expires: Fri, 30 Oct 1998 14:19:41 GMT Last-Modified: Mon, 29 Jun 1998 02:28:12 GMT ETag: "3e86-410-3596fbbc" Content-Length: 1040 Content-Type: text/html
HTML 会在紧跟在这些头信息之后,他们之间用一个空行隔开。在实现以了解到设置 HTTP 头相关的信息。
Pragma HTTP 头(以及为什么它没用)
很多人认为指定了 Pragma: no-cache HTTP 头可以避免表述被缓存。这并不一定是真的。HTTP 规范中没有任何关于 Pragma 响应头的规定,不过 Pragma 请求头(就是浏览器发送给服务器的头信息)却正在商讨中。虽然有一小部分缓存会遵从这个头信息,但大多数不会。你应该使用下面这些头信息代替它。
使用 Expires HTTP 头控制新近程度
Expires HTTP 头是控制缓存的基础方法,它告诉所有缓存与之相关的表述存在多久的保鲜期。那保鲜期之后,缓存应该检查源服务器,看文档是否被改变。几乎各种缓存都支持 Expires 头。
多数服务器允许你通过多种方法来设置 Expires 响应头。一般来说,他们可以设置绝对的过期时间,根据上次客户端取回表述时(最近访问时间)计算的时间,或者根据上次服务器文档修改时间计算的时间(最近修改时间)。
Expires 头特别适合缓存静态图像(比如导航栏和按钮),因为他们不会经常变化,你可以为他们设置一个非常长的过期时间,使你的站点具有更优势的响应性能。对于一些更新比较规律遥页面来说,他们也很有用。举例来说,如果你每天早晨 6:00 更新新闻页面,那就可以把表述内容设置在那个时间过期,然后缓存会知道什么时候该去获取更新的内容,而不需要用户去点击“刷新”。
Expires 头只支持 HTTP 日期值,任何其它值都会被认为“过去时”,结果表述不会被缓存。还要注意,HTTP 日期是格林威治(GMT)时间,而不是本地时间。
比如:
Expires: Fri, 30 Oct 1998 14:19:41 GMT
虽然 Expires 头很有用,但它也有一些局限。首先日期就是个麻烦事,Web 服务器和缓存上的时钟就必须同步。如果他们对时间的看法不一致就达不到预期的效果,因为缓存有可能认为已经过期的内容还在有效期内。
如果使用
Expires头就必须保证服务器时钟的准确性,这非常重要。要达到这个条件,有一个办法是使用网络时间协议(NTP),请向你身边的系统管理员了解关于 NTP 的知识。
使用 Expires 的另一个问题是容易忘记为某些内容设置了特定的过期时间。如果你没有在那之前更新 Expires,那么每个请求最终都会到服务器上去获取内容,既增加延迟,也会增加负载。
Cache-Control HTTP 头
HTTP 1.1 引入了一类新的头信, Cache-Control 响应头,让 Web 可以更方便地控制内容,避免 Expires 所具有的限制。
Cache-Control 响应头有如下一些:
max-age=[秒] — 指定表述内容的最大有效期。跟Expires类似,这个指令的时间是相对于请求时间,而不是绝对时间。[秒] 是从请求开始你期望表述过期前保持有效的总秒数。s-maxage=[秒] — 和max-age相似,但它只对共享(例如代理)缓存有效。public— 把通过认证的响应标记为可缓存。一般情况下,如果需要 HTTP 认证,响应会自动标记为私有的。private— 允许用户(比如一个浏览器)缓存响应,不允许共享缓存(比如代理)进行缓存。no-cache— 强制缓存将请求提交到原服务器进行验证后释放缓存副本,一次不落。这可以确保谨慎地对待认证(结合 public 标记),严谨地更新,同时又不牺牲缓存所带来的各种好处。no-store— 指示缓存在任何情况下都不保留表述的副本。must-revalidate— 告诉缓存,他们必须遵守你给他们关于内容更新的每一项信息。HTTP 允许缓存服务在一些特殊情况下认为表述过期,你可以通过指定这个头参数告诉缓存你希望它严格遵守你的规则。proxy-revalidate— 与must-revalidate相似,但它只对代理缓存有效。
举例说明:
Cache-Control: max-age=3600, must-revalidate
校验器和校验
在Web 缓存的工作原理一文中,我们说过在表示层发生变化时服务器和缓存使用校验进行通信。通过使用它,缓存可以避免在本地已有副本时下载整个表示层,但他们不确定它是否仍然是最新的。
校验器是非常重要的; 如果它不存在,并且没有任何新的信息( Expires 或 Cache-Control )可用,则缓存将根本不存储表示层。
最通用的校验器是头部的 Last-Modified,用来标识文档最近一次修改的时间。如果缓存存储了一个带有Last-Modified 头部的表示层,缓存可以借助一个 If-Modified-Since 请求向服务端确认当前缓存的表示层在最近一次修改后是否发生了变更。
HTTP1.1引入了一个新的叫做 ETag 的校验器。ETags是一个由服务端生成的唯一标识符,并且每当表示层发生变更时ETags的值都会发生变化。由于ETag是由服务端生成的,所以当缓存通过 If-None-Match 请求得知ETag在服务端匹配成功时,便可以确认缓存存储的表示层和服务端的内容是一致的,没有发生任何变化。
几乎所有的缓存都使用了最近一次修改时间来作为校验器,同时ETag校验器的使用也在逐步增长。
大多数的现代网站服务器会自动地为静态内容(例如文件)同时生成 ETag 和 Last-Modified 这两个校验器,这个过程不需要任何人为参与。然而,服务器在为诸如CGI、ASP或者数据库站点这样的动态内容生成ETag 和 Last-Modified 校验器时就显得力不从心了,具体可浏览编写缓存可感知的脚本。
关于构建一个缓存感知的站点的忠告
除了使用新鲜度信息和校验,还有一些其他的处理可以使得你的站点更利于缓存。
始终使用URL - 这一条是缓存的黄金原则。如果向不同的页面、不同的用户提供同样的数据内容,或者同样的内容来自于不同的站点,这时应该使用一致的URL。这是最简单、最有效的让站点更利于缓存的方法。例如,如果一旦在HTML代码段中使用"/index.html"作为一个资源引用,那么就一直按照这种方式坚持下去。
使用一个包含图片和其他元素的公共库,并在不同的地方引用他们。
使用缓存来存储图片和很少变更的页面,这个的实现可以借助一个设置了很大的值的
Cache-Control: max-age头部信息。通过一个精确的最大存活时间或者过期时间让缓存识别出更新频繁的页面。
如果资源(特别是可下载的文件)发生了变更,改变其名字。通过这种方式,可以让资源在未来的某个时间过期,同时也能保证当前版本仍然是有效的。唯一需要设置一个短的过期时间的部分就是链接到这些资源的页面。
在必要的情况下再修改文件。如果你这么做了,那么每个文件都会有一个不真实的距离当前时间更近的
Last-Modified值。举个例子,当准备更新站点时,不要复制整个站点文件进行更新,仅选择那些确实修改了的文件去执行更新操作。只在有需要的情况下使用cookie。cookies很难被缓存存储起来,并且在大多数场景都是没有必要的。如果必须用到cookie的话,那么也只在动态页面中使用cookie。
用REDbot检查页面文件。这个工具可以协助站点开发管理人员应用上文中讨论过的诸多原则。
编写缓存可感知的脚本
默认情况下,大多数脚本不会返回校验器(一个 Last-Modified 或 ETag 响应头)或新鲜度信息( Expires 或 Cache-Control )。虽然一些脚本确实是动态的(意味着它们为每个请求返回不同的响应),但许多(如搜索引擎和数据库驱动的网站)可以从此类缓存友好中受益。
一般来说,如果脚本生成的输出在以后的某个时间(无论是几分钟还是几天后)都可以使用相同的请求重现,那么它应该是可缓存的。如果脚本的内容仅根据 URL 中的内容而变动,则它是可缓存的; 如果其输出取决于cookie、身份认证信息或其他外部标准,则它可能不是可缓存的。
让脚本对缓存友好(同时有更好的表现)的最佳方式是只要脚本发生变化,就将其内容转储到一个普通文件中。Web 服务器会把这个文件跟其它 Web 页面同等对待,为其生成验证器,让一切变得简单。记住,只写内容变动过的文件,避免刷新新没有内容变动文件的
Last-Modified时间。还有一种方法可以让脚本在一定的限制条件下被缓存,即设置一个跟寿命相关的头。虽然用
Expires可以做到,但用Cache-Control: max-age可能更简单,它会按一定时间间隔刷新请求。如果这些办法都不适合你,那你需要用脚本生成一个验证器,然后响应
If-Modified-Since或If-None-Match请求。这个操作可以通过解析 HTTP 头之后,适当的响应304 Not Modified来实现。不过操作起来似乎不简单。
其它技巧:
不使用 POST,除非确有必要。多数缓存不会保存 POST 响应。如果你通过路径或查询(通过 GET)发送信息,缓存会保存这些信息以备将来使用。
不要在 URL 中嵌入用户特定的信息,除非生成的内容对每个用户都不同。
不要以为所有用户请求都来自同一台主机,因为也有可能来自缓存。
生成
Content-Length响应头。这很容易做到,而且它会允许通过长连接来响应脚本。这样一来,客户端可以在一个 TCP/IP 连接上请求多个表述,而不是为每个请求建立连接。这样你的网站看起来会更快。
FAQs (常见问题)
实现可缓存最重要的事情是什么?
一个好的策略是识别最流行、最大的表示层(尤其是图像)并首先在它们上使用。
我如何使用缓存尽可能快地加载页面?
最可缓存的表示层是具有较长新鲜时间集的那一部分。校验确实有助于减少查看表示层所花费的时间,但缓存仍然必须联系原服务器以查看它是否是最新的。 如果缓存已经知道它是最新的,它将被直接提供。
我知道缓存很棒,但我需要统计有多少人访问了我的页面!
如果在每次访问页面时你都必须知悉,在页面(或页面本身)上选择 ONE 小项,并通过为其提供适当的头信息使其不可缓存。例如,你参考每个页面上的 1x1 透明的不可缓存图像的逻辑。Referer 头将包含有关哪个页面调用它的信息。
请注意,即使这样也无法提供有关你用户的真实准确的统计信息,并且对互联网和你的用户是不友好的; 它会产生不必要的流量,并强迫人们等待下载完成未缓存项。
如何获取一个表示层的HTTP头部信息?
许多浏览器会通过一个“page info”或者一个小型的接口等方式向使用者提供 Expires 和 Last-Modified 的头部信息。如果可以的话,你会得到一个页面的菜单和关联在这个页面上的任何表示层(比如图像),以及这些表示层的详细信息。
如果想要看到表示层的所有HTTP头部信息,可能需要使用Telnet客户端手动连接到Web服务器上。
要想通过Telnet连接到服务器上,需要把端口号(默认80)作为一个独立的字段输入到连接到请求中,需要以如下的形式连接到服务器上: www.example.com:80 或者 www.example.com 80(以空格分隔) 。具体格式可以参考你使用的客户端文档。
一旦连接成功建立,就可以对任意表示层发起请求。例如,如果你想要获得 http://www.example.com/foo.html 的所有头部信息,连接到 www.example.com,端口号是 80,并且输入:
GET /foo.html HTTP/1.1 [return] Host: www.example.com [return][return]
每次当你看到 [return] 提示时,就按下回车键,确保在结束之前要按够两次回车。这个操作就会打印出对应的头部信息,紧随其后的是整个完成的表示层信息。如果只是想头部信息的话,用 HEAD 代替 GET 即可。
我的页面是受密码保护的,代理缓存怎么处理这种情况?
默认情况下,受HTTP身份认证保护的页面被视为是私有的,这些页面不会被共享缓存保存下来。然而,可以通过缓存控制(Cache-Control): public 头部使受身份认证保护的页面变成共有资源,适用于HTTP1.1的缓存就会将这些页面缓存下来。
如果你想让这些页面既能被缓存存储的同时又能继续保持其对每个用户的身份认证特性,就需要将 Cache-Control: public 和 no-cache 结合起来使用。这两个头部会要求缓存取出表示层数据返回给请求之前首先将用户的身份信息提交给服务器进行认证。命令格式如下:
Cache-Control: public, no-cache
不管上面的处理执行与否,最好的方式是最小化认证保护的使用范围。例如,如果图片数据是非敏感数据,那么将这些图片放在一个独立的文件夹目录下,然后在服务端配置不对这个文件夹目录做身份认证保护。这样,这些图片就可以被缓存存储起来了。
如果人们通过缓存访问我的站点,我需要担心安全性吗?
https:// 页面是不会被代理缓存缓存(或解密)的,因此你不必担心这一点。然而,因为缓存存储了从获取的 http:// 响应和 URL ,所以你应该对不安全的站点小心一些; 一个不道德的管理员可以随意地收集有关其用户的信息,尤其是在 URL 中。
实际上,在服务器和客户端之间的网络上的任何管理员都可以收集此类信息。一个特别的问题是 CGI 脚本将用户名和密码放在 URL 本身中; 这使得其他人找到并使用这些登录信息变得很简单。
如果你知道常见的 Web 安全性所存在的问题,那么代理缓存不应该有任何例外。
我正在寻找一个集成 Web 发布解决方案。哪一个是缓存可感知的?
这要看具体情况。一般来说,解决方案越复杂,缓存起来就越困难。最糟糕的是动态生成所有内容并不提供校验器的解决方案; 它们可能根本是不可缓存的。请与供应商的技术人员联系以获取更多信息,并参阅下面的实现说明。
我的图片在一个月以后才失效,但是我需要现在就在缓存中更新它们!
过期(Expires)头部是没有办法绕过去的,缓存内容会一直被使用直到缓存(不管是浏览器缓存还是代理缓存)耗尽了存储空间并且必须删除掉所有的表示层。
最有效的解决方法是改变任何指向它们的链接,这样,全新的表示层会从源服务端加载到客户端。记住,任何引用了这些表示层的页面也会被缓存保存下来。鉴于此,最好将静态图片和相似的表示层全都缓存下来,同时严格把控引用这些资源的HTML页面。
如果想要从某个指定的缓存中重新加载一个表示层,你既可以在使用缓存的时候强制加载(在Firefox中,按下shift的同时按下‘reload’将发出一个 Pragma: no-cache请求头部来完成这个操作),也可以让缓存管理员通过他们的接口删除掉这个表示层。
如何让用户在我负责维护的网站托管服务上发布缓存友好的页面?
如果你使用的的Apache,考虑下容许他们使用 .htaccess文件并且提供对应的文档。
否则,你可以建立一个事先决定好的区域,这个区域用来对应每个虚拟服务上的各种缓存属性。例如,可以指定一个 /cache-1m 目录来维护需要在访问后缓存一个月的缓存内容的相关信息,一个 /no-cache 目录通过使用头部信息标识那些不需要缓存存储来自其自身的表示层。
不管你能做什么,最好首先在缓存方面和你的最大的客户一起合作。大部分的节省(带宽和服务器负载方面)会从高流量站点中产生。
页面是可缓存的,但是浏览器在每次请求时都会重新请求这些页面。怎么样可以强制缓存存储这些表示层?
缓存不需要保留或者重用一个表示层,它只需要在某些情况下不保留或者使用它们。基于表示层的文件大小、类型(图片或者HTML文件)、以及它们需要占用的存储空间大小,缓存会决定哪些表示层会被保存下来。相对于更受欢迎或者更大的表示层,缓存认为你的内容可能不值得被缓存存储起来。
一些缓存机制允许管理员决定哪些表示层可以优先被缓存,而另外一些缓存机制则允许表示层可以被固定在缓存中,这样它们就总是有效的表示层。
实施说明 — Web 服务器
一般来说,不管你想部署到哪个 Web 服务器,最好都选择它最新的版本。新版本可能会包含更易于处理缓存的特性,而且通常还会在非常重要的安全和性能方面所有改进。
模块需要设置到 Apache 中。虽然模块被包含在发行包中,但默认情况下并未设置可用。想知道某个模块是否在服务器中可用,找到 https 二进制程序然后运行httpd -l。这个命令会打印出已经生效的模块列表(注意这只包含了随 Apache 编译的模块。在以后的 Apache 版本中,可以使用 httpd -M 把动态加载的模块也打印出来)。我们要找 expires_module 和 headers_module 这两个模块。
如果他们还未生效,你在有管理员权限的情况下可以重新编译 Apache 以包含他们。只需要在配置文件中找到恰当的行,取消掉他们的注释就行,也可以使用
-enable-module=expires和-enable-module=headers这两个参数来配置(1.3 或更高的版本)。欲知详情,请查阅 Apache 发行的 INSTALL 文件。
一旦你的 Apache 包含了正确的模块,就可以使用 mod_expires 来指定表述到期的时间,这个配置写在 .htaccess 文件或者服务器的 asccess.conf 文件中都行。你可以按访问时间或修改时间来指定过期时间,可以将其应用于某个文件类型,也可以用作默认配置。
要应用Cache-Control头,你需要使用mod_headers模块,该模块允许你为某资源指定任意HTTP头。 请参阅mod_headers文档。
这是一个示例.htaccess文件,展示了一些头的使用。
.htaccess文件允许Web发布者使用通常仅在配置文件中可找到的命令。它们会影响它们所在目录及其子目录的内容。与服务器管理员沟通下,了解它们是否已被启用。
### activate mod_expires ExpiresActive On ### Expire .gif's 1 month from when they're accessed ExpiresByType image/gif A2592000 ### Expire everything else 1 day from when it's last modified ### (this uses the Alternative syntax) ExpiresDefault "modification plus 1 day" ### Apply a Cache-Control header to index.html <Files index.html> Header append Cache-Control "public, must-revalidate" </Files>
注意mod_expires会在合适时机自动计算并插入一个
Cache-Control:max-age头。
Apache 2的配置和1.3版本的非常类似;更多信息请参考2.2mod_expires和mod_headers文档。
微软的IIS
Microsoft的Internet Information Server以一种灵活的方式使得设置头信息变得非常容易。请注意,这仅适用于服务器的第4版,该版本仅在NT Server上运行。
要指定站点区域的头信息,请在“Administration Tools”界面中选择它,然后调出其属性。在选择HTTP Headers选项卡之后,你应该看到两个有趣的区域; Enable Content Expiration和Custom HTTP headers。第一个区域应该是不言自明的,第二个可以用于适用于Cache-Control头。
有关在Active Server Pages中设置头的资料,请参阅下面的ASP部分。也可以从ISAPI模块设置头; 有关详细信息,请参考MSDN。
Netscape/iPlanet企业服务器
从版本3.6开始,企业服务器不再提供任何显式的方法来设置Expires头。但是,它从3.0开始就支持HTTP 1.1功能。这意味着HTTP 1.1缓存(代理和浏览器)将能够利用你所做的缓存控制配置。
要使用Cache-Control头,请选择管理服务器中的Content Management | Cache Control Directives。 然后,使用资源选择器,选择要设置头的目录。
实现附注— 服务器端脚本
由于服务器端脚本的重点在于动态内容,因此即使是对可缓存的内容,也不会生成可缓存的页面。如果你的内容经常变动,但不是每次点击都会变动,请考虑设置Cache-Control:max-age头; 大多数用户在相对较短的时间内再次访问该页面。例如,当用户点击“后退”按钮时,如果没有任何校验器或新鲜度信息可用,则他们必须等到从服务器重新下载页面之后才能看到它。
要记住的一件事是,使用Web服务器时设置HTTP比在脚本语言中设置HTTP标头更容易。都尝试下。
CGI
CGI脚本是最常用的生成内容的方式之一。你可以通过在发送正文之前添加HTTP响应头轻松得附加到HTTP响应头上;大多数CGI实现已经要求你为Content-Type头执行此操作。例如,在Perl中;
#!/usr/bin/perl print "Content-type: text/html\n"; print "Expires: Thu, 29 Oct 1998 17:04:19 GMT\n"; print "\n"; ### the content body follows...
既然它全是文本,你可以简单地使用内置函数生成Expires以及其他日期相关的头信息。如果你使用Cache-Control: max-age将会更简单;
print "Cache-Control: max-age=600\n";
这将使此脚本在请求后可缓存10分钟,因此如果用户点击“后退”按钮,他们将不会重新提交请求。
CGI规范还使得在脚本环境中客户端所发送的请求头可用; 每个标头的名称前面都有“HTTP_”。因此,如果客户端发出If-Modified-Since请求,它将显示为HTTP_IF_MODIFIED_SINCE。
Server Side Includes
SSI(通常与扩展名为.shtml的一起使用)是Web发布者能够将动态内容添加到页面中的首选方式之一。通过在页面中使用特殊标记,可以使用有限形式的HTML内脚本。
大多数SSI实现都没有设置校验器,因此SSI是不能缓存。 但Apache的实现允许用户通过在适当的文件上设置组执行权限并结合XbitHack full指令来指定可以缓存哪些SSI文件。