Effective Use of Caching to Boost VoiceXML Application Performance

Just as HTML has simplified the process of developing and deploying visual applications, VoiceXML has done the same for voice applications. The VoiceXML language is simple to learn, so you can develop applications rapidly. Additionally, because VoiceXML interpreters use the HTTP protocol to fetch VoiceXML documents and other resources such as audio, grammars, scripts, and data, applications are easy to deploy and to update.

When designing and building a voice application, however, there are some techniques you should employ to make sure that it is as responsive to the user as possible. This document focuses on techniques pertinent to caching by first explaining its significance and then by going into detail about how to leverage it in your voice applications.

The most significant way for you to improve the performance of your voice application is to allow it to be cacheable. A cache is a repository used to store previously fetched resources, also known as objects. Caching provides two main benefits:

  • Your voice application will be more responsive to the user because the VoiceXML interpreter will get access to your content more quickly. A responsive application is a key facet to increased customer satisfaction.
  • The load on your Web servers is reduced. You'll need fewer of them, and that will save you money.

Consider your Web browser. As you surf the Web, your browser caches on your personal computer the pages and referenced objects you request - HTML text, images, and client-side scripts. If you request an object more than once, the browser determines if it can use the version in the cache or if it needs to fetch a new version across the network. It achieves this first by checking the expiration date and time of the object as specified via the HTTP response headers that the server sent with the content. If the server did not specify the expiry data, or the data indicates that the content is stale, the browser sends a conditional request to the server to determine if the object has changed since the original request date. If-Modified-Since (IMS) and If-None-Match are two ways of accomplishing this validation with the server. Only if the object has been modified does the server send an updated version. Otherwise, the server returns only a status code (304) indicating that the object has not changed.

Just as a Web browser caches the HTML documents and other objects that make up a Web application, the Tellme VoiceXML interpreter uses a local cache, also known as an 'end cache', that stores the objects that make up a voice application - VoiceXML documents, audio files, client-side scripts, grammars, and data.

In addition, the Tellme Voice Application Network employs an intermediary layer of caching, also known as a 'proxy cache'. All VoiceXML interpreters on the Tellme Voice Application Network are instructed to make HTTP requests through a proxy cache, further reducing network bandwidth requirements and minimizing latency by sharing cached objects with multiple VoiceXML interpreters.

The existence of these caches, however, doesn't guarantee that the VoiceXML interpreter can take advantage of them. The utility of these caches depends in large part on the way you design and implement your voice applications and on how you configure the Web servers that host your voice applications.

When it comes to caching, you need to understand two fundamental principles: freshness and validation. A cached object is considered fresh if it can be served from the cache instantly without checking with the server. If an object is not fresh, it is considered stale. In that case, the cache contacts the originating HTTP server to determine whether or not the object has changed. This is called validation. The server only returns an updated version of the object if it has a later version than the cache.

A cache applies a set of rules each time a client requests an object. The following is a list of the common ones:

  1. If the HTTP headers transmitted with the object instruct the cache not to store that object, the cache respects that instruction.
  2. If the object is transmitted using authentication or a secure protocol such as SSL, the end cache may cache it, but the proxy cache won't.
  3. An object is fresh if its expiration date and expiration time has not elapsed.
  4. If an object is stale, the cache asks the originating server to validate the object. If the object is deemed invalid, the server sends a new copy as part of the same transaction.

HTTP is a request-response protocol. The client, whether it is a Web browser or a VoiceXML interpreter, requests an object from a Web server, and the server sends a response. A response consists of a set of HTTP headers and possibly some content. To determine freshness and to perform validation, the cache relies on information transmitted by the HTTP server along with the content. For example, consider the following HTTP response headers:

HTTP/1.1 200 OK
Date: Fri, 08 Feb 2002 06:00:15 GMT
Server: Apache/1.3.19 (Unix)
Last-Modified: Fri, 08 Feb 2002 03:00:00 GMT
ETag: "15dbae-3ed-3c634aa0"
Expires: Sat, 09 Feb 2002 06:00:15 GMT
Accept-Ranges: bytes
Content-Length: 1005
Connection: close
Content-Type: text/xml

