| <!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 |
| <FilesMatch "\.shtml[.$]"> |
| SetOutputFilter INCLUDES |
| </FilesMatch> |
| </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> |
| |