10. Implementation Notes — Server-Side Scripting
Because the emphasis in server-side scripting is on dynamic content, it doesn’t make for very cacheable pages, even when the content could be cached. If your content changes often, but not on every page hit, consider setting a Cache-Control: max-age header; most users access pages again in a relatively short period of time. For instance, when users hit the ‘back’ button, if there isn’t any validator or freshness information available, they’ll have to wait until the page is re-downloaded from the server to see it.
CGI
CGI scripts are one of the most popular ways to generate content. You can easily append HTTP response headers by adding them before you send the body; Most CGI implementations already require you to do this for the Content-Type header. For instance, in Perl;
#!/usr/bin/perl
print “Content-type: text/htmln";
print “Expires: Thu, 29 Oct 1998 17:04:19 GMTn";
print “n";
### the content body follows…
Since it’s all text, you can easily generate Expires and other date-related headers with in-built functions. It’s even easier if you use Cache-Control: max-age;
print “Cache-Control: max-age=600n";
This will make the script cacheable for 10 minutes after the request, so that if the user hits the ‘back’ button, they won’t be resubmitting the request.
The CGI specification also makes request headers that the client sends available in the environment of the script; each header has ‘HTTP_’ prepended to its name. So, if a client makes an If-Modified-Since request, it will show up as HTTP_IF_MODIFIED_SINCE.
See also the cgi_buffer library, which automatically handles ETag generation and validation, Content-Length generation and gzip content-coding for Perl and Python CGI scripts with a one-line include. The Python version can also be used to wrap arbitrary CGI scripts with.
Server Side Includes
SSI (often used with the extension .shtml) is one of the first ways that Web publishers were able to get dynamic content into pages. By using special tags in the pages, a limited form of in-HTML scripting was available.
Most implementations of SSI 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 the mod_include documentation.
PHP
PHP is a server-side scripting language that, when built into the server, can be used to embed scripts inside a page’s HTML, much like SSI, but with a far larger number of options. PHP can be used as a CGI script on any Web server (Unix or Windows), or as an Apache module.
By default, representations 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
Header(“Cache-Control: must-revalidate");
$offset = 60 * 60 * 24 * 3;
$ExpStr = “Expires: " . gmdate(“D, d M Y H:i:s", time() + $offset) . " GMT";
Header($ExpStr);
?>
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 (although recent versions have made it easier; see the PHP’s date documentation). Of course, it’s easy to set a Cache-Control: max-age header, which is just as good for most situations.
For more information, see the manual entry for header.
See also the cgi_buffer library, which automatically handles ETag generation and validation, Content-Length generation and gzip content-coding for PHP scripts with a one-line include.
Cold Fusion
Cold Fusion, by Macromedia is a commercial server-side scripting engine, with support for several Web servers on Windows, Linux and several flavors of Unix.
Cold Fusion makes setting arbitrary HTTP headers relatively easy, with the CFHEADER tag. Unfortunately, their example for setting an Expires header, as below, is a bit misleading.
It doesn’t work like you might think, 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.
However, Cold Fusion does provide a date formatting function that will do the job; GetHttpTimeString. In combination with DateAdd, it’s easy to set Expires dates; here, we set a header to declare that representations of the page expire in one month;
<cfheader name="Expires"
value="#GetHttpTimeString(DateAdd(‘m’, 1, Now()))#">
You can also use the CFHEADER tag to set Cache-Control: max-age and other headers.
Remember that Web server headers are passed through in some deployments of Cold Fusion (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 and ASP.NET
When setting HTTP headers from ASPs, make sure you either place the Response method calls before any HTML generation, or use Response.Buffer to buffer the output. Also, note that some versions of IIS set a Cache-Control: private header on ASPs by default, and must be declared public to be cacheable by shared caches.
Active Server Pages, built into IIS and also available for other Web servers, also allows you to set HTTP headers. For instance, to set an expiry time, you can use the properties of the Response object;
specifying the number of minutes from the request to expire the representation. Cache-Control headers can be added like this:
In ASP.NET, Response.Expires is deprecated; the proper way to set cache-related headers is with Response.Cache;
Response.Cache.SetExpires ( DateTime.Now.AddMinutes ( 60 ) ) ;
Response.Cache.SetCacheability ( HttpCacheability.Public ) ;