If you're curious about the details of each of these headers, consult Section 14 of the HTTP 1.1 specification, Header Field Definitions. For the purposes of caching, the most important of these headers are the Date, Expires, Last-Modified, and ETag headers.

A server sends an Expires header to a client to indicate that an object is fresh until the specified date and time. After that time the client must check with the origin server to see if a newer version of the object is available. The time associated with the Expires header should be specified in Greenwich Mean Time (GMT). In this example, the header indicates to the cache that the object will expire 24 hours after the content access time (Date header).

For more information about the Expires header, please see Section 14.21 of the HTTP/1.1 specification.

An ETag is a unique identifier calculated by the HTTP server when an object is served. Calculation of this value is server-dependent, but it is typically based on the file's location in the file system, the modification time of the file, and the file size. The proxy cache sends along with the If-None-Match request header the ETag it received when it last cached the object. The server recalculates the ETag for the requested object and returns a fresh copy of the object if the values do not match.

Note. High volume Web sites attain increased reliability by configuring multiple Web servers to serve the same content via one or more load-balancers. It is vital that your Web servers generate equivalent ETag values for identical content. Please verify this with your Web server administrator or review your Web server configuration documentation for details on configuring ETag header generation.

For more information about the ETag header, please see Section 14.19 of the HTTP/1.1 specification.

Suppose the interpreter requests a VoiceXML document from your server for the first time on Friday, February 8, 2002 at 06:00 GMT. The proxy cache receives the request, doesn't find the document in its cache, and forwards the request to your server. In response, the server returns the above HTTP headers along with the content to the proxy cache. The proxy cache notices the Expires header and stores the document in its local cache, along with the values of the Date, Last-Modified, and ETag headers and the current date and time. The proxy cache forwards the complete set of HTTP headers and the content to the VoiceXML interpreter which proceeds to store the same information in its local cache.

Given that your VoiceXML document is now cached, suppose that a different user calls your application on Friday, February 8, 2002, at 06:30:30 GMT resulting in a request for the same document. In addition, assume that the same VoiceXML interpreter is asked to receive the call. The interpreter knows that it has the document in its cache, so it performs a freshness calculation according to Section 13.2.4 of the HTTP/1.1 specification. Since Fri, 08 Feb 2002 06:30:30 GMT is less than Fri, 09 Feb 2002 06:00:15 GMT - the expiration date as defined by the Expires header, the page is considered fresh and will be loaded from the cache without having to fetch the original document from the server across the Internet.

If a different interpreter had received the call, it would not have found the document in its local cache. When it requested the document from the origin server, however, the proxy cache would have intercepted the request, performed the Expires calculation, and returned its copy of the document.

Suppose a day goes by, and a user calls your application on Saturday, February 9, 2002, at 09:00:00 GMT. The document is now stale according to the freshness calculation. The VoiceXML interpreter must contact the origin server to see if a newer VoiceXML document is available. The interpreter sends an If-Modified-Since check using the value of the Last-Modified header stored in the cache to see if the document has been modified. The proxy cache performs the same check and reaches the same conclusion. If an ETag is available, the proxy cache uses it to formulate an If-None-Match check. If not, it performs an If-Modified-Since check.

If the document has not been modified, the Web server will return an HTTP 304 response code. The proxy cache returns this response to the interpreter, and the interpreter uses the version of the document in the cache thus eliminating the need to refetch the entire document from your Web server and the need to reparse it.

If, on the other hand, the Web server determines that the document has been modified, it returns the latest version of the document to the proxy cache which returns the new version to the interpreter, and both the proxy and local caches are updated with the latest HTTP headers and content.

Note. If the Web server had not included an Expires header with the VoiceXML document, the interpreter would have to validate the content with the Web server via the proxy cache each time the document is needed by the application. Even if the Web server returns an HTTP 304 response every time, the cost of setting up and tearing down an HTTP transaction can add up, especially if the Web server and VoiceXML interpreters are far apart.

