This document describes an easy way to provide your apache
WWW server with a set of customized error messages which take
advantage of Content
Negotiation and eXtended
Server Side Includes (XSSI) to return error messages
generated by the server in the client's native language.

By using XSSI, all customized messages
can share a homogenous and consistent style and layout, and
maintenance work (changing images, changing links) is kept to a
minimum because all layout information can be kept in a single
file.
Error documents can be shared across different servers, or
even hosts, because all varying information is inserted at the
time the error document is returned on behalf of a failed
request.

Content Negotiation then selects the appropriate language
version of a particular error message text, honoring the
language preferences passed in the client's request. (Users
usually select their favorite languages in the preferences
options menu of today's browsers). When an error document in
the client's primary language version is unavailable, the
secondary languages are tried or a default (fallback) version
is used.

You have full flexibility in designing your error documents
to your personal taste (or your company's conventions). For
demonstration purposes, we present a simple generic error
document scheme. For this hypothetic server, we assume that all
error messages...

possibly are served by different virtual hosts (different
host name, different IP address, or different port) on the
server machine,

show a predefined company logo in the right top of the
message (selectable by virtual host),

print the error title first, followed by an explanatory
text and (depending on the error context) help on how to
resolve the error,

have some kind of standardized background image,

display an apache logo and a feedback email address at
the bottom of the error message.

An example of a "document not found" message for a german
client might look like this:
All links in the document as well as links to the server's
administrator mail address, and even the name and port of the
serving virtual host are inserted in the error document at
"run-time", i.e., when the error actually occurs.

For this concept to work as easily as possible, we must take
advantage of as much server support as we can get:

By defining the MultiViews option, we
enable the language selection of the most appropriate
language alternative (content negotiation).

By setting the LanguagePriority
directive we define a set of default fallback languages in
the situation where the client's browser did not express any
preference at all.

By enabling Server Side
Includes (and disallowing execution of cgi scripts for
security reasons), we allow the server to include building
blocks of the error message, and to substitute the value of
certain environment variables into the generated document
(dynamic HTML) or even to conditionally include or omit parts
of the text.

The AddHandler and AddType directives
are useful for automatically XSSI-expanding all files with a
.shtml suffix to text/html.

By using the Alias directive, we
keep the error document directory outside of the document
tree because it can be regarded more as a server part than
part of the document tree.

The <Directory>-Block
restricts these "special" settings to the error document
directory and avoids an impact on any of the settings for the
regular document tree.

For each of the error codes to be handled (see RFC2068
for an exact description of each error code, or look at
src/main/http_protocol.c if you wish to see
apache's standard messages), an ErrorDocument in
the aliased /errordocs directory is defined.
Note that we only define the basename of the document here
because the MultiViews option will select the best candidate
based on the language suffixes and the client's preferences.
Any error situation with an error code not handled
by a custom document will be dealt with by the server in the
standard way (i.e., a plain error message in
english).

Finally, the AllowOverride
directive tells apache that it is not necessary to look for a
.htaccess file in the /errordocs directory: a minor speed
optimization.

The resulting httpd.conf configuration would then
look similar to this: (Note that you can define your own
error messages using this method for only part of the document
tree, e.g., a /~user/ subtree. In this case, the configuration
could as well be put into the .htaccess file at the root of the
subtree, and the <Directory> and </Directory>
directives -but not the contained directives- must be
omitted.)

The directory for the error messages (here:
/usr/local/apache/errordocs/) must then be created
with the appropriate permissions (readable and executable by
the server uid or gid, only writable for the administrator).

By defining the MultiViews option, the server was
told to automatically scan the directory for matching variants
(looking at language and content type suffixes) when a
requested document was not found. In the configuration, we
defined the names for the error documents to be just their
error number (without any suffix).

The names of the individual error documents are now
determined like this (I'm using 403 as an example, think of it
as a placeholder for any of the configured error
documents):

No file errordocs/403 should exist. Otherwise, it would
be found and served (with the DefaultType, usually
text/plain), all negotiation would be bypassed.

For each language for which we have an internationalized
version (note that this need not be the same set of languages
for each error code - you can get by with a single language
version until you actually have translated
versions), a document
errordocs/403.shtml.lang is created and
filled with the error text in that language (see below).

One fallback document called
errordocs/403.shtml is created, usually by
creating a symlink to the default language variant (see below).

By putting as much layout information in two special "include
files", the error documents can be reduced to a bare minimum.

One of these layout files defines the HTML document header
and a configurable list of paths to the icons to be shown in
the resulting error document. These paths are exported as a set
of XSSI environment variables and are later evaluated by the
"footer" special file. The title of the current error (which is
put into the TITLE tag and an H1 header) is simply passed in
from the main error document in a variable called
title.By changing this file, the layout of all generated
error messages can be changed in a second. (By
exploiting the features of XSSI, you can easily define
different layouts based on the current virtual host, or even
based on the client's domain name).

The second layout file describes the footer to be displayed
at the bottom of every error message. In this example, it shows
an apache logo, the current server time, the server version
string and adds a mail reference to the site's webmaster.

For simplicity, the header file is simply called
head.shtml because it contains server-parsed
content but no language specific information. The footer file
exists once for each language translation, plus a symlink for
the default language.

Example: for English, French and German
versions (default english)foot.shtml.en,foot.shtml.fr,foot.shtml.de,foot.shtml symlink to
foot.shtml.en

Both files are included into the error document by using the
directives <!--#include virtual="head" -->
and <!--#include virtual="foot" -->
respectively: the rest of the magic occurs in mod_negotiation
and in mod_include.

Do we need a special handling for languages other than those we
have translations for? We did set the LanguagePriority, didn't
we?!

Well, the LanguagePriority directive is for the case where
the client does not express any language priority at all. But
what happens in the situation where the client wants one of the
languages we do not have, and none of those we do have?

Without doing anything, the Apache server will usually
return a [406 no acceptable variant] error, listing the choices
from which the client may select. But we're in an error message
already, and important error information might get lost when
the client had to choose a language representation first.

So, in this situation it appears to be easier to define a
fallback language (by copying or linking, e.g., the
english version to a language-less version). Because the
negotiation algorithm prefers "more specialized" variants over
"more generic" variants, these generic alternatives will only
be chosen when the normal negotiation did not succeed.

As of Apache-1.3, it is possible to use the
ErrorDocument mechanism for proxy error messages
as well (previous versions always returned fixed predefined
error messages).

Most proxy errors return an error code of [500 Internal
Server Error]. To find out whether a particular error document
was invoked on behalf of a proxy error or because of some other
server error, and what the reason for the failure was, you can
check the contents of the new ERROR_NOTES CGI
environment variable: if invoked for a proxy error, this
variable will contain the actual proxy error message text in
HTML form.

The following excerpt demonstrates how to exploit the
ERROR_NOTES variable within an error document:

So, to summarize our example, here's the complete listing of
the 400.shtml.en document. You will notice that it
contains almost nothing but the error text (with conditional
additions). Starting with this example, you will find it easy
to add more error documents, or to translate the error
documents to different languages.

Here is the complete head.shtml file (the funny
line breaks avoid empty lines in the document after XSSI
processing). Note the configuration section at top. That's
where you configure the images and logos as well as the apache
documentation directory. Look how this file displays two
different logos depending on the content of the virtual host
name ($SERVER_NAME), and that an animated apache logo is shown
if the browser appears to support it (the latter requires
server configuration lines of the form BrowserMatch "^Mozilla/[2-4]" anigif
for browser types which support animated GIFs).