Server-side includes allow web developers to reuse content across an entire site employing a simple but powerful structure. This tutorial illustrates how to use Apache eXtended Server Side Includes (XSSI) to create composite web sites and moderately complex, refined web applications without having to resort to more intricate frameworks. After introducing the basics, it shows several techniques that will give you an idea of what you can achieve with XSSI, but I will not address configuration issues. (For some pointers, see the section titled XSSI Resources.) XSSI is by no means a new web technology: server-side includes came about more than a decade ago. Nevertheless, XSSI is a workhorse and can prove indispensable while developing and maintaining dozen of sites.
Technically speaking, XSSI elements are instructions embedded into web documents. Traditionally this meant plain, standard (X)HTML pages; however, adding XSSI to any text-based document is straightforward. While Apache httpd serves static pages without any processing, the server must parse XSSI pages and then, in turn, execute the XSSI instructions and transmit the final result. Although this is similar to what other dynamic content-generation technologies use, many people consider XSSI a lackluster framework. Many web developers resort to more sophisticated technologies--dynamic content management systems such as PHP-Nuke on the one hand and web site development and delivery engines including Mason, Cold Fusion, and Lotus Notes/Domino on the other--and discount XSSI as "simplistic" and "limited." This is, to some extent, true. For example, XSSI does not allow you to connect to databases or perform mathematical operations. Nevertheless, this is by no means the whole story. XSSI has many features, including pattern matching. Many web sites work just fine without a database back end, especially if they use the database simply to store documents. In fact, you can even build a light content management system using XSSI.
XSSI has several advantages over other server-side technologies:
XSSI has a small but powerful set of instructions that you can pick up in
just a few hours. For example, to
set the value of the variable
who to the string
<!--#set var="who" value="World" -->
who, you can recall its value anywhere in a
page. For example, to display its value, use the
XSSI variables are of a single type only: character strings. There are no
integers, floats, and the like. Although you can assign a number to a variable,
Apache XSSI does not handle it any differently than a letter-only string.
Clearly, you cannot use XSSI to develop an online mortgage calculator. Listing
1 shows the Apache XSSI version of a Hello World page, which displays the
who twice: once in the document
and later in the main body,
Listing 1. Variable assignment and substitution
<html> <head> <!--#set var="who" value="World" --> <title>Hello <!--#echo var="who" -->!</title> </head> <body> <h1>Hello <!--#echo var="who" -->!</h1> </body> </html>
Besides programmer-defined variables, Apache makes more than 30 environment
variables accessible through your code (see XSSI
Resources) and gives you access to values passed as arguments to your XSSI
code. Effectively, you can customize presentation and content based on a wide
variety of parameters: browser make and version, user location or network
domain, time of day, and user-specified information. You can always get a
listing of the environment variables by embedding the
instruction in any (X)HTML document:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html> <head> <title>The Apache Environment Variables</title> </head> <body> <pre> <!--#printenv --> </pre> </body> </html>
The most common use of XSSI is file inclusion. Professionally designed web sites feature navigational aids, copyright notices, disclaimers, and other items that appear on every page. With XSSI you can store this content once and use it from any page on the site. This allows you to incorporate future updates and modifications virtually instantaneously. Listing 2 shows how to add a menu on the top of the Hello World page.
Listing 2. File inclusion
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html> <head> <!--#set var="who" value="World" --> <title>Hello <!--#echo var="who" -->!</title> </head> <body> <!--#include file="top-menu.html"--> <h1>Hello <!--#echo var="who" -->!</h1> </body> </html>
When a user requests the document shown in Listing 2, Apache will replace the file inclusion instruction (in bold) with the contents of the file top-menu.html (Listing 3), located in the same directory.
Listing 3. (X)HTML fragment used as an included menu
<p>Hello: <a href="hello-world.shtml">World</a> - Africa - Antarctica - America -Asia - Europe - Oceania</p>
Included files typically contain (X)HTML fragments only, not complete documents: reusable, common content, such as a navigational menu, makes a good (X)HTML fragment to store once and include throughout the site. When Apache receives a request for the document in Listing 2, it will parse Listing 2; process and execute the XSSI instructions (one variable assignment, two variable substitutions, and one file inclusion), effectively stitching together Listing 2 and Listing 3; and, finally, send the final output shown in Listing 4, which is a valid (X)HTML document, to the browser for display (Figure 1).
Listing 4. After Listing 2 and Listing 3 are processed, this is the final output sent to the requesting browser
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html> <head> <title>Hello World!</title> </head> <body> <p>Hello: <a href="hello-world.shtml">World</a> -Africa - Antarctica - America - Asia - Europe - Oceania</p> <h1>Hello World!</h1> </body> </html>
Figure 1. "Hello World!" in the browser
Experienced web site authors may have noticed that XSSI syntax looks as if it is enclosed in (X)HTML comments. This is not an accident: XSSI elements are actually formatted just like SGML comments, which is a big advantage. First, if due to some misconfiguration the unparsed document is sent to the browser, it will not display the directives. Second, this format works well with both plain-text editors and WYSIWYG web authoring software. Finally, it means that documents written in any markup language stemming from SGML, including XML, can incorporate XSSI naturally.
A standard element in most sites is the copyright notice, usually placed at the bottom of every page. Copyright notices comprise the copyright year and holder, and some statement regarding the retention or availability of the rights; for example, "All rights reserved." You can easily add such a "static" copyright notice in a fashion similar to that in Listing 2. However, web pages often evolve over time, with updates and modifications, and may include parts copyrighted in different years. Keeping track of which pages are copyrighted and when can be a maintenance nightmare. This is partly the reason that it is common to see copyrights that span from the first year the site went live to the current year, irrespective of when a single page's most recent update actually occurred. Listing 5 presents how XSSI can help you dynamically create such a copyright notice.
Listing 5. Sitewide copyright notice
<!--#config timefmt="%Y" --> <p>©2003-<!--#echo var="DATE_LOCAL" --> Kostas Pentikousis. All rights reserved.</p>
This approach has two steps. First, it must figure out what is the current
year. The current date and time is always available through the environment
DATE_LOCAL. However, the page should display only the
current year, so it needs to select the appropriate time format. The
<!--#config timefmt="%Y" -->
causes the display of only part of the date/time variables, such as
DATE_LOCAL: the four-digit year (
%Y). After this
configuration, the next step is to display the current time using the
DATE_LOCAL. If you decide that the copyright
notice should not span to the current year but, instead, until the year in
which the page had its last update, replace
Having already specified the time format,
echo will display the
current year only. This time format will be in effect for the remainder of the
document, unless re
configured. You can assign
timefmt any string, which may include special time-conversion
characters. Table 1 presents a partial listing of available conversion
|Conversion character||Replaced by|
||Locale's full or abbreviated weekday name|
||Day of the month: decimal number without a leading zero|
||Locale's full or abbreviated month name|
||Locale's 4-digit year|
<!--#config timefmt="Today is %B %e, %Y." --> <p><!--#echo var="DATE_LOCAL"--></p>
<p>Today is March 13, 2005</p>
Your options are limited only by the conversion characters and formats supported by the strftime() function available on your system. Finally, remember that there is more than one way to do it: Listing 6 presents a different implementation of the copyright notice, which produces the same output as that of Listing 5.
Listing 6. Sitewide copyright notice redux
<!--#config timefmt="2003-%Y" --> <p>©<!--#echo var="DATE_LOCAL" --> Kostas Pentikousis. All rights reserved.</p>
Any file can include others, which in turn can contain more file inclusions and other dynamically created content. Listing 7 illustrates this by including the file in Listing 5 (in bold), exactly as shown earlier. This recursion is a very powerful XSSI feature not available in many competitive technologies. The rest of this article takes advantage of this feature to dynamically generate multiple versions of a document and create hierarchical menus with XSSI.
Listing 7. Inclusion of static (top-menu.html) and dynamic (copy.shtml) content
<html> <head> <!--#set var="who" value="World" --> <title>Hello <!--#echo var="who" -->!</title> </head> <body> <!--#include file="top-menu.html"--> <h1>Hello <!--#echo var="who" -->!</h1> <!--#include file="copy.shtml"--> </body> </html>
Many sites offer each article in two different versions. One is heavy on graphics, pictures, navigational aids, possibly ads, and may partition each article to several parts or pages. The other version is simpler, often dubbed "printer friendly," and presents the same content in a single part but without most of the ads and navigational aids. Ideally you should be able to store each article in one document and allow for many different views or presentation formats. Note that you cannot really achieve this with, say, alternative style sheets. The alternative versions do include the same text, which could have been formatted using alternative style sheets, but they also include a variety of ancillary elements whose appearance style sheets alone cannot control. By making good use of cascaded style sheets and XSSI, it's possible to achieve this without the need for database lookups, content transformations, or storing the same content twice.
First, there must be a way to differentiate between the different versions a user may be requesting. For example, the user should be able to request the media-rich version with the URL
and the printer-friendly version by asking for
The trick is to scan the requested URL for the string
QUERY_STRING proves very useful in this case.
Its value is the string past the
? up to the end of the URL,
i.e. exactly where the code needs to focus its attention. In the first case,
when the media-rich version is requested,
QUERY_STRING is empty
(""). In the second case, it contains the string
if else statement to check whether
QUERY_STRING contains the string
Listing 8. Use of a conditional statement to determine whether the printer-friendly version is requested
<!--#if expr="$QUERY_STRING = /print/" --> <!--#set var="view" value="print" --> <!--#else --> <!--#set var="view" value="fancy" --> <!--#endif -->
Seasoned programmers may find the
if else syntax slightly
verbose. Remember, however, that XSSI directives should always be formatted as
valid SGML comments. The condition fits in the
inside double quotes (
So far, this has shown only how to display variables using the
echo directive, which expects the variable name as its argument.
On the other hand, the conditional statement (
if) operates with
the value of a variable. The code in Listing 8 accesses the value
QUERY_STRING by prepending the dollar sign
$) to the variable name (
QUERY_STRING). If the pattern
match succeeds, then
view is set to
fancy. Listing 9 includes the code shown in Listing 8 and uses
view variable to direct the browser to load the appropriate
style sheet, named either print.css or fancy.css, before
displaying the page. This highlights that XSSI instructions can appear anywhere
in the content of a file. A few words of caution, though: make sure that you
keep your single (
') and double (
") quotes, both of which (X)HTML and XSSI
support, always balanced.
Listing 9. Use of a variable defined in another file
<html> <head> <!--#include file="meta.shtml" --> <link rel="stylesheet" href="<!--#echo var='view'-->.css" type="text/css"> <!--#set var="who" value="World" --> <title>Hello <!--#echo var="who" -->!</title> </head> <body> <!--#include file="top-menu.shtml"--> <h1>Hello <!--#echo var="who" -->!</h1> <!--#include file="footer.shtml" --> </body> </html>
set in a different file
(meta.shtml, from Listing 8), it's still usable in the current file
(Listing 9). In programming language terminology, the scope of each XSSI
variable is from the point of definition to the end of the document. Keep in
mind that if a variable is undefined or the code never assigned it a value,
echo will display
Of course, you can use conditional statements not only with style sheets,
but also with any kind of document content. For example, Listing 10 presents
the new, XSSI-enabled navigation bar, which is not present in the printer-friendly version. In the same way, it's possible to replace a Flash ad with a
simple image banner in the printer-friendly version. Listing 11 employs the
same technique to include a link conditionally. If
view is not
equal to (
!=) the string
Listing 10. This navigation bar does not appear in the printer-friendly version
<!--#if expr="$view != print" --> <p>Hello: <a href="?World">World</a> - Africa - Antarctica - America - Asia - Europe - Oceania</p> <!--#endif -->
Listing 11. This content is common to all pages on the site and is placed in a single file (footer.shtml)
<!--#if expr="$view != print" --> <p><a href="?print">Printer-friendly version</a></p> <!--#endif --> <!--#config timefmt="2003-%Y" --> <p>©<!--#echo var="DATE_LOCAL" --> Kostas Pentikousis. All rights reserved.</p>
These examples use two different kinds of string matching. First, there's a pattern match in:
<!--#if expr="$QUERY_STRING = /print/" -->
This tries to match print anywhere in the contents of
$QUERY_STRING. You can read this instruction as "If the
$QUERY_STRING matches 'print', then." On the
<!--#if expr="$QUERY_STRING = print" -->
does an exact match on
$QUERY_STRING. This directive
essentially means "If the expression
'print', then." One implication of using pattern matching is that if the user
requests any of the following:
http://www.example.org/hello-world.shtml?print-this http://www.example.org/hello-world.shtml?printANDthis http://www.example.org/hello-world.shtml?still_printer-friendly
Apache will serve the printer-friendly version. If the code uses exact matching instead, all three will result in serving the media-rich version instead.
Checking for inequality can prove quite useful as well. For example, to make
view does not contain the string
<!--#if expr="$view != /print/" -->
You may wonder why Listing 8 set a variable (
view) instead of using
if else statements and the same pattern matching on
$QUERY_STRING several times. Pattern matching is computationally
more expensive than exact matching, especially with long strings.
QUERY_STRING can be long, so it is likely more efficient to do a
single pattern match, set a variable, and then do multiple exact matches to
determine which (X)HTML fragments to include. In general it is a good idea to
avoid recurring pattern matches and, if possible, to replace (redundant) pattern
matches with exact matches.
Taking advantage of environment variables and conditional statements, you
can use XSSI to redirect visitors to the most appropriate page based on the
make and version of their browser, their IP address, fully qualified domain
name, the time of day, and so on. Remember that you can always nest
if elif else statements. Listing 12 illustrates a very simple case
in which you want to redirect users from your local networks (*.example.org) to
one page (http://www.example.org/local) and users from any other domain to
another one (http://www.example.org/visitors). Note that this is a good
solution only if you do not have administration access to Apache configuration
files. Otherwise, you should use
Listing 12. Conditional redirect using XSSI
<!--#if expr="$REMOTE_HOST = /example.org/" --> <meta http-equiv="refresh" content="0;URL=/local"> <!--#elif expr="$REMOTE_HOST = '' " --> <meta http-equiv="refresh" content="0;URL=/not_fully_qualified_hostname"> <!--#else --> <meta http-equiv="refresh" content="0;URL=/visitors"> <!--#endif -->
Several online magazines partition their articles in an effort to increase ad revenue by creating more spots for ads. For example, splitting each article into two parts means that the site can double the number of banners, buttons, and "unobtrusive ads" it serves. There is also an old-style rule dictating that pages should be about one and a half "screens" long. In this fashion, the user does not have to scroll more than once or twice while browsing a page. For example, suppose that the article about World has two parts. The entire article content is stored once at
This URL will also display the first part. The second part has a URL of
In order to make the little application more robust, you have chosen to
display the first part of the article unless the user explicitly requests the
second one. That way if, for example, a user adds random characters after the
?, she will get a valid response and not an error message. In fact,
the first part is the "proper" response, because more often than not articles
progress serially and readers should read them in that order. Of course, you
would also like to have a printer-friendly version that includes both parts
Listing 13 illustrates that four lines of XSSI code are enough to bisect the
article. First, check that the user did not explicitly request the second page
$QUERY_STRING != 'p=2'). If he did, the first
else (in bold) will fail and the first part is skipped. Instead, the
code displays the second part because the second condition (
= 'p=2') is met. If the user requests a printer-friendly version, both
parts will display. This guarantees robustness.
Listing 13. Article partitioning with XSSI
<html> <head> <!--#include file="meta.shtml" --> <link rel="stylesheet" href="<!--#echo var="view"-->.css" type="text/css"> <!--#set var="title" value="World Statistics" --> <title><!--#echo var="title" --></title> </head> <body> <h1><!--#echo var="title" --> </h1> <!--#if expr="$QUERY_STRING != 'p=2' || $view = print" --> Text for part 1 here... <!--#set var="p" value="2" --> <!--#endif -->> <!--#if expr="$QUERY_STRING = 'p=2' || $view = print" --> Text for part 2 here.. <!--#set var="p" value="1" --> <!--#endif --> <!--#include file="article-tools.shtml" --> <!--#include file="copy.shtml" --> </body> </html>
An important component of article partitioning is the navigational aids that
help the visitor move from one part to the next. First, the code needs to
determine which part is currently on display. Because the article has only two
parts, a single variable (
p in Listing 13) will do: you simply need to
p to the value of the complementing part. Based
on this value, the code in article-tools.shtml (Listing 14) generates
either a navigation label of Next:
Note that the first conditional statement in Listing 14 ensures that neither of the labels appear in the printer-friendly version. Listing 14 includes extraneous indentation in order to make the syntax easier to read. A real-world application would have put the entire snippet in three lines:
Listing 14. "Previous"/"Next" navigation label generation
<!--#if expr="$view != print" --> <p> <a href="?p=<!--#echo var='p' -->"> <!--#if expr="$p = 1" --> Previous <!--#else --> Next <!--#endif --> </a> </p> <!--#endif -->
An alternative version of article-tools.shtml (Listing 15)
generates the "next" page number. With modification, this technique can also
partition an article into more than two parts. However, XSSI lacks a repetitive
or conditional loop instruction (such as
while) and basic arithmetic functions. Thus, if you wish to divide
an article into several pieces, adding the partitioning code by hand quickly
Listing 15. Generation of numeric navigation labels
<!--#if expr="$view != print" --> <p> Page <!--#if expr="$p = 1" --> <a href="?p=1" -->1</a> | 2 <!--#else --> 1 | <a href="?p=2" -->2</a> <!--#endif --> </p> <!--#endif -->
I find fragmenting a short article in more than two parts to be quite frustrating for the user. Nevertheless, doing so for a long report, user documentation, or even a book that has distinct parts or sections is a good idea in general. In fact, this technique is best to apply when the text has various independent sections that share a common introductory or concluding segment. In that case, partitioning also has the advantage of permitting the reader to bookmark each section individually, while at the same time providing many different search engine entry points. It is also a great feature if the site audience includes mobile users or visitors connecting through low-bandwidth connections. The following section explains how to take article partitioning with XSSI one step further.
Long texts such as books, standards documents, and user manuals are typically organized in chapters and sections. By exploiting this inherent structure, XSSI can provide an all-inclusive/printer-friendly version of the entire text; furnish each book chapter independently; and allow users to read any assortment of chapters à la carte. This portioning method is a form of basic content management, which allows for increased usability--making it possible to deliver long documents in palatable pieces while keeping the number of maintainable and updatable documents to a minimum.
To further illustrate the partitioning method using XSSI, consider a presentation of some text from the first book of Charles Dickens's A Tale of Two Cities, which is freely available from Project Gutenberg. Following the terminology used in the previous section, each book chapter becomes a part in the presentation. Chapter 1 is available through the URL
Chapter 2 is available from
and so on. Of course, a good application should also generate dynamic links between chapters (Previous/Next). A printer-friendly version of the entire book is available via
while a printer-friendly version of the first chapter only is available at
Finally, there's one more feature: if the user would like to read chapters 2 and 3 in one go, then she can do so by requesting:
Figure 2 illustrates the opening page of the book, which contains the table of contents, an action button (titled "View selected"), and a link to a printer-friendly version of the entire book. The table of contents includes links to each chapter separately and check boxes that can be used to select a subset of chapters. Note that users can address and bookmark all these different views of the same document independently, yet the entire text is still in a single file (Listing 16).
Figure 2. The best and worst of times
Listing 16. Book chapters à la carte using XSSI
<html> <head> <!--#include file="meta.shtml" --> <link rel="stylesheet" href="<!--#echo var='view'-->.css" type="text/css"> <!--#set var="title" value="A Tale of Two Cities" --> <title><!--#echo var="title" --></title> </head> <body> <h1 id="top"><!--#echo var="title" --></h1> <p>A story of the French Revolution by Charles Dickens.</p> <h2>Book the First--Recalled to Life</h2> <!--#if expr="$view != print" --> <form method="GET"> <p><input type="submit" value="View selected"></p> <p><input type="checkbox" name="p" value="1"> <a href="?p=1">I. The Period</a><br /> <input type="checkbox" name="p" value="2"> <a href="?p=2">II. The Mail</a><br /> <input type="checkbox" name="p" value="3"> <a href="?p=3">III. The Night Shadows</a><br /> ... <p><input type="submit" value="View selected"></p> </form> <!--#endif --> <!--#if expr="$QUERY_STRING = /p\=1/ || $QUERY_STRING= print" --> <h3>I. The Period</h3> <p>It was the best of times, it was the worst of times, ...</p> <!--#set var="next" value="p=2" --> <!--#include file="back2top.shtml" --> <!--#endif --> <!--#if expr="$QUERY_STRING = /p\=2/ || $QUERY_STRING = print" --> <h3>II. The Mail</h3> <p>It was the Dover road that lay, ...</p> <!--#set var="previous" value="p=1" --> <!--#set var="next" value="p=3" --> <!--#include file="back2top.shtml" --> <!--#endif --> <!--#if expr="$QUERY_STRING = /p\=3/ || $QUERY_STRING = print" --> <h3>III. The Night Shadows</h3> <p>A wonderful fact to reflect upon, ...</p> <!--#set var="previous" value="p=2" --> <!--#include file="back2top.shtml" --> <!--#endif --> <!--#if expr="$view != print && $QUERY_STRING != /&p\=/" --> <p> <!--#if expr="$previous" --> <a href="?<!--#echo var="previous" -->">previous</a> | <!--#endif --> <!--#if expr="$next" --> | <a href="?<!--#echo var="next" -->">next</a> <!--#endif --> </p> <!--#endif --> <!--#include file="book-tools.shtml" --> </body> </html>
The partitioning code is similar to that of the previous section. For example, the first part will display if the condition
<!--#if expr="$QUERY_STRING = /p\=1/ || $QUERY_STRING= print" -->
is true. Chapter 1 will display if
p=1 is present anywhere in
$QUERY_STRING; that is, when requested either as a single chapter
or as part of a collection of chapters with
Note that code has to escape the equal sign in this conditional
\=) in order to differentiate the character equal sign in the
p=1 from the operator equal sign. Alternatively, the first
chapter will display if the user requests the entire book in printer-friendly
format. Listing 16 also includes code for generating the navigational labels
(Previous/Next) and links that take the user back to the top of the page
(and should not appear in the printer-friendly versions) as shown in Listing
Listing 17. Back to the top
<!--#if expr="$view != print" --> <p><a href="#top">Back to top ⇑</a></p> <!--#endif -->
Finally, the code includes Listing 18, which generates links to the printer-friendly versions. The label "Print this" appears only when the user views a single chapter or a set of chapters, but not the opening screen (Figure 2).
Listing 18. Book-tools revealed
<!--#if expr="$view != print" --> <p> <!--#if expr="$QUERY_STRING = /p\=/" --> <a href="?print&<!--#echo var='QUERY_STRING' -->">Print this</a> - <!--#endif --> <a href="?print">Print entire book</a></p> <!--#endif -->
The principle presented here applies to scores of different applications. For example, a bus operator web site can provide customized schedules using XSSI only. In this scenario, the schedule to and from a particular end point is stored in a single document with the timetables for weekdays and weekends forming the different parts of the text. In a fashion similar to the Dickens example, a passenger traveling out of town for the weekend may choose to get a printout of the Friday (weekday) timetable to her destination along with the Sunday (weekend) timetable from her destination. The site can go one step further and use time/date information to present today's schedule by default. Traveling directions can also be partitioned, for example, based on the different means of transportation. In this case, all parts may share some common text, such as the destination address and phone number, without the need to store this information and replicate it in several places.
As a last example, consider the web site of a registered charity that accepts donations via several different methods such as small cash, check, or credit card donations, automatic paycheck withholdings, and planned giving. The charity would like to offer online details about each method. Of course, this wide range of options comes with a diverse set of rules and regulations, which may force some designers to create different pages for each category--a decision that usually increases future maintenance time. Moreover, it is convenient to be able to pick and choose the method that is more interesting to a potential donor. Using the partitioning method above, the site can include details on all possible donation procedures in a single document while delivering customized "web brochures" to donors.
All XSSI solutions, including the partitioning method, can work with client-side scripting to enhance certain aspects of the application, if required. In addition, you can also generate the content itself offline using back-end solutions. For example, your content producers may edit the document using a wiki environment run on the company intranet server. At regular intervals, a back-end application may check for updates on the wiki and, if there are any, generate the XSSI-enriched document for publishing on the public company web site. That way, you can enforce document control via the wiki, which makes the public server content more secure, because a bare-bones Apache with XSSI enabled is less complex and in general more mature and robust than wiki environments. In addition, you can deliver the web site content with fewer hardware and software requirements, as there is no need for a document database. This is an efficient cost-cutting solution, because the web site can serve more users with the same configuration.
This section proposes an alternative implementation of hierarchical menus using XSSI. The main advantage with XSSI is that client make, capabilities, or configuration options do not affect the look and feel of the site at all. The menus will work just fine, because the final product is pure markup and the browser does not need to execute any client-side code. You will also get the coolness factor, at least to some extent. Moreover, the site will load significantly faster, because users will download only the menu items that they are interested in instead of downloading all menus and submenus at once. Last, the XSSI implementation presented below enforces structured thinking while laying down the site design, which leads to well-organized, functional web sites.
Assume that you are building a site that provides information on the planets of the solar system, the greater geographical areas on each planet (aka continents for Earth), and the political subdivisions in each area (countries). Clearly the content size in each category, and hence in each menu, is quite unbalanced. This, of course, is the case with most real-world web sites: some sections of the content are richer and more elaborate (Our Products or Our Services sections of corporate sites); others are bare-bones (the omnipresent Contact Us).
Table 2, Column a, shows a good candidate for this site's first-level, left-side vertical menu. Each planet is a link, an item in the menu, which when selected leads to the corresponding page. The heading Solar System is also a menu item. When the user selects Earth, the corresponding page loads and the relevant submenu opens up, displaying the second-level menu items (column b), the continents. Similarly, when the user selects Africa, the third-level menu (column c) opens up, displaying the list of African countries along with information on Africa.
Table 2. Hierarchical menus illustrated
This menu structure is easy to translate into a directory structure, which I will exploit as follows. Each of the menu items maps to a directory in the implementation, and each directory has a single document titled index.shtml that includes the menu and the corresponding text. Listing 19 presents the outline of every such document.
Listing 19. Page template
<html> <head><title>Solar System</title></head> <body> <!--#include virtual="/menu.shtml" --> <p>Content particular to the Solar System... </body></html>
Although this may seem a bit restrictive, it is not a prerequisite--it
simply eases the presentation in this tutorial. The only XSSI directive is the
familiar file inclusion. Notice that instead of including the
menu.shtml, the code includes its
virtual location on the
site. Where is all the magic, you ask? That happens in
menu.shtml, presented in Listing 20. Each menu item is listed with the
corresponding link, and each item that has subcategories conditionally includes
the relevant submenu. This submenu displays only when the requested document is
under the corresponding hierarchy.
Listing 20. The first-level hierarchical menu implementation includes the menu items ("Solar System") and, where needed, conditional inclusions of the relevant submenus
<p><a href="/">Solar System</a></p> <ul> <li><a href="/mercury">Mercury</a></li> <li><a href="/venus">Venus</a></li> <li><a href="/earth">Earth</a> <!--#if expr="$REQUEST_URI = /earth/" --> <!--#include virtual="/earth/menu.shtml" --> <!--#endif --> </li> <li><a href="/mars">Mars</a></li> ... </ul>
For example, if the user requests the main page of the site:
Listing 20 will display the opening page with information on the solar system and the menu as shown in Table 2, Column a. The submenu for Earth will not display. In contrast, when the user clicks on the link for Earth, she effectively requests
and the submenu shown in Listing 21 will be included and the full menu will display as in Table 2, Column b. Again, the menu.shtml found that the earth directory contains the links to the menu items (Africa, Antarctica) and a conditional submenu inclusion where appropriate. Finally, if the user clicks on Africa, she is effectively requesting
In that case, the complete submenu will be display as in Table 2, Column c.
Listing 21. Second-level submenu ("Earth")
<ul> <li><a href="/earth/africa">Africa</a> <!--#if expr="$REQUEST_URI = /africa/" --> <!--#include virtual="/africa/menu.shtml" --> <!--#endif --> </li> <li><a href="/antarctica">Antarctica</a></li> ... </ul>
Listing 22. Third-level submenu ("Africa")
<ul> <li>Algeria</li> <li>Angola</li> <li>Benin</li> ... <li>Zimbabwe</li> </ul>
Effectively, this hierarchical menu's implementation takes advantage of the directory structure and conditional recursive file inclusions, delivering a simple yet powerful navigational aid. You can extend submenus and add new menu items on demand. Maintaining the menus is straightforward because the menu reflects the directory structure. In fact, you could even develop a back-end solution to create menus and the corresponding directory structure based on configuration files along the lines discussed in the previous section.
Although this tutorial presents several solutions based on XSSI, it does not cover configuration issues. If you are responsible for setting up XSSI on a server, your first reading should probably be the Apache Tutorial: Introduction to Server Side Includes. This excellent yet brief tutorial explains how to configure Apache to run XSSI and introduces several commands along with examples of typical usage. The Apache Module mod_include documentation is a great resource as well.
The NCSA HTTPd Tutorial: Server Side Includes introduces SSI as a technology that allows users "to provide simple information on the fly." The NCSA HTTPd is a web server that is now obsolete but is, after all, the granddaddy of Apache. This tutorial is from 1995 and is mostly of historical value today. Do pay attention to the warnings about security, but bypass the caveats about performance: every dynamic content technology places a burden on the server typically, as mentioned in the introduction, much heavier than XSSI.
The tutorial Using XSSI and ErrorDocument to configure customized international server error responses makes for very interesting reading and can give you many ideas for your applications. The XSSI Library by Ross Olson is full of recipes, tips, and code samples. For example, Olson categorized the 30-plus environment variables you can use in your XSSI code and lists them along with a short description and examples Finally, the Apache Hello World Benchmarks has more details than I mentioned in the beginning of the tutorial.
Kostas Pentikousis is currently an ERCIM Fellow at VTT, The Technical Research Center of Finland, and resides in Oulu, Finland.
Return to the Apache DevCenter.
Copyright © 2009 O'Reilly Media, Inc.