While all this may sound complicated, this is the kind of logic and computation for which machines are well suited, and the savings is substantial, especially if the objects that comprise your voice application can be cached for long periods of time.

Like Web applications, VoiceXML applications typically consist of two types of content: static and dynamic. Static content includes VoiceXML documents, audio files, command grammars, and client-side JavaScript. Once deployed, this content changes infrequently. Dynamic content, on the other hand, changes frequently, oftentimes with each request. Dynamic content is generated using a server-side framework such as Perl CGI, Java Server Pages (JSP), or Microsoft Active Server Pages (ASP). You use these frameworks to bind data from your backend database to the user interface of your application. Such pages are rarely cacheable.

The full range of caching features available with HTTP/1.1 can be applied to each of the documents and resources that comprise your application. Given, however, that this can be a tremendous amount of error-prone work, consider the following alternative caching strategy:

  • Structure your application so that it can be easily versioned in its entirety. Specific instructions on how to accomplish this are provided below.
  • Rather than determining how long each file in your application can be cached, consider only whether or not it can be cached at all.
  • If a document can be cached, set the Expires header for a year later than the time the document is requested by the client.

Warning. You should only set the Expires header to a long interval (e.g. year) if you adopt the versioning strategy described in this document.

Although the caching strategy suggested here will impose specific requirements on how you structure and update your application, you'll be able to maintain your application more easily while simultaneously achieving optimal caching behavior. If you apply an alternative caching strategy, the interdependencies between the components of your application will introduce additional complexity in coordinating expiry times with deployment. Failed coordination is likely to result in unexpected application behavior at runtime.

To provide some context to help you understand and embrace the version-based caching strategy, consider a hypothetical brokerage application. It has a main menu that plays some prompts, references a command grammar, and provides transitions to various modules including signin, stock quotes, portfolio, and balances. The menu rarely changes, and neither do the main dialogs in each of the subordinate modules. Once the user provides input to any of these subordinate modules, however, the application makes a request to the server for up-to-the-minute, personalized information.

Voice applications have a single entry point. When Tellme provisions a telephone number to service your application, it directs that number to a single URL. With the exception of this entry point, you want the objects that comprise your application to be cacheable for as long as possible. When organizing your application's directory structure, place a stub entry point in the top-level directory. The other objects that comprise your application should be placed in a version-specific directory.

A logical way to organize the physical documents that comprise the brokerage application follows.

The code for the application entry point is located in a top-level directory. The main menu and application root document are located in a version-specific directory. The individual modules, shared Javascript ("js"), common audio ("vui"), and CGI scripts are located in subdirectories below the version-specific directory. Each of these directories may contain additional subdirectories.

The application entry point document should not be cached. The entry point serves as a stub to facilitate updates and merely navigates to the true starting point of your application. In the hypothetical brokerage application, the entry point navigates to a cacheable document containing the main menu.

Code for a generic application entry point document follows:

<vxml version="2.1" application="./yyyy-mm-dd-hhmm/app_root.vxml">
   <form id="main">
      <block>
         <!-- jump to main menu -->
         <goto next="./yyyy-mm-dd-hhmm/index.vxml"/>
      </block>
   </form>
</vxml>

Observe that the paths to the application root document and the main menu specify a directory that includes the date and time. Usage of the date and time is a simple convention for naming the version-specific directory that allows you to examine the debug logs to verify that the VoiceXML interpreter is retrieving the correct content.

To make the document that corresponds to your application entry point non-cacheable, configure your HTTP server to send the Cache-Control HTTP header with the value no-cache.

Cache-Control: no-cache

To faciliate caching and to ease the update process, the documents in your application should use relative paths to reference the objects located on the same server as your application entry point.

Consider the main document of the quotes module, index.vxml, located in the quotes directory below the version-specific directory. It should also reference the application root document using a relative path. In addition, it uses a relative path when navigating back to the application's main menu. By using relative paths, you need not modify any path references within your content other than those in the application entry point when it comes time to upgrade your application to a new version.

