Apache - Server Side Includes

Note: The original of this page was written many, many moons ago when the world was young. We had the unhappy task of re-visiting it recently because of an email question and have corrected a number of errors, ambiguities and explanations which were shaky at best. All the information relates to PHP5 and Apache 2.x (currently 2.4).

Sad Note: Apache 2.4 (actually 2.3.13+) has changed the syntax of conditional expressions. The old behavior can be restored using the SSILegacyExprParser directive. Why do they do this stuff? Apache 1.3 used to be like a rock for backward compatibility.

Happier (Maybe) Note: We recently decided to create an HTML Help Service for LDAPviewer a Java based open source application we are working on. The Help service would be viewable from the web and (if using JRE 1.8) using local files distributed with the application. We are addicted to the use of (X)SSI includes to keep HTML development to a minimum. The question was what to do about local files. The result was this very trivial and limited Java XSSI tool, which will expand (X)SSI include directives in .html, .htm and .shtml files and copy referenced images to a images subdirectory in the output directory (fixing the <img> src reference at the same time). We do all our HTML page development on the web version and simply run the files through this directory based tool to create pure HTML (HTML5 in our case) as output to be distributed with the application or for use with web servers which do not support (X)SSI.

SSI Overview

Server Side Includes are an Apache feature implemented by mod_include (see also this useful Apache article). Files which have the suffix .shtml (or whatever suffix name you use on the AddHandler server-parsed directive and its corresponding AddType text/html directive) are parsed by the server for SSI (or any file if the XBitHack directive is used). SSIs may be used for many purposes as may be seen from the descriptions and examples below. Our own use of SSIs may help illustrate their use:

config element

Controls many aspects of the parsing behaviour. More than one attribute may be defined in a single #config statement. Allows the following attributes:

ermsg

An optional string that may be used to override the standard error message, for example, ermsg="** SSI Error**"

sizefmt

Sets the format to be used for reporting size values (using the 'fsize' element) and may take the value 'bytes' in which case the size is reported in bytes or 'abbrev' in which case the size will be reported in Kbytes or Mbytes as appropriate.

timefmt

Determines the format for date/time strings and may take any of the values defined here. Note: If a special date format is required in any variable then this command must be issued before the variable is set.

Example

<!--#config ermsg="gottcha" timefmt="%c" -->

Changes the standard error message and outputs all date/times in 'locale' format.

include Element

Element

Attributes

Meaning

include

This command inserts the text of the included file into the parsed file. SSI files may be nested, that is the included file may contain additional SSI statements (but in this case must have an .shtml suffix irrespective of the setting of XBitHack). The included file inherits all the access control properties of the parsed file including all execute checks.

Files which generate dynamic content may be included by SSI e.g. PHP or other CGI files but the variable QUERY_STRING is reset to null but QUERY_STRING_UNESCAPED remains valid with an additional \ between parameters (it has been processed twice).

Files are included 'as is' i.e. they do not require any HTML other than that required in the context of the full page being generated. The attributes define the included file and may take the following values:

file

Defined as being relative to the current directory and below only. It does not allow '../' or an absolute path.

virtual

Accepts any relative format, for instance, '../../' or '/absolute/path/and/file' but only on this server.

Example

<!--#include virtual="../../styles.shtml" -->

Includes the file styles.shtml (which because it is named .shtml may contain additional SSI statements) in this parsed file.

Conditional Expresssions

SSI enables conditional processing using any of the environmental or user defined variables. SSI provides the following elements to support this feature:

If the test_condition result is TRUE then the lines following this element are included in the output until either the #else (if present) or #endif elements are reached.

Note: Since Apache 2.3.13 the test_condition syntax has changed - see notes under tests below.

#else

none

If the #if or #elif test_condition result is FALSE the SSI will continue from this point unconditionally until #endif.

#endif

none

Terminates the #if condition and MUST be present otherwise the conditional expression is NOT executed and the SSI will appear in the resulting output rather than the expected results.

Test Conditions Allowed

Important Note: The following syntax has been available since dinosaurs walked the earth - until Apache 2.3.13. From this release on it croaks and requires that you use this spiffy new format. However fear not, the SSILegacyExprParser on directive will restore the old processing syntax and mean that your decrepit old code (this includes us) will continue to work. The SSILegacyExprParser directive can be placed in either an .htaccess file or a <Directory> clause. So, for example, if you want to use the old conditional processing syntax across all webs (and what idiot is going to rewrite all their SSI just to move to a new version of apache) then you would typically place it in a Directory clause covering the root document (giving it the same scope as your Include parameter) as shown below:

Notes:

If string2 is written /string2/ it is interpreted as a regular expression (see here), for example, /MSIE/ will find MSIE anywhere in string1.

