custom_errordocs.html revision 235a3e48d17b30fafb12619ee145300bdb67dd00
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<HTML>
<HEAD>
<TITLE>International Customized Server Error Messages</TITLE>
</HEAD>
<!-- Background white, links blue (unvisited), navy (visited), red (active) -->
<BODY
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#000080"
ALINK="#FF0000"
>
<!--#include virtual="header.html" -->
<H1 ALIGN="CENTER">Using XSSI and <SAMP>ErrorDocument</SAMP> to configure
customized international server error responses</H1>
<P>
<H2>Index</H2>
<UL>
<LI><A HREF="#intro">Introduction</A>
<LI><A HREF="#createdir">Creating an ErrorDocument directory</A>
<LI><A HREF="#docnames">Naming the individual error document files</A>
<LI><A HREF="#headfoot">The common header and footer files</A>
<LI><A HREF="#createdocs">Creating ErrorDocuments in different languages</A>
<LI><A HREF="#fallback">The fallback language</A>
<LI><A HREF="#proxy">Customizing Proxy Error Messages</A>
<LI><A HREF="#listings">HTML listing of the discussed example</A>
</UL>
<HR>
<H2><A NAME="intro">Introduction</A></H2>
This document describes an easy way to provide your apache WWW server
with a set of customized error messages which take advantage of
to return error messages generated by the server in the client's
native language.
</P>
<P>
By using XSSI, all
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.<BR>
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.
</P>
<P>
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.
</P>
<P>
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...
<UL>
<LI>possibly are served by different virtual hosts (different host name,
different IP address, or different port) on the server machine,
<LI>show a predefined company logo in the right top of the message
(selectable by virtual host),
<LI>print the error title first, followed by an explanatory text and
(depending on the error context) help on how to resolve the error,
<LI>have some kind of standardized background image,
<LI>display an apache logo and a feedback email address at the bottom
of the error message.
</UL>
</P>
<P>
An example of a "document not found" message for a german client might
look like this:<BR>
<IMG SRC="/images/custom_errordocs.gif"
ALT="[Needs graphics capability to display]"><BR>
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
actually occurs.
</P>
<H2><A NAME="createdir">Creating an ErrorDocument directory</A></H2>
For this concept to work as easily as possible, we must take advantage
of as much server support as we can get:
<OL>
we enable the language selection of the most appropriate language
alternative (content negotiation).
<LI>By setting the
<A HREF="/mod/mod_negotiation.html#languagepriority"
>LanguagePriority</A>
directive we define a set of default fallback languages in the situation
where the client's browser did not express any preference at all.
(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.
for automatically XSSI-expanding all files with a <SAMP>.shtml</SAMP>
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.
restricts these "special" settings to the error document directory
and avoids an impact on any of the settings for the regular document tree.
<LI>For each of the error codes to be handled (see RFC2068 for an exact
description of each error code, or look at
if you wish to see apache's standard messages), an
in the aliased <SAMP>/errordocs</SAMP> 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 <EM>not</EM> handled by a
custom document will be dealt with by the server in the standard way
directive tells apache that it is not necessary to look for
a .htaccess file in the /errordocs directory: a minor speed
optimization.
</OL>
similar to this: <SMALL>(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.)</SMALL>
<PRE>
LanguagePriority en fr de
AllowOverride none
Options MultiViews IncludesNoExec FollowSymLinks
<FilesMatch "\.shtml[.$]">
SetOutputFilter INCLUDES
</FilesMatch>
</Directory>
# "400 Bad Request",
# "401 Authorization Required",
# "403 Forbidden",
# "404 Not Found",
# "500 Internal Server Error",
</PRE>
The directory for the error messages (here:
appropriate permissions (readable and executable by the server uid or gid,
only writable for the administrator).
<H3><A NAME="docnames">Naming the individual error document files</A></H3>
By defining the <SAMP>MultiViews</SAMP> 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).
<P>
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):
<UL>
would be bypassed.
<LI>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 <EM>have</EM> translated versions), a document
filled with the error text in that language (<A HREF="#createdocs">see
below</A>).
created, usually by creating a symlink to the default language
variant (<A HREF="#fallback">see below</A>).
</UL>
<H3><A NAME="headfoot">The common header and footer files</A></H3>
By putting as much layout information in two special "include files",
the error documents can be reduced to a bare minimum.
<P>
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 <CODE>title</CODE>.<BR>
<STRONG>By changing this file, the layout of all generated error
messages can be changed in a second.</STRONG>
(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).
<P>
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.
<P>
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.<P>
<STRONG>Example:</STRONG> for English, French and German versions
(default english)<BR>
Both files are included into the error document by using the
directives <CODE><!--#include virtual="head" --></CODE>
and <CODE><!--#include virtual="foot" --></CODE>
respectively: the rest of the magic occurs in mod_negotiation and
in mod_include.
<P>
See <A HREF="#listings">the listings below</A> to see an actual HTML
implementation of the discussed example.
<H3><A NAME="createdocs">Creating ErrorDocuments in different languages</A>
</H3>
After all this preparation work, little remains to be said about the
actual documents. They all share a simple common structure:
<PRE>
<!--#set var="title" value="<EM>error description title</EM>" -->
<!--#include virtual="head" -->
<EM>explanatory error text</EM>
<!--#include virtual="foot" -->
</PRE>
In the <A HREF="#listings">listings section</A>, you can see an example
of a [400 Bad Request] error document. Documents as simple as that
certainly cause no problems to translate or expand.
<H3><A NAME="fallback">The fallback language</A></H3>
Do we need a special handling for languages other than those we have
translations for? We did set the LanguagePriority, didn't we?!
<P>
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?
<P>
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.
<P>
So, in this situation it appears to be easier to define a fallback language
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.
<P>
A simple shell script to do it (execute within the errordocs/ dir):
<PRE>
for f in *.shtml.en
do
ln -s $f `basename $f .en`
done
</PRE>
<P>
</P>
<H2><A NAME="proxy">Customizing Proxy Error Messages</A></H2>
<P>
As of Apache-1.3, it is possible to use the <CODE>ErrorDocument</CODE>
mechanism for proxy error messages as well (previous versions always
returned fixed predefined error messages).
</P>
<P>
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
<CODE>ERROR_NOTES</CODE> CGI environment variable:
if invoked for a proxy error, this variable will contain the actual proxy
error message text in HTML form.
</P>
<P>
The following excerpt demonstrates how to exploit the <CODE>ERROR_NOTES</CODE>
variable within an error document:
</P>
<PRE>
<!--#if expr="$REDIRECT_ERROR_NOTES = ''" -->
<p>
The server encountered an unexpected condition
which prevented it from fulfilling the request.
</p>
<p>
<A HREF="mailto:<!--#echo var="SERVER_ADMIN" -->"
SUBJECT="Error message [<!--#echo var="REDIRECT_STATUS" -->] <!--#echo var="title" --> for <!--#echo var="REQUEST_URI" -->">
Please forward this error screen to <!--#echo var="SERVER_NAME" -->'s
WebMaster</A>; it includes useful debugging information about
the Request which caused the error.
<pre><!--#printenv --></pre>
</p>
<!--#else -->
<!--#echo var="REDIRECT_ERROR_NOTES" -->
<!--#endif -->
</PRE>
<H2><A NAME="listings">HTML listing of the discussed example</A></H2>
So, to summarize our example, here's the complete listing of the
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.
<HR><PRE>
<!--#set var="title" value="Bad Request"
--><!--#include virtual="head" --><P>
Your browser sent a request that this server could not understand:
<BLOCKQUOTE>
<STRONG><!--#echo var="REQUEST_URI" --></STRONG>
</BLOCKQUOTE>
The request could not be understood by the server due to malformed
syntax. The client should not repeat the request without
modifications.
</P>
<P>
<!--#if expr="$HTTP_REFERER != ''" -->
Please inform the owner of
<A HREF="<!--#echo var="HTTP_REFERER" -->">the referring page</A> about
the malformed link.
<!--#else -->
Please check your request for typing errors and retry.
<!--#endif -->
</P>
<!--#include virtual="foot" -->
</PRE><HR>
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 <BR><CODE>BrowserMatch "^Mozilla/[2-4]" anigif</CODE><BR>
for browser types which support animated GIFs).
<HR><PRE>
<!--#if expr="$SERVER_NAME = /.*\.mycompany\.com/"
--><!--#set var="IMG_CorpLogo"
--><!--#set var="ALT_CorpLogo" value="Powered by Linux!"
--><!--#else
--><!--#set var="IMG_CorpLogo"
--><!--#set var="ALT_CorpLogo" value="Powered by Linux!"
--><!--#endif
--><!--#set var="IMG_BgImage" value="http://$SERVER_NAME:$SERVER_PORT/errordocs/BgImage.gif"
--><!--#set var="DOC_Apache" value="http://$SERVER_NAME:$SERVER_PORT/Apache/"
--><!--#if expr="$anigif"
--><!--#set var="IMG_Apache" value="http://$SERVER_NAME:$SERVER_PORT/icons/apache_anim.gif"
--><!--#else
--><!--#set var="IMG_Apache" value="http://$SERVER_NAME:$SERVER_PORT/icons/apache_pb.gif"
--><!--#endif
--><!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<HTML>
<HEAD>
<TITLE>
[<!--#echo var="REDIRECT_STATUS" -->] <!--#echo var="title" -->
</TITLE>
</HEAD>
<BODY BGCOLOR="white" BACKGROUND="<!--#echo var="IMG_BgImage" -->"><UL>
<H1 ALIGN="center">
[<!--#echo var="REDIRECT_STATUS" -->] <!--#echo var="title" -->
<IMG SRC="<!--#echo var="IMG_CorpLogo" -->"
ALT="<!--#echo var="ALT_CorpLogo" -->" ALIGN=right>
</H1>
<HR><!-- ======================================================== -->
<DIV>
</PRE><HR>
<HR><PRE>
</DIV>
<HR>
<DIV ALIGN="right"><SMALL><SUP>Local Server time:
<!--#echo var="DATE_LOCAL" -->
</SUP></SMALL></DIV>
<DIV ALIGN="center">
<A HREF="<!--#echo var="DOC_Apache" -->">
<IMG SRC="<!--#echo var="IMG_Apache" -->" BORDER=0 ALIGN="bottom"
ALT="Powered by <!--#echo var="SERVER_SOFTWARE" -->"></A><BR>
<SMALL><SUP><!--#set var="var"
value="Powered by $SERVER_SOFTWARE -- File last modified on $LAST_MODIFIED"
--><!--#echo var="var" --></SUP></SMALL>
</DIV>
<ADDRESS>If the indicated error looks like a misconfiguration, please inform
<A HREF="mailto:<!--#echo var="SERVER_ADMIN" -->"
SUBJECT="Feedback about Error message [<!--#echo var="REDIRECT_STATUS"
-->] <!--#echo var="title" -->, req=<!--#echo var="REQUEST_URI" -->">
<!--#echo var="SERVER_NAME" -->'s WebMaster</A>.
</ADDRESS>
</UL></BODY>
</HTML>
</PRE><HR>
<H3>More welcome!</H3>
If you have tips to contribute, send mail to <A
HREF="mailto:martin@apache.org">martin@apache.org</A>
<!--#include virtual="footer.html" -->
</BODY>
</HTML>