|
|
![]() WORKSHOP
Caching for Web Authors and Webmasters
What's a Web Cache? Why do people use them? There are two main reasons that people use Web caches:
Aren't Web Caches bad for me? Why should I help them? Unfortunately for them, even if no Web caches were used, there are too many variables on the Internet to assure that they'll be able to get an accurate picture of how users see their site. If this is a big concern for you, this document will teach you how to get the statistics you need without making your site cache-unfriendly. Another concern is that caches can serve content that is out of date, or stale. However, this document can show you how to configure your server to control this, while making it more cacheable. On the other hand, if you plan your site well, caches can help your Web site load faster, and save load on your server and Internet link. The difference can be dramatic; a site that is difficult to cache may take several seconds to load, while one that takes advantage of caching can seem instantaneous in comparison. Users will appreciate a fast-loading site, and will visit more often. The fact is that caches will be used whether you like it or not. If you don't configure your site to be cached correctly, it will be cached using whatever defaults the cache's administrator decides upon. Kinds of Web Caches
Browser Caches This cache is useful when a client hits the 'back' button to go to a page they've already seen. Also, if you use the same navigation images throughout your site, they'll be served from the browser cache almost instantaneously.
Proxy Caches Because proxy caches usually have a large number of users behind them, they are very good at reducing latency and bandwidth use. That's because popular objects are requested only once, and served to a large number of clients. Most proxy caches are deployed by large companies or ISPs that want to reduce the amount of Internet bandwidth that they use. Because the cache is shared by a large number of users, there are a large number of shared hits (objects that are requested by a number of clients). Hit rates of 50% efficiency or greater are not uncommon. Proxy caches are a type of shared cache.
How Web Caches Work Generally speaking, these are the most common rules that are followed for a particular request (don't worry if you don't understand the details, it will be explained below):
How (not) to Control Caches
HTML Meta Tags vs. HTTP Headers Meta tags are easy to use, but aren't very effective. That's because they're usually only honored by browser caches (which actually read the HTML), not proxy caches (which almost never read the HTML in the document). While it may be tempting to slap a Pragma: no-cache meta tag on a home page, it won't necessarily cause it to be kept fresh, if it goes through a shared cache. On the other hand, true HTTP headers give you a lot of control over how both browser caches and proxies handle your objects. They can't be seen in the HTML , and are usually automatically generated by the Web server. However, you can control them to some degree, depending on the server you use. In the following sections, you'll see what HTTP headers are interesting, and how to apply them to your site.
HTTP headers are sent by the server before the HTML, and only seen by the client, and any intermediate caches. Typical HTTP 1.1 response headers might look like this:
HTTP/1.1 200 OKThe HTML document would follow these headers, separated by a blank line.
Pragma HTTP Headers (and why they don't work)
Expires HTTP Header Most Web servers allow you to set Expires response headers in a number of ways. Commonly, they will allow setting an absolute time to expire, a time based on the last time that the client saw the object (last access time), or a time based on the last time the document changed on your server (last modification time). Expires headers are especially good for making static images (like navigation bars and buttons) cacheable. Because they don't change much, you can set extremely long expiry time on them, making your site appear much more responsive to your users. They're also useful for controlling caching of a page that is regularly changed. For instance, if you update a news page once a day at 6am, you can set the object to expire at that time, so caches will know when to get a fresh copy, without users having to hit 'reload'. The only value valid in an Expires header is a HTTP date; anything else will most likely be interpreted as 'in the past', so that the object is uncacheable. Also, remember that the time in a HTTP date is Greenwich Mean Time (GMT), not local time.
For example: Although the Expires header is useful, it is still somewhat limited; there are many situations where content is cacheable, but the HTTP 1.0 protocol lacks methods of telling caches what it is, or how to work with it. HTTP 1.1 introduces a new class of headers, the Cache-Control response headers, which allow Web publishers to define how pages should be handled by caches. They include directives to declare what should be cacheable, what may be stored by caches, modifications of the expiration mechanism, and revalidation and reload controls. Interesting Cache-Control response headers include:
For Example: If you plan to use the Cache-Control headers, you should have a look at the excellent documentation in the HTTP 1.1 draft; see References and Further Information . Validators and ValidationIn How Web Caches Work, we explained that validators are used by servers and caches to communicate when an object has changed. By using them, caches avoid having to download the entire object when they already have a copy locally, but they're not sure if it's still fresh. Validators are very important, because if at least one isn't present, most caches will not store an object at all. The most common validator is the time that the document last changed, the Last-Modified time. When a cache has an object stored that includes a Last-Modified header, it can use it to ask the server if the object has changed since the last time it was seen, with an If-Modified-Since request. HTTP 1.1 introduced a new kind of validator called the ETag. Etags are unique identifiers that are generated by the server and changed every time the object does. Because the server controls how the ETag is generated, caches can be surer that if the ETag matches when they make a If-None-Match request, the object really is the same. Almost all caches use Last-Modified times in determining if an object is fresh; as more HTTP/1.1 caches come online, Etag headers will also be used. Most modern Web servers will generate both ETag and Last-Modified validators for static content automatically; you won't have to do anything. However, they don't know enough about dynamic content (like CGI, ASP or database sites) to generate them; see Writing Cache-Aware Scripts. Tips for Building a Cache-Aware Site
Writing Cache-Aware Scripts To do this, you'll need to make the script generate either validator (or both, preferably), and then respond to If-Modified-Since and/or If-None-Match requests with a 304 Not Modified response, when appropriate. This can be done by parsing the HTTP headers (which are available in the CGI environment), and then appending HTTP headers (along with Content-type) at the start of the response. If this isn't practical, consider whether you can use an Expires header for even a short amount of time. Remember that if you can represent content as plain files, you should use them; the Web server takes care of generating validators and responding to conditional requests automatically, and it makes your life easier. Pre-generating pages from a script is often the most practical way to make a site perform better, and be cache-friendly. Some other tips;
Q. What are the most important things to make cacheable?
Q. I understand that caching is good, but I need to keep statistics on
how many people visit my page! Be aware that even this will not give truly accurate statistics about your users, and is unfriendly to the Internet and your users; it generates unnecessary traffic, and forces people to wait for that uncached item to be downloaded. For more information about this, see On Interpreting Access Statistics in the references.
Q. I've got a page that is updated often. How do I keep caches from
giving my users a stale copy? For example, if your site's home page changes every day at 8am, set the Expires header for 23 hours after the last modification time. This way, your users will always get a fresh copy of the page. See also the Cache-Control: max-age header.
Q. How can I see which HTTP headers are set for an object? To see the full headers of an object, you'll need to manually connect to the Web server. Using a Telnet client, open the Web server port. Depending on what program you use, you may need to type the port into a separate field, or you may need to connect to www.myhost.com:80 or www.myhost.com 80 (note the space). Consult your Telnet client's documentation. Once you've opened a connection to the site, type a request for the object. For instance, if you want to see the headers for http://www.myhost.com/foo.html, connect to www.myhost.com, port 80, and type:
GET /foo.html HTTP/1.1 [return] Press the Return key every time you see [return]; make sure to press it twice at the end. This will print the headers, and then the full object. To see the headers only, substitute HEAD for GET.
Q. My pages are password-protected; how do proxy caches deal with
them? If you'd like the pages to be cacheable, but still authenticated for every user, combine the Cache-Control: public and must-revalidate headers. This tells the cache that it must submit the new client's authentication information to the origin server before releasing the object from the cache. Whether or not this is done, it's best to minimize use of authentication; for instance, if your images are not sensitive, put them in a separate directory and configure your server not to force authentication for it. That way, those images will be naturally cacheable.
Q. Should I worry about security if my user access my site through a
cache? In fact, any administrator on the network between your server and your clients could gather this type of information. One particular problem is when CGI scripts put usernames and passwords in the URL itself; this makes it trivial for others to find and use their login. If you're aware of the issues surrounding Web security in general, you shouldn't have any surprises from proxy caches.
Q. I'm looking for an integrated Web publishing solution. Which ones
are cache-aware?
Q. My images expire a month from now, but I need to change them in the
caches now! The most effective solution is to rename the files; that way, they will be completely new objects, and loaded fresh from the origin server. Remember that the page that refers to an object will be cached as well. Because of this, it's best to make static images and similar objects very cacheable, while keeping the HTML pages that refer to them on a tight leash. If you want to reload an object from a specific cache, you can either force a reload (in Netscape holding down 'shift' while pressing 'reload' will do this, by issuing a Pragma: no-cache request header) while using the cache. Or, you can have the cache administrator delete the object through their interface.
Q. I run a Web Hosting service. How can I let my users publish
cache-friendly pages? Otherwise, you can establish predetermined areas for various caching attributes in each virtual server. For instance, you could specify a directory /cache-1m that will be cached for one month after access, and a /no-cache area that will be served with headers instructing caches not to store objects from it. Whatever you are able to do, it is best to work with your largest customers first on caching. Most of the savings (in bandwidth and in load on your servers) will be realized from high-volume sites.
A Note About the HTTP
Implementation Notes - Web Servers
Apache 1.3 The modules need to be built into Apache; although they are included in the distribution, they are not turned on by default. To find out if the modules are enabled in your server, find the httpd binary and run httpd -l; this should print a list of the available modules. The modules we're looking for are mod_expires and mod_headers.
Once you have an Apache with the appropriate modules, you can use mod_expires to specify when objects should expire, either in .htaccess files or in the server's access.conf file. You can specify expiry from either access or modification time, and apply it to a file type or as a default. See http://www.apache.org/docs/ for more information, and speak with your local Apache guru if you have trouble. To apply Cache-Control headers, you'll need to use the mod_headers module, which allows you to specify arbitrary HTTP headers for a resource. See http://www.apache.org/docs/ Here's an example .htaccess file that demonstrates use of some headers.
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>
Netscape Enterprise 3.6 To use Cache-Control headers, choose Content Management | Cache Control Directives in the administration server. Then, using the Resource Picker, choose the directory where you want to set the headers. After setting the headers, click 'OK'. For more information, see Netscape Enterprise Server Documentation
Microsoft IIS 4.0 To specify headers for an area of a site, select it in the Administration Tools' interface, and bring up its properties. After selecting the HTTP Headers tab, you should see two interesting areas; Enable Content Expiration and Custom HTTP headers. The first should be self-explanatory, and the second can be used to apply Cache-Control headers. It is also possible to set headers from ISAPI modules; refer to MSDN for details.
Lotus Domino Server 4.6 Even if this limitation is overcome, Notes' habit of referring to the same object by different URLs (depending on a variety of factors) bars any measurable gains. There is also no documented way to set an Expires header. Because of all of this, Domino servers can seem quite slow. Version 5 of the server is in beta testing as of this writing, and claims to address some of these concerns. From preliminary testing, it appears that while some gains have been made, there still aren't any controls available to developers to fine-tune how their pages are cached.
Implementation Notes - Server-Side Scripting One thing to keep in mind is that it may be easier to set HTTP headers with your Web server rather than in the scripting language, depending on both implementations.
Server Side Includes Most implementations of SSL do not set validators, and as such are not cacheable. However, Apache's implementation does allow users to specify which SSI files can be cached, by setting the group execute permissions on the appropriate files, combined with the XbitHack full directive. For more information, see http://www.apache.org/docs/
PHP 3 By default, objects processed by PHP are not assigned validators, and are therefore uncacheable. However, developers can set HTTP headers by using the Header() function. For example, this will create a Cache-Control header, as well as an Expires header three days in the future:
<?php Remember that the Header() function MUST come before any other output. As you can see, you'll have to create the HTTP date for an Expires header by hand; PHP doesn't provide a function to do it for you. Hopefully, a more convenient method will be incorporated into PHP soon. For more information, see http://www.php.net/manual/function.header.php3
Cold Fusion 4.0 Cold Fusion makes setting arbitrary HTTP headers relatively easy, with the CFHEADER tag. Unfortunately, setting date-related functions in Cold Fusion isn't easy as Allaire's documentation leads you to believe; their example for setting an Expires header, as below, won't work. <CFHEADER NAME="Expires" VALUE="#Now()#"> It doesn't work because the time (in this case, when the request is made) doesn't get converted to a HTTP-valid date; instead, it just gets printed as a representation of Cold Fusion's Date/Time object. Most clients will either ignore such a value, or convert it to a default, like January 1, 1970. Even worse, Cold Fusion's date formatting functions aren't flexible enough to generate a date that is HTTP-valid. That means that you'll either have to write your own, or wait until Allaire fixes this problem. Of course, you can still use the CFHEADER tag to set Cache-Control and other headers. Also, Remember that Web server tags are passed through with some implementations (such as CGI); check yours to determine whether you can use this to your advantage, by setting headers on the server instead of in Cold Fusion.
ASP <% Response.Expires=1440 %> specifying the number of minutes from the request to expire the object. Likewise, absolute expiry time can be set like this (make sure you format HTTP date correctly): <% Response.ExpiresAbsolute=#May 31,1996 13:30:15 GMT# %> Cache-Control headers can be added like this: <% Response.CacheControl="public" %>
References and Further Information
Cache Now! Campaign
HTTP 1.1 Specification
Web Caching Overview
On Interpreting Access Statistics
About This Document All trademarks within are property of their respective holders. Although the author believes the contents to be accurate at the time of publication, no liability is assumed for them, their application or any consequences thereof. If any misrepresentations, errors or other need for clarification is found, please contact the author immediately. The latest copy of this document can always be obtained from http://www.pobox.com/~mnot/cache_docs/ © Internet Technical Group Last update: Mar 6, 1999 URL: http://www.sandia.gov/itg/newsletter/mar99/workshop_proxies.html hosted by Sandia National Labs Disclaimer: Neither Sandia Corporation, the United States Government, nor any agency thereof, nor any of their employees makes any warranty, express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness of any information, apparatus, product, or process disclosed, or represents that its use would not infringe privately-owned rights. Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, or favoring by Sandia Corporation, the United States Government, or any agency thereof. The views and opinions expressed herein do not necessarily state or reflect those of Sandia Corporation, the United States Government or any agency thereof. |