<vxml version="2.1" application="../app_root.vxml">
   <link event="vtrader.onmainmenu">
      <grammar mode="voice"
               root="root_rule"
               tag-format="semantics/1.0"
               version="1.0"
               xml:lang="en-US"
               xmlns="http://www.w3.org/2001/06/grammar">
        <rule id="root_rule" scope="public">
          <one-of>
            <item>
              main menu
            </item>
          </one-of>
        </rule>
      </grammar>
   </link>
   <catch event="vtrader.onmainmenu">
      <goto next="../index.vxml"/>
   </catch>

   <form id="quotes">
      <field name="equity">
        <prompt>Say a company name</prompt>
        <!-- more code to get a company name -->
      </field>
   </form>
</vxml>

By following the caching strategy described in this document, all of your application's resources are cacheable with the exception of the following:

  • Your application entry point.
  • Your server-side scripts.
  • Resources sent by your HTTP server accompanied by one or more Set-Cookie headers.
  • Resources transmitted using authentication or a secure protocol such as SSL. While the end cache may cache these resources, the proxy cache does not.

You should examine each of your server-side scripts to determine how frequently the generated content changes. If the generated content is constant, the script is cacheable. If the script accepts parameters, but the content generated by the script doesn't change, be sure to use the HTTP GET method when invoking the script. Scripts invoked using the HTTP POST method are not cacheable.

To ensure that a non-cacheable server-side script requested using the HTTP GET method does not get cached, make sure your script generates the appropriate HTTP headers. For example, the following Perl CGI code sends the Content-Type, Pragma, and Cache-Control HTTP headers to the client. The latter two headers request that the client browser not cache the content.

#!/usr/local/bin/perl -w

my $rand = int(rand()*100);

print <<EOF;
Content-Type: text/xml
Pragma: no-cache
Cache-Control: no-cache

<?xml version="1.0"?>
<vxml version="2.1">
<form>
   <block>The answer is $rand</block>
</form>
</vxml>
EOF

If, on the other hand, you determine that a server-side script is cacheable, your script should check the incoming HTTP request headers for the If-Modified-Since HTTP request header and respond appropriately.

Note. Your script will only receive an IMS request if you included a Last-Modified response header in a previous HTTP request.

The following Perl CGI checks the HTTP_IF_MODIFIED_SINCE environment variable. This contains the value of the If-Modified-Since header if it was sent by the client browser. The HasChanged compares this date with that of the modification time of the data file. If the data hasn't changed since the last request, the script returns a 304 HTTP response code. If it has, the script returns the latest data.

#! /usr/local/bin/perl -w
use strict;
use Time::gmtime qw(gmctime);
use File::stat qw(stat);
use HTTP::Date qw(parse_date);
use Date::Calc qw(Date_to_Time);

sub HasChanged($$);
sub ReadData($);

my($path) = "data.txt";
# get the modification time of the data file
my($dt) = gmctime(stat($path)->mtime);

if (!HasChanged($dt, $ENV{HTTP_IF_MODIFIED_SINCE}))
{
   # return the 'not modified' status code to the client
   print <<EOF;
Status: 304 Not Modified

EOF
}
else
{
  my($num) = ReadData($path);

  print <<EOF;
Content-Type: text/xml
Last-Modified: $dt

<?xml version="1.0"?>
<vxml version="2.1">
<form>
   <block>The magic number is $num</block>
</form>
</vxml>
EOF
}

# has the data changed?
sub HasChanged($$)
{
   my($modCurrent, $modIMS) = @_;

   # client not asking IMS; just wants the content
   if (!$modIMS)
   {
      return 1;
   }

   my($yrIMS, $monIMS, $dayIMS, $hrIMS, $minIMS, $secIMS) 
        = parse_date($modIMS);
   my($yrCur, $monCur, $dayCur, $hrCur, $minCur, $secCur) 
        = parse_date($modCurrent);

   my($epochIMS) 
        = Date_to_Time($yrIMS, $monIMS, $dayIMS, $hrIMS, $minIMS, $secIMS);
   my($epochCurrent)  
        = Date_to_Time($yrCur, $monCur, $dayCur, $hrCur, $minCur, $secCur);
   if ($epochCurrent > $epochIMS)
   {
      return 1;
   }
   else
   {
      return 0;
   }
}

