|  | <!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 | 
|  | <A HREF="../content-negotiation.html">Content Negotiation</A> | 
|  | and <A HREF="../mod/mod_include.html">eXtended Server Side Includes (XSSI)</A> | 
|  | to return error messages generated by the server in the client's | 
|  | native language. | 
|  | </P> | 
|  | <P> | 
|  | By using XSSI, all | 
|  | <A HREF="../mod/core.html#errordocument">customized messages</A> | 
|  | 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 | 
|  | are inserted in the error document at "run-time", <EM>i.e.</EM>, when the error | 
|  | 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> | 
|  | <LI>By defining the <A HREF="../mod/core.html#options">MultiViews option</A>, | 
|  | 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. | 
|  | <LI>By enabling <A HREF="../mod/mod_include.html">Server Side Includes</A> | 
|  | (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. | 
|  | <LI>The <A HREF="../mod/mod_mime.html#addhandler">AddHandler</A> and | 
|  | <A HREF="../mod/mod_mime.html#addtype">AddType</A> directives are useful | 
|  | for automatically XSSI-expanding all files with a <SAMP>.shtml</SAMP> | 
|  | suffix to <EM>text/html</EM>. | 
|  | <LI>By using the <A HREF="../mod/mod_alias.html#alias">Alias</A> 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. | 
|  | <LI>The <A HREF="../mod/core.html#directory"><Directory></A>-Block | 
|  | 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 | 
|  | <CODE>src/main/http_protocol.c</CODE> | 
|  | if you wish to see apache's standard messages), an | 
|  | <A HREF="../mod/core.html#errordocument">ErrorDocument</A> | 
|  | 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 | 
|  | (<EM>i.e.</EM>, a plain error message in english). | 
|  | <LI>Finally, the <A HREF="../mod/core.html#allowoverride">AllowOverride</A> | 
|  | directive tells apache that it is not necessary to look for | 
|  | a .htaccess file in the /errordocs directory: a minor speed | 
|  | optimization. | 
|  | </OL> | 
|  | The resulting <SAMP>httpd.conf</SAMP> configuration would then look | 
|  | 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 | 
|  | Alias  /errordocs  /usr/local/apache/errordocs | 
|  | <Directory /usr/local/apache/errordocs> | 
|  | AllowOverride none | 
|  | Options MultiViews IncludesNoExec FollowSymLinks | 
|  | AddType text/html .shtml | 
|  | AddHandler server-parsed .shtml | 
|  | </Directory> | 
|  | #    "400 Bad Request", | 
|  | ErrorDocument  400  /errordocs/400 | 
|  | #    "401 Authorization Required", | 
|  | ErrorDocument  401  /errordocs/401 | 
|  | #    "403 Forbidden", | 
|  | ErrorDocument  403  /errordocs/403 | 
|  | #    "404 Not Found", | 
|  | ErrorDocument  404  /errordocs/404 | 
|  | #    "500 Internal Server Error", | 
|  | ErrorDocument  500  /errordocs/500 | 
|  | </PRE> | 
|  | The directory for the error messages (here: | 
|  | <SAMP>/usr/local/apache/errordocs/</SAMP>) must then be created with the | 
|  | 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> | 
|  | <LI>No file errordocs/403 should exist. Otherwise, it would be found and | 
|  | served (with the DefaultType, usually text/plain), all negotiation | 
|  | 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 | 
|  | <SAMP>errordocs/403.shtml.<EM>lang</EM></SAMP> is created and | 
|  | filled with the error text in that language (<A HREF="#createdocs">see | 
|  | below</A>). | 
|  | <LI>One fallback document called <SAMP>errordocs/403.shtml</SAMP> is | 
|  | 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> | 
|  | For simplicity, the header file is simply called <CODE>head.shtml</CODE> | 
|  | 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> | 
|  | <CODE>foot.shtml.en</CODE>,<BR> | 
|  | <CODE>foot.shtml.fr</CODE>,<BR> | 
|  | <CODE>foot.shtml.de</CODE>,<BR> | 
|  | <CODE>foot.shtml</CODE> symlink to <CODE>foot.shtml.en</CODE><P> | 
|  | 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 | 
|  | (by copying or linking, <EM>e.g.</EM>, 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. | 
|  | <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 | 
|  | <SAMP>400.shtml.en</SAMP> 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. | 
|  | <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> | 
|  |  | 
|  | Here is the complete <SAMP>head.shtml</SAMP> 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 <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" | 
|  | value="http://$SERVER_NAME:$SERVER_PORT/errordocs/CorpLogo.gif" | 
|  | --><!--#set var="ALT_CorpLogo" value="Powered by Linux!" | 
|  | --><!--#else | 
|  | --><!--#set var="IMG_CorpLogo" | 
|  | value="http://$SERVER_NAME:$SERVER_PORT/errordocs/PrivLogo.gif" | 
|  | --><!--#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> | 
|  | and this is the <SAMP>foot.shtml.en</SAMP> file: | 
|  | <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> | 
|  |  |