Strings are enclosed in quotes thus, expr="${QUERY_STRING} = 'a=b'". Note: The syntax change in 2.3.13+ changes this expression form to expr="%{QUERY_STRING} = 'a=b'".

If either string1 or string2 is a variable then the following syntax must be used ${var_name} - using curly brackets or braces. Note: The syntax change in 2.3.13+ changes this expression form to %{var_name}.

test_condition1 or test_condition2 above may be substituted for any of the string1 string2 expressions above, thus very complex tests may be built up.

Ambiguity. If we want to test something, say a path that has the form /path/name, this would be interpreted as a regular expression (see note above). In order to stop this (to disambiguate in the jargon) we create a string and use an escape sequence to remove it, for example, \"/path/name\".

GCI variables. CGI variables contained in a QUERY_STRING are not initialised (unlike PHP etc.) before calling SSI. If you need to test such a variable, for instance, myform.html?a=b&c=d then you need to use the form expr="${QUERY_STRING} = /a=b/". Note: The syntax change in 2.3.13+ changes this expression form to expr="%{QUERY_STRING} = /a=b/".

set Element

Element

Attributes

Meaning

set

Sets the value of a variable which may be used by subsequent SSI or CGI programs.

Attributes are:

var

The variable name to be set.

value

The value to be placed in the variable. The value may be set explicitly (x=y) or by assignment of an existing variable (environmental or user defined) and in this case is preceded by '$'. If you need to place a $ in a variable use the escape character, for example, var="give_to_me" value="\$1000"

The first example shows an explicit value (Windows) used to set the variable ($ not required).