# read the data from the specified file
sub ReadData($)
{
   my($path) = @_;

   open HFILE, $path;
   undef $/;  
   my($num) = <HFILE>;
   close HFILE;
   $num;
}

Note. For a simple script such as the one shown above, it would be more effective to simply return the content.

If you're implementing your server-side scripts using Apache mod_perl, refer to the meets_conditions method of the Apache::File module for details on a simpler way to perform If-Modified-Since validation. If you're using Apache's C API, refer to the ap_meets_conditions function.

A dynamic script can also participate in If-None-Match validation by emitting an appropriate ETag response header. For information on sending this header using Apache mod_perl see Entity Tags in the Issuing Correct Headers Apache documentation.

Once the development of your application has stablized and you're ready to deploy your first version, you should configure your Web server to send the appropriate Expires header along with the static documents that make up your application. When following the caching and versioning strategy recommended in this document, you should configure your Web server to expire each static document one year from the time it is requested by any client.

While all Web servers provide some means for setting the Expires header, the interface for doing this differs from server to server. Because of the popularity and ubiquity of the Apache Web Server, a document on configuring Apache 1.3x to send Expires headers is available. If you are using a different Web server, please contact your server administrator or consult your Web server documentation.

When you need to deploy an updated version of your content, do the following:

  1. Create a new version-specific directory on the Web server.
  2. Propagate all the content, including the updates, to that directory.
  3. Update the configuration of your Web server to send the appropriate Expires headers.
  4. Revise the application entry point document to navigate to the documents contained in the new version-specific directory.

Because the application entry point is not cached, the next time a VoiceXML interpreter is directed to handle a call to your application, it will request a fresh copy of the application entry point from the server. Updates to the application entry point cause the interpreter to request objects in the new version-specific directory because they will not yet exist in the interpreter's local cache.

For example, let's say you published the first version of your application at 11:00 GMT on March 27, 2002 and plan to deploy an updated version of your application at 10:00 GMT on April 10, 2002. Your non-cacheable application entry point is currently accessible via the URL http://vtrader.acme.net/index.vxml. This URL will not change. The other documents in your application, currently accessible via the URL http://vtrader.acme.net/2002-03-27-1100/ will be replaced by the updated content accessible via the URL http://vtrader.acme.net/2002-04-10-1000/. To perform the update, you create a directory on your server named 2002-04-10-1000 and publish the updated content to that directory. When you're ready to transition to this new version, simply modify the paths in the application entry point document to reference the new version-specific directory as follows:

<vxml version="2.1" application="./2002-04-10-1000/app_root.vxml">
   <form id="main">
      <block>
         <!-- jump to main menu -->
         <goto next="./2002-04-10-1000/index.vxml"/>
      </block>
   </form>
</vxml>

Web applications that present backend data to users typically use server-side scripts to generate content on the fly. Server-side scripting is a powerful paradigm for building applications, but most of the content that is transferred to the client from the server is typically static.

Consider, for example, the portfolio module of the brokerage application that reads a list of company names, the number of shares held, the current price per share, and the total value. Using a server-side script, the server not only transmits the raw data across the network but also transmits the UI and standard boilerplate markup. The following example shows some VoiceXML generated by an application server.

<?xml version="1.0"?>
<vxml version="2.1"
 xmlns="http://www.w3.org/2001/vxml">
