[an error occurred while processing this directive] [an error occurred while processing this directive]
This page describes the functionality offered by, and the use of, Apache Server Side Includes (SSI and XSSI) (how to configure Apache to support SSI).
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.
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:
NOTE: You cannot include SSI in a file named with a .php suffix (or whatever you have set your AddType values to for PHP) irrespective of the setting of XBitHack, instead use this technique.
Our goal is that with either a few simple changes to the CSS styles page and or the format of SSI included files we can change, more or less, the overall look and feel of the site very rapidly. SSIs offer, as well as the standard Apache environmental variables, additional SSI specific variables. These variables may be displayed at run time using the #printenv SSI element. Display them for our server using #printenv or the tidy PHP version.
Note: You can avoid the need to use .shtml file suffixes by the use of the XBitHack Apache Directive.
The generic format of an Apache SSI is:
<!--#element attribute=value attribute=value ... -->
There MUST NOT be a space between the '#element' and the second opening '-' and there MUST BE a space between the last character of the last 'attribute=value pair and the first closing '-'.
The element types are:
Directive | Function |
config | formatting control. |
include | in-line inclusion of files including nested files. |
flow control | IF, ELIF, ELSE, ENDIF constructs for conditional processing. |
set | sets variables that may be used by CGI or SSI processing. |
printenv | displays all current variables. |
echo | displays contents of a variable. |
exec | executes a shell or cgi script |
fsize | provides the size of a file. |
flastmod | last modification date of a file. |
time & date | time and date formats. |
Element | Attributes | Meaning |
config | 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.
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. |
<!--#include virtual="../../styles.shtml" -->
Includes the file styles.shtml (which because it is named .shtml may contain additional SSI statements) in this parsed file.
SSI enables conditional processing using any of the environmental or user defined variables. SSI provides the following elements to support this feature:
#if | expr="test_condition" The test_condition may take any of the forms defined here. If the test_condition result is TRUE then the lines following this element are included in the output until either an #elif (if present), #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. |
#elif | expr="test_condition" The test_condition may take any of the forms defined here. 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. |
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:
DocumentRoot "/usr/local/www" <Directory "/usr/local/www"> ... # Options Indexes IncludesNOEXEC FollowSymLinks # SSILegacyExprParser on ... </Directory>
test_condition | Result |
string1 | TRUE if string NOT empty. |
string1=string2 | TRUE if string1 is equal to string2. |
string1!=string2 | TRUE if string1 is NOT equal to string2. |
string1<string2 | TRUE if string1 is less than string2. |
string1>string2 | TRUE if string1 is greater than string2. |
string1>=string2 | TRUE if string1 greater than or equal to string2. |
(test_condition) | TRUE if test_condition TRUE. |
!test_condition | TRUE if test_condition FALSE. |
test_condition1 && test_condition2 | TRUE if test_condition1 AND test_condition2 TRUE. |
test_condition1 || test_condition2 | TRUE if test_condition1 OR test_condition2 TRUE. |
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/".
Conditional nesting. Don't. you cannot nest SSI conditional statements.
Examples
<!--#if expr="${isJS}" --> ... code for Javascript ... .... <!--#if expr="${isW3C}" --> ..... code for W3C DOM here .... ... <!--#elif expr="${isIE}" --> ... code for MS DHTML DOM .... .... <!--#else --> ... code for NS4.x DOM here ... .... <!--#endif
The above implements the psuedo code shown above for processing Browser user defined variables.
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" |
Examples
<!--#set var="platform" value="Windows" --> <!--#set var="real_mod_date" value="$LAST_MODIFIED" -->
Notes
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.
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.
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 enoding 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. |
<!--#echo var="really" --> <a href="<!--#echo var="HTTP_REFERER" -->">Go Back</a> <a href="http://<!--#echo var="HTTP_HOST" -->/abs/ref.html">Hyperlink</a>
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.
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. |
<!--#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.
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. |
<p>Download size=<!--#fsize file="download.zip" --></p>
Example illustrates use of the element for information.
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. |
<p>expenses updates <!--#flastmod virtual="../expenses.html" -->.</p>
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
We set global Apache variables that describe the client browser environment. If you want more info about what we do.
Variable | Meaning |
isJS | 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.
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.
Each page on our site is laid out as shown below:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>Apache - Server Side Includes</title> <!-- include files covering page source, meta data, javascript, CSS and page banners --> <body> <!-- page body text in enclosing div --> <div class="l-c"> stunning page content </div> <!-- include left and right menus --> <!-- site wide footer --> <!--#include virtual="../../templates/footer.shtml" --> </body> </html>
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:
<!-- standard footer full width --> <!-- set time format to 4 digit year --> <!--#config timefmt="%Y" --> Copyright © 1994 - <!--#echo var="DATE_LOCAL" --> ZyTrax, Inc. <!-- format date --> <!--#config timefmt="%B %d %Y" --> Page modified: <!--#echo var="LAST_MODIFIED" -->.
The LAST_MODIFIED variable is set for the page/url/uri requested by the user and is not modified to reflect any included files.
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!
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>Apache - Server Side Includes</title> <!-- page source statement --> <!-- this page originated from http://<!--#echo var="HTTP_HOST" --><!--#echo var="REQUEST_URI" --> --> <!-- include files covering meta data javascript, CSS and page banners --> <body> <!-- page body text in enclosing div --> <div class="l-c"> stunning page content </div> <!-- include left, right menus and footer --> </body> </html>
Relative references in href attributes typically take the form:
<a href="../tech">tech home</a><br>
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.
[an error occurred while processing this directive]