The last example sets a user defined variable ('real_mod_date') to the environmental variable LAST_MODIFIED date of this file, we can now use this variable (using #echo) in, say, a standard footer. In this way we can get the last modified date for the 'real' file whereas if we used LAST_MODIFIED directly in our included footer file it would only change when we changed our footer file.

printenv Element

Prints (displays) a list of all the variables (environmental and user defined) and their values. It has no attributes:

<!--#printenv -->

Primarily used for de-bugging. Click here to display them for our server. Or if you want the tidy PHP version here.

echo Element

Element

Attributes

Meaning

echo

Prints (displays) a variable (either environmental or user defined). If the the variable has not been set by some method (e.g. BrowserMatch etc.) then 'none' is printed.

Allows the following attributes:

var

The variable name e.g. var="LAST_MODIFIED". Since echo only deals with variables you do not need the '$' sign.

encoding

Defines the encoding method used to display the variable. It may take the value 'none' in which case it is not touched, 'url' in which case URL encoding is performed or omitted in which case 'entity' encoding is perfomed (normal for HTML output). Unless you have a special requirement omit this attribute.

Examples

The first example displays a user defined variable which was previously set by SSI or Apache's BrowserMatch).

The second example illustrates that you can place SSI statements anywhere in your files. In this case using a dynamic back button to return to the calling page (OK so your Back button does the same thing - but with this technique you can save it in say, a mail page, and go back two pages at once (see here).

The last example is the trivial way we use to create absolute references without any work (on our part). We use it in all our page headers and it allows us to use a test site or our live site without changing anything, finally because it's absolute it will also work at any level in the directory structure etc. etc.). (example) Note: the 'http://' string is required because the $HTTP_HOST variable does not supply it - where HTTP_REFERER used in the previous example returns a full URL/URI.

exec Element

Element

Attributes

Meaning

exec

Allows execution of any shell or CGI script. This feature can be disabled using the Apache 'Options IncludesNOEXEC' directive otherwise it should be enabled and used on a robustly configured (read secure) server. In particular a server with too liberal write permissions is potentially vulnerable. Make sure that any required write permissions on the web site are limited i.e. contained in a unique sub-directory or other appropriate methods used.

Takes the following attributes.

cgi

The value parameter defines the relative path to the script to be executed. If the path doe not begin with a '/' it is assumed to be relative to the current document. The script is run even if the server would not normally recognise it as a cgi program and the directory containing it must be enabled via the use of a 'ScriptAlias' directive or the ExecCGI option.

The script is supplied with the QUERY_STRING and PATH_INFO variables. The environmental and user defined variables are also available to this script.

cmd

Will execute the shell command using /bin/sh. The environmental and user defined variables are available to this script.

Example

<!--#exec cmd="ls" -->
<!--#exec cgi="../hitcounter.php" -->

The first line executes a listing of the current directory and displays the results. The second line executes the php script defined (this could also have been done using the #include variable above.

fsize Element

Element

Attributes

Meaning

fsize

Prints (displays) the size of the file defined by 'file' or 'virtual' below. The measurement value is defined by the #config 'sizefmt' attribute.

file

Defined as being relative to the current directory and below only i.e does not allow '../' nor an absolute path.

virtual

Accepts any relative format e.g. '../../' or '/absolute/path/and/file' but only on this server.

Example

<p>Download size=<!--#fsize file="download.zip" --></p>

Example illustrates use of the element for information.

flastmod Element

Element

Attributes

Meaning

flastmod

Prints (displays) the last modification date of the file defined by either the 'file' or 'virtual' attributes below. This construct is somewhat static but can be incredibly useful if you want to inform users of the latest change to a central file or database i.e. suppose you have a standard file that is updated whenever your cousin takes a bath then you can output on the bottom of every page of your web:

'last bath on <!--#flastmod file="mycousin.bath" -->'

Pretty useful feature.

file

Defined as being relative to the current directory and below only i.e does not allow '../' nor an absolute path.

virtual

Accepts any relative format e.g. '../../' or '/absolute/path/and/file' but only on this server.

Example

<p>expenses updates <!--#flastmod virtual="../expenses.html" -->.</p>

Time and Date Format Codes

These are parameters to the well known ctime function and may take one or more of the following values:

Value

Meaning

%a

Abbreviated weekday name

%A

Full weekday name

%b

Abbreviated month name

%B

Full month name

%c

'Locale' formatted date and time

%C

Default formatted date and time

%d

Day of month (01 - 31)

%D

Date as %m/%d/%y

%e

Day of month (1 - 31)

%H

Hour (00 - 23)

%I

Hour (01 - 12)

%j

Day of year (001 to 365)

%m

Month of year (01 - 12)

%M

Minute (00 to 59)

%n

Insert new line

%p

String containing AM or PM

%r

Time as %I:%M:%S

%R

Time as %H:%M

%S

Seconds (00 to 59)

%t

Insert a TAB character

%T

Time as %H:%M:%S

%U

Week number within year (00 to 53) (Sunday = first day)

%w

Day number (0 - 6) (Sunday = 0)

%W

Week number within year (00 to 53) (Monday = first day)

%x

Country specific date format

%X

Country specific time format

%y

Year within century

%Y

Year as YYYY (4 digits)

%Z

Timezone name

You can place any number of the above symbols in an expression interspersed with formatting characters:

%A-%B,%Y %T e.g. Monday-July,2001 01:12:23

User Variables and Browser Type

If set (TRUE) the client is a browser from any company that supports a generic version 4 feature set, specifically it supports DHTML, CSS and Javascript 1.3 compatibility at the CODE level but the object models may be different. If not set (FALSE) the browser either does not support Javascript or supports a degenerate version of Javascript.

isIE

If set (TRUE) the client is an MSIE browser that supports Microsofts DHTML Object Model. If not set (FALSE) the browser supports the Netscape 4.x Object model (defined by Javascript 1.3).

isW3C

If set (TRUE) the client browser supports the W3C Document Obect Model Level-2. Currently this is assumed to be only MSIE 5.5+, Opera 7.54+ and browsers based on Gecko Netscape 6+. If not set (FALSE) the browser does not support the W3C DOM.

The psuedo code to process these variables is:

If isJS not set (FALSE)
Exit no DHTML, CSS or Javascript support in browser
If isW3C set
W3C CSS-2 and DOM supported
Else
If isIE set
MS DHTML DOM and CSS
Else
CSS and DOM are NS Javascript 1.3

We use these variables to generate browser specific javascript and CSS style sheets using SSI conditional elements in included files.

Example - Page Changed and Copyright Date in Footer

The page modified: and Copyright dates at the foot of every page on our site are generated by SSI in a single included footer file.

This is the contents of the included footer.shtml file (we've omitted all the formatting HTML 'cos it does not affect the technique - but use page source in your browser if you are insatiably curious) and it generates the end copyright year to reflect the current date and uses the real_date variable set in the parent page:

The LAST_MODIFIED variable is set for the page/url/uri requested by the user and is not modified to reflect any included files.

Example - Page Source Comment

We set a page source comment in every page using standard SSI elements and the variable HTTP_HOST and REQUEST_URI as shown in the following fragment. Why do we do it? - because it seemed like a good idea at the time. If you look at page source you will also see that we spell 'originated' incorrectly on almost every page (we fixed it on this page) - so much for a cut and paste technique!

Example - Absolute Page References with SSI

Which is short but when you are including files requires a different include file for every level in the hierarchy since the relative reference changes.

To keep things simple we maintain single site wide files for menus that use absolute addressing generated by SSI as shown in the fragment below (which is the same as that shown above using relative addressing):

<a href="http://<!--#echo var="HTTP_HOST" -->/tech">tech home</a><br>

This works for any level in the hierarchy and on our test (pre-publish) site.

Problems, comments, suggestions, corrections (including broken links) or something to add? Please take the time from a busy life to 'mail us' (at top of screen), the webmaster (below) or info-support at zytrax. You will have a warm inner glow for the rest of the day.