| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" |
| "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| |
| <html xmlns="http://www.w3.org/1999/xhtml"> |
| <head> |
| <meta name="generator" content="HTML Tidy, see www.w3.org" /> |
| |
| <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> |
| |
| <h2>Index</h2> |
| |
| <ul> |
| <li><a href="#intro">Introduction</a></li> |
| |
| <li><a href="#createdir">Creating an ErrorDocument |
| directory</a></li> |
| |
| <li><a href="#docnames">Naming the individual error document |
| files</a></li> |
| |
| <li><a href="#headfoot">The common header and footer |
| files</a></li> |
| |
| <li><a href="#createdocs">Creating ErrorDocuments in |
| different languages</a></li> |
| |
| <li><a href="#fallback">The fallback language</a></li> |
| |
| <li><a href="#proxy">Customizing Proxy Error |
| Messages</a></li> |
| |
| <li><a href="#listings">HTML listing of the discussed |
| example</a></li> |
| </ul> |
| <hr /> |
| |
| <h2><a id="intro" name="intro">Introduction</a></h2> |
| |
| <p>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...</p> |
| |
| <ul> |
| <li>possibly are served by different virtual hosts (different |
| host name, different IP address, or different port) on the |
| server machine,</li> |
| |
| <li>show a predefined company logo in the right top of the |
| message (selectable by virtual host),</li> |
| |
| <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> |
| |
| <li>have some kind of standardized background image,</li> |
| |
| <li>display an apache logo and a feedback email address at |
| the bottom of the error message.</li> |
| </ul> |
| |
| <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 id="createdir" 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> |
| |
| <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> |
| |
| <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> |
| |
| <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> |
| |
| <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> |
| |
| <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> |
| |
| <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> |
| |
| <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.</li> |
| </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 id="docnames" 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):</p> |
| |
| <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> |
| |
| <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> |
| |
| <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>).</li> |
| </ul> |
| |
| <h3><a id="headfoot" 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> |
| |
| <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> |
| |
| <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> |
| |
| <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> |
| |
| <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> |
| |
| <p>See <a href="#listings">the listings below</a> to see an |
| actual HTML implementation of the discussed example.</p> |
| |
| <h3><a id="createdocs" 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 id="fallback" 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> |
| |
| <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> |
| |
| <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> |
| |
| <p>A simple shell script to do it (execute within the |
| errordocs/ dir):</p> |
| <pre> |
| for f in *.shtml.en |
| do |
| ln -s $f `basename $f .en` |
| done |
| </pre> |
| |
| <h2><a id="proxy" 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 id="listings" 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> |
| |