<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE manualpage SYSTEM "../style/manualpage.dtd">
<?xml-stylesheet type="text/xsl" href="../style/manual.en.xsl"?>

<manualpage>
  <relativepath href=".." />
  <parentdocument href="./">Miscellaneous Documentation</parentdocument>

  <title>International Customized Server Error Messages</title>

  <summary>

    <note type="warning"><title>Warning:</title>
      <p>This document has not been fully updated
      to take into account changes made in the 2.0 version of the
      Apache HTTP Server. Some of the information may still be
      relevant, but please use it with care.</p>
    </note>

    <p>This document describes an easy way to provide your Apache
    HTTP Server with a set of customized error messages which take
    advantage of <a href="../content-negotiation.html">Content
    Negotiation</a> and <module>mod_include</module> to return 
    error messages generated by the server in the client's native
    language.</p>
  
  </summary>

  <section id="intro">

    <title>Introduction</title>

    <p>By using SSI, all <directive 
    module="core">ErrorDocument</directive> 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.</p>

    <p>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:</p>

    <p><img src="../images/custom_errordocs.gif"
    alt="[Needs graphics capability to display]" /></p>
    
    <p>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.</p>
  </section>

  <section id="createdir">

    <title>Creating an ErrorDocument directory</title>

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

    <ol>
      <li>By defining the MultiViews <directive 
      module="core">Options</directive>, we
      enable the language selection of the most appropriate
      language alternative (content negotiation).</li>

      <li>By setting the <directive
      module="mod_negotiation.html">LanguagePriority</directive>
      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 <module>mod_include</module>
      (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 <directive 
      module="mod_mime">AddHandler</directive> and <directive
      module="mod_mime">AddType</directive> directives
      are useful for automatically SSI-expanding all files with a
      <code>.shtml</code> suffix to <em>text/html</em>.</li>

      <li>By using the <directive
      module="mod_alias">Alias</directive> 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 <directive type="section"
      module="core">Directory</directive> 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 <directive
      module="core">ErrorDocument</directive> in
      the aliased <code>/errordocs</code> 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 <directive
      module="core">AllowOverride</directive> directive tells 
      apache that it is not necessary to look for a .htaccess file 
      in the /errordocs directory: a minor speed optimization.</li>
    </ol>

    <p>The resulting <code>httpd.conf</code> configuration would then
    look similar to this:</p>

    <note><title>Note</title>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 &lt;Directory&gt; and &lt;/Directory&gt;
    directives -but not the contained directives- must be
    omitted.</note>

<example>
  LanguagePriority en fr de <br />
  Alias  /errordocs  /usr/local/apache/errordocs <br />
  <br /> 
  &lt;Directory /usr/local/apache/errordocs&gt; <br />
  <indent>
    AllowOverride none <br />
    Options MultiViews IncludesNoExec FollowSymLinks <br />
    AddType text/html .shtml <br />
    &lt;FilesMatch "\.shtml[.$]"&gt; <br />
    <indent>
     SetOutputFilter INCLUDES <br />
    </indent>
    &lt;/FilesMatch&gt; <br />
   </indent>
   &lt;/Directory&gt; <br />
  <br />
  #    "400 Bad Request", <br />
  ErrorDocument  400  /errordocs/400 <br />
  #    "401 Authorization Required", <br />
  ErrorDocument  401  /errordocs/401 <br />
  #    "403 Forbidden", <br />
  ErrorDocument  403  /errordocs/403 <br />
  #    "404 Not Found", <br />
  ErrorDocument  404  /errordocs/404 <br />
  #    "500 Internal Server Error", <br />
  ErrorDocument  500  /errordocs/500 <br />
</example>

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

    <section id="naming">

    <title>Naming the Individual Error Document files</title>

    <p>By defining the <code>MultiViews</code> 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>

    <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
      <code>errordocs/403.shtml.<em>lang</em></code> is created and
      filled with the error text in that language (<a
      href="#createdocs">see below</a>).</li>

      <li>One fallback document called
      <code>errordocs/403.shtml</code> is created, usually by
      creating a symlink to the default language variant (<a
      href="#fallback">see below</a>).</li>
    </ul>
    </section>

    <section id="headfoot">

    <title>The Common Header and Footer Files</title>

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

    <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 SSI 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>.</p>
    
    <p><strong>By changing this file, the layout of all generated
    error messages can be changed in a second.</strong> (By
    exploiting the features of SSI, 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>

<example>
for English, French and German versions (default english) <br />
<br />
foot.shtml.en, <br />
foot.shtml.fr, <br />
foot.shtml.de, <br />
foot.shtml symlink to <br />
foot.shtml.en
</example>

    <p>Both files are included into the error document by using the
    directives <code>&lt;!--#include virtual="head" --&gt;</code>
    and <code>&lt;!--#include virtual="foot" --&gt;</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>
    </section>

    <section id="createdocs">

    <title>Creating ErrorDocuments in Different Languages</title>

    <p>After all this preparation work, little remains to be said
    about the actual documents. They all share a simple common
    structure:</p>

<example>
&lt;!--#set var="title" value="<em>error description title</em>" --&gt; <br />
&lt;!--#include virtual="head" --&gt; <br />
<indent>
 <em>explanatory error text</em> <br />
</indent>
&lt;!--#include virtual="foot" --&gt;
</example>

    <p>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.</p>
    </section>

    <section id="fallback">

    <title>The Fallback Language</title>

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

    <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>

<example>
  for f in *.shtml.en <br />
  do <br />
  <indent>
     ln -s $f `basename $f .en` <br />
  </indent>
  done
</example>

    </section>
  </section>

  <section id="proxy">

  <title>Customizing Proxy Error Messages</title>

    <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>

<example>
 &lt;!--#if expr="$REDIRECT_ERROR_NOTES = ''" --&gt; <br />
 <br />
  &lt;p&gt; <br />
  <indent>
   The server encountered an unexpected condition <br />
   which prevented it from fulfilling the request. <br />
  </indent>
  &lt;/p&gt; <br />
<br />
  &lt;p&gt; <br />
  <indent>
   &lt;a href="mailto:&lt;!--#echo var="SERVER_ADMIN" --&gt;" <br />
    SUBJECT="Error message [&lt;!--#echo var="REDIRECT_STATUS" --&gt;] &lt;!--#echo var="title" --&gt; for &lt;!--#echo var="REQUEST_URI" --&gt;"&gt; <br />
   Please forward this error screen to &lt;!--#echo var="SERVER_NAME" --&gt;'s <br />
   WebMaster&lt;/a&gt;; it includes useful debugging information about <br />
   the Request which caused the error. <br />
<br />
   &lt;pre&gt;&lt;!--#printenv --&gt;&lt;/pre&gt; <br />
  </indent>
  &lt;/p&gt; <br/>
 <br />
 &lt;!--#else --&gt; <br />
 <indent>
  &lt;!--#echo var="REDIRECT_ERROR_NOTES" --&gt;<br />
 </indent>
<br />
 &lt;!--#endif --&gt;
</example>

  </section>

  <section id="listings">

  <title>HTML Listing of the Discussed Example</title>

    <p>So, to summarize our example, here's the complete listing of
    the <code>400.shtml.en</code> 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.</p>

<example>
&lt;!--#set var="title" value="Bad Request"--&gt; <br />
&lt;!--#include virtual="head" --&gt;<br />
<br />
   &lt;p&gt; <br />
   <indent>
   Your browser sent a request that this server could not understand: <br />
   &lt;blockquote&gt; <br />
     <indent>
     &lt;strong&gt;&lt;!--#echo var="REQUEST_URI" --&gt;&lt;/strong&gt;<br />
     </indent>
   &lt;/blockquote&gt; <br />
   <br />
   The request could not be understood by the server due to malformed <br />
   syntax. The client should not repeat the request without <br />
   modifications. <br />
   </indent>
   &lt;/p&gt; <br />
<br />
   &lt;p&gt; <br />
   <indent>
   &lt;!--#if expr="$HTTP_REFERER != ''" --&gt; <br />
    <indent>
    Please inform the owner of <br />
    &lt;a href="&lt;!--#echo var="HTTP_REFERER" --&gt;"&gt;the referring page&lt;/a&gt; about <br />
    the malformed link. <br />
    </indent>
   <br />
   &lt;!--#else --&gt; <br />
    <indent>
    Please check your request for typing errors and retry. <br />
    </indent>
   <br />
   &lt;!--#endif --&gt; <br />
   </indent>
   &lt;/p&gt; <br />
<br />
&lt;!--#include virtual="foot" --&gt;
</example>

    <p>Here is the complete <code>head.shtml.en</code> file (the funny
    line breaks avoid empty lines in the document after SSI
    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 </p>

    <example>BrowserMatch "^Mozilla/[2-4]" anigif</example>

    <p>for browser types which support animated GIFs).</p>

<example>
&lt;!--#if expr="$SERVER_NAME = /.*\.mycompany\.com/" --&gt;<br />
<indent>
&lt;!--#set var="IMG_CorpLogo" value="http://$SERVER_NAME:$SERVER_PORT/errordocs/CorpLogo.gif" --&gt;<br />
&lt;!--#set var="ALT_CorpLogo" value="Powered by Linux!" --&gt;<br />
</indent>
<br />
&lt;!--#else --&gt; <br />
<indent>
&lt;!--#set var="IMG_CorpLogo" value="http://$SERVER_NAME:$SERVER_PORT/errordocs/PrivLogo.gif" --&gt; <br />
&lt;!--#set var="ALT_CorpLogo" value="Powered by Linux!" --&gt; <br />
</indent>
&lt;!--#endif--&gt; <br />
<br />
&lt;!--#set var="IMG_BgImage" value="http://$SERVER_NAME:$SERVER_PORT/errordocs/BgImage.gif" --&gt; <br />
&lt;!--#set var="DOC_Apache" value="http://$SERVER_NAME:$SERVER_PORT/Apache/" --&gt; <br />
<br />
&lt;!--#if expr="$anigif" --&gt; <br />
<indent>
&lt;!--#set var="IMG_Apache" value="http://$SERVER_NAME:$SERVER_PORT/icons/apache_anim.gif" --&gt; <br />
</indent>
&lt;!--#else--&gt;<br />
<indent>
&lt;!--#set var="IMG_Apache" value="http://$SERVER_NAME:$SERVER_PORT/icons/apache_pb.gif" --&gt;<br />
</indent>
&lt;!--#endif--&gt;<br />
<br />

&lt;!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"&gt; <br />
&lt;html&gt; <br />
&lt;head&gt; <br />
  <indent>
  &lt;title&gt; <br />
  [&lt;!--#echo var="REDIRECT_STATUS" --&gt;] &lt;!--#echo var="title" --&gt; <br />
  &lt;/title&gt; <br />
  </indent>
&lt;/head&gt; <br />
<br />
&lt;body bgcolor="white" background="&lt;!--#echo var="IMG_BgImage" --&gt;"&gt; <br />
<indent>
  &lt;h1 align="center"&gt; <br />
  [&lt;!--#echo var="REDIRECT_STATUS" --&gt;] 
  &lt;!--#echo var="title" --&gt; <br />
  &lt;img src="&lt;!--#echo var="IMG_CorpLogo" --&gt;" <br />
  &nbsp;&nbsp;alt="&lt;!--#echo var="ALT_CorpLogo" --&gt;" align="right"&gt; <br />
  &lt;/h1&gt; <br />
<br />
  &lt;hr /&gt;
  &lt;!-- ======================================================== --&gt; <br />
  &lt;div&gt;
</indent>
</example>

    <p>and this is the <code>foot.shtml.en</code> file:</p>

<example>
<indent>
  &lt;/div&gt; <br />
  &lt;hr /&gt; <br />
  <br />
  &lt;div align="right"&gt; <br />
  <indent>
  &lt;small&gt;Local Server time:
  &lt;!--#echo var="DATE_LOCAL" --&gt;&lt;/small&gt; <br />
  </indent>
  &lt;/div&gt; <br />
  <br />
  &lt;div align="center"&gt;<br />
  <indent>
    &lt;a href="&lt;!--#echo var="DOC_Apache" --&gt;"&gt; <br />
    &lt;img src="&lt;!--#echo var="IMG_Apache" --&gt;" border="0" align="bottom" <br />
    &nbsp;&nbsp;alt="Powered by &lt;!--#echo var="SERVER_SOFTWARE" --&gt;"&gt;&lt;/a&gt; <br />
  &lt;br /&gt;<br />

  &lt;small&gt;&lt;!--#set var="var" value="Powered by $SERVER_SOFTWARE -- <br />
  File last modified on $LAST_MODIFIED" --&gt; <br />
  &lt;!--#echo var="var" --&gt;&lt;/small&gt; <br />
  </indent>
  &lt;/div&gt; <br />
  <br />
  &lt;p&gt;If the indicated error looks like a misconfiguration, please inform <br />
  &lt;a href="mailto:&lt;!--#echo var="SERVER_ADMIN" --&gt;" <br />
  subject="Feedback about Error message [&lt;!--#echo var="REDIRECT_STATUS" --&gt;] <br />
  &lt;!--#echo var="title" --&gt;, req=&lt;!--#echo var="REQUEST_URI" --&gt;"&gt; <br />
  &lt;!--#echo var="SERVER_NAME" --&gt;'s WebMaster&lt;/a&gt;.<br />
  &lt;/p&gt;<br />
  </indent>
<br />
&lt;/body&gt;<br />
&lt;/html&gt;
</example>

    <p>If you have tips to contribute, send mail to <a
    href="mailto:martin@apache.org">martin@apache.org</a></p>
  </section>

</manualpage>