<form id="read_list">
<block>
   <prompt>There are ten stocks in your portfolio</prompt>
   <break time="300ms"/>

   <prompt>100</prompt>
   <prompt>shares of</prompt>
   <prompt>Microsoft Corporation</prompt>
   <prompt>valued at</prompt>
   <prompt>$6000.00</prompt>
   <break time="300ms"/>

   <prompt>50</prompt>
   <prompt>shares of</prompt>
   <prompt>Sun Microsystems</prompt>
   <prompt>valued at</prompt>
   <prompt>$550.00</prompt>
   <break time="300ms"/>

   <prompt>80</prompt>
   <prompt>shares of</prompt>
   <prompt>AOL Time Warner</prompt>
   <prompt>Down</prompt>
   <prompt>valued at</prompt>
   <prompt>$1920.00</prompt>
   <break time="300ms"/>

   <!-- and so on for the remaining 7 stocks -->

</block>
</form>
</vxml>

Alternatively, by using the data element, you can reduce the amount of information that needs to be processed and transmitted by your Web server, restricting it to just the data. The VoiceXML content that presents the data can be contained in a cacheable, static VoiceXML document as shown in the following example:

<?xml version="1.0"?>
<vxml version="2.1"
 xmlns="http://www.w3.org/2001/vxml">

<!-- static JavaScript utility routines including GetStockListFromData -->
<script src="stocklist.js"/>

<form id="read_list">

<!-- fetch the portfolio data as XML -->
<data name="oData" src="portfolio.cgi" namelist="uid"/>
<!-- convert the data into a JavaScript array of objects --> 
<script>var aStocks = GetStockListFromData(oData);</script>

<block>
   <prompt>There are <value expr="aStocks.length"/> stocks in your portfolio.</prompt>
   <break time="300ms"/>

   <foreach item="oStock" array="aStocks">
      <var name="qty" expr="oStock.GetShareCount()"/>
      <prompt><value expr="qty"/></prompt>
      <prompt>shares of</prompt>
      <prompt><value expr="oStock.GetCompanyName()"/></prompt>
      <prompt>valued at</prompt>
      <prompt>$<value expr="oStock.GetValuePerShare()*qty"/></prompt>
      <break time="300ms"/>
   </foreach>
</block>
</form>
</vxml>

For a complete tutorial on using the data element, please see Using the data element.

The following is a short list of "do's and don'ts" that pertain to caching.

As a developer, it can be tempting to control caching by seeding the content with meta tags. While the VoiceXML interpreter and its local cache respects the cache settings specified in the meta, the proxy caches running on the Tellme Voice Application Network do not. The proxy caches check the HTTP headers sent with an object; they do not take the time to parse the object. Intermediary caches outside the Tellme Voice Application Network may behave differently, but you should not rely upon the meta when optimizing your application for caching.

The pragma: no-cache header is not part of the HTTP/1.1 standard and is not respected by some caches, including the caches used by the Tellme Voice Application Network. To force the VoiceXML interpreter and proxy cache to always retrieve the latest version of a document from the Web server, use the HTTP/1.1 standard Cache-Control: no-cache.

Other caches outside the Tellme Voice Application Network may respect one of the following:

  • Setting the Cache-Control header no-cache.
  • Setting the Expires header to a date and time in the past .
  • Setting the pragma header to no-cache.

In general, there is no penalty for sending all three headers with appropriate values.

The HTTP/1.1 specification introduced a Cache-Control modifier named max-age. This modifier allows you to specify the number of seconds after which an object should be considered stale. Unlike the Expires header, the max-age header makes no guarantees that the object is fresh before the specified number of seconds elapses. max-age provides a guarantee of staleness but makes no guarantees about freshness.

While Tellme's proxy caches support the max-age and Expires headers, currently, the end caches support only the Expires header. To get the full benefits of both the proxy and end caches, your server should send the Expires header. If your HTTP server generates both Expires and max-age headers simultaneously, verify that the values do not conflict.

In the following example, the Expires header indicates that the object expires at 06:00:15 GMT on Saturday, February 9, 2002, twenty-four hours after the access time of 06:00:15 GMT on Friday, February 8, 2002. The max-age header also indicates that the content is stale after twenty-four hours (24*60*60=86400) has elapsed, so there is no conflict.

