• 12/10/2003

International Customized Server Error Messages



This document describes an easy way to provide your apache WWW server with a set of customized error messages which take advantage of Content Negotiation and eXtended Server Side Includes (XSSI) to return error messages generated by the server in the client’s native language.

By using XSSI, all customized 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.
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.

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.

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…

  • possibly are served by different virtual hosts (different host name, different IP address, or different port) on the server machine,
  • show a predefined company logo in the right top of the message (selectable by virtual host),
  • print the error title first, followed by an explanatory text and (depending on the error context) help on how to resolve the error,
  • have some kind of standardized background image,
  • display an apache logo and a feedback email address at the bottom of the error message.

An example of a “document not found” message for a german client might look like this:

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.

Creating an ErrorDocument directory

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

  1. By defining the MultiViews option, we enable the language selection of the most appropriate language alternative (content negotiation).
  2. By setting the LanguagePriority directive we define a set of default fallback languages in the situation where the client’s browser did not express any preference at all.
  3. By enabling Server Side Includes (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.
  4. The AddHandler and AddType directives are useful for automatically XSSI-expanding all files with a .shtml suffix to text/html.
  5. By using the Alias 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.
  6. The -Block restricts these “special” settings to the error document directory and avoids an impact on any of the settings for the regular document tree.
  7. For each of the error codes to be handled (see RFC2068 for an exact description of each error code, or look at src/main/http_protocol.c if you wish to see apache’s standard messages), an ErrorDocument in the aliased /errordocs 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 not handled by a custom document will be dealt with by the server in the standard way (i.e., a plain error message in english).
  8. Finally, the AllowOverride directive tells apache that it is not necessary to look for a .htaccess file in the /errordocs directory: a minor speed optimization.

The resulting httpd.conf configuration would then look similar to this: (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 and directives -but not the contained directives- must be omitted.)

  LanguagePriority en fr de 
  Alias  /errordocs  /usr/local/apache/errordocs
   AllowOverride none
   Options MultiViews IncludesNoExec FollowSymLinks
   AddType text/html .shtml
   AddHandler server-parsed .shtml
  #    "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

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

Naming the individual error document files

By defining the MultiViews 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).

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

  • No file errordocs/403 should exist. Otherwise, it would be found and served (with the DefaultType, usually text/plain), all negotiation would be bypassed.
  • 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 have translated versions), a document errordocs/403.shtml.lang is created and filled with the error text in that language (see below).
  • One fallback document called errordocs/403.shtml is created, usually by creating a symlink to the default language variant (see below).

The common header and footer files

By putting as much layout information in two special “include files”, the error documents can be reduced to a bare minimum.

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 title.
By changing this file, the layout of all generated error messages can be changed in a second. (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).

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.

For simplicity, the header file is simply called head.shtml 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.

Example: for English, French and German versions (default english)
foot.shtml symlink to foot.shtml.en

Both files are included into the error document by using the directives and respectively: the rest of the magic occurs in mod_negotiation and in mod_include.

See the listings below to see an actual HTML implementation of the discussed example.

Creating ErrorDocuments in different languages

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

 explanatory error text

In the listings section, 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.

The fallback language

Do we need a special handling for languages other than those we have translations for? We did set the LanguagePriority, didn’t we?!

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?

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.

So, in this situation it appears to be easier to define a fallback language (by copying or linking, e.g., 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.

A simple shell script to do it (execute within the errordocs/ dir):

  for f in *.shtml.en
     ln -s $f `basename $f .en`

Customizing Proxy Error Messages

As of Apache-1.3, it is possible to use the ErrorDocument mechanism for proxy error messages as well (previous versions always returned fixed predefined error messages).

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 ERROR_NOTES CGI environment variable: if invoked for a proxy error, this variable will contain the actual proxy error message text in HTML form.

The following excerpt demonstrates how to exploit the ERROR_NOTES variable within an error document:

The server encountered an unexpected condition which prevented it from fulfilling the request.