HTTP/1.1 200 OK
Date: Fri, 08 Feb 2002 06:00:15 GMT
Server: Apache/1.3.19 (Unix)
Last-Modified: Fri, 08 Feb 2002 03:00:00 GMT
ETag: "15dbae-3ed-3c634aa0"
Cache-Control: max-age=86400
Expires: Sat, 09 Feb 2002 06:00:15 GMT
Accept-Ranges: bytes
Content-Length: 1005
Connection: close
Content-Type: text/xml

Note. If you use Netscape Enterprise Server 6.1 or earlier, note that the HTTP server does not provide a high-level administrative option to send an Expires header with designated content. To configure the Netscape Server to generate an Expires header, you must implement an NSAPI plug-in. Please contact your Solution Director for details.

Neither the end cache nor the proxy cache will cache content when the HTTP response headers sent with the content contain a Set-Cookie header. For this reason, you should be judicious about where you set cookies within your application, or your application will lose the benefits of caching. For example, you typically do not want to set cookies on your audio resources.

To verify whether or not a Set-Cookie header is sent with a particular resource, obtain the URL of the resource, and following the instructions in the section entitled Verifying HTTP headers.

Recall from the section How caching works, that, while the end cache may cache a resource transmitted using SSL, the proxy cache will not. You should restrict your use of SSL to the components of your application that contain confidential data. Furthermore, by using the data element, you can separate data from presentation logic and limit your use of SSL to resources that probably shouldn't be cached.

Proper use of caching will improve application performance, but it won't necessarily increase application reliability. For example, do not rely upon caching to mask server downtime.

Although the Tellme Voice Application Network has a robust caching infrastructure in place, numerous factors may contribute to additional requests to your Web server:

  • Caches have a finite capacity and are routinely scavenged.
  • According to the HTTP/1.1 specification, a client is free to request a fresh version of an object from an origin server as often as is necessary. For example, a VoiceXML document may include code that requests a resource with a maxage attribute set to 0.
  • A new proxy cache may be brought into service on the Tellme Voice Application Network; an existing proxy cache may be taken down for maintenance.

To attain increased reliability add Web servers to your installation, and load-balance them. If you do this, however, make sure your Web servers are configured to generate equivalent ETag values for identical content.

Some Web servers are not configured to return Last-Modified and ETag headers. To verify that your Web server returns these headers, perform the following steps:

  1. Identify the URL of a document on your Web server that is cacheable.
  2. Use telnet to connect to your server on the HTTP port (80 by default).
  3. Use the HTTP HEAD command to retrieve a document from your server.
  4. Include a Host and Connection request header to complete the transaction.
  5. Examine the HTTP headers returned. If the Last-Modified and ETag headers are returned, your Web server is configured properly.

For example, the following telnet session fetches the HTTP headers for http://studio.tellme.com/index.html.

% telnet studio.tellme.com 80
Trying 64.41.140.79...
Connected to studio.tellme.com.
Escape character is '^]'.
HEAD /index.html HTTP/1.1
Host: studio.tellme.com
Connection: close

HTTP/1.1 200 OK
Date: Fri, 08 Feb 2002 09:01:18 GMT
Server: Apache/1.3.19 (Unix)
Last-Modified: Fri, 08 Feb 2002 03:49:33 GMT
ETag: "10f709-ef-3c634acd"
Accept-Ranges: bytes
Content-Length: 239
Connection: close
Content-Type: text/html

As an alternative, if you have access to a machine running UNIX, you can use the HEAD utility to retrieve the HTTP response headers for any URL. The following example uses HEAD to retrieve the response headers for the URL http://studio.tellme.com/index.html.

HEAD -e http://studio.tellme.com/index.html

Note. Use the -p switch to specify a proxy if your UNIX host is running behind a firewall.

See Also
Configuring Apache 1.3 to use mod_expires, Hypertext Transfer Protocol -- HTTP/1.1, Mark Nottingham's Caching Tutorial for Web Authors and Webmasters
[24]7 Inc.| Terms of Service| Privacy Policy| General Disclaimers