the end of the net: personal stuff computer stuff topography genealogy Lego

Serving up XHTML

How to return the appropriate mime-type using Apache content negotiation

Nowadays, a lot of people write XHTML pages, and take care to validate them, which is great. However, in most cases browsers don't actually get to take advantage of all these really great XHTML pages because as far as they are concerned, the pages are just plain old HTML. This is because in most cases, XHTML pages are served up by the server with the Content-Type set to text/html, which is not the correct one for XHTML.

XHTML is different from HTML (4) in other ways than just the syntactic closing of tags and quoting of all attributes. It also has slightly different rules for applying CSS, and it disallows some common javascript features, such as document.write (probably because that fraks up the DOM which would be a shame after all the carefully balanced tagging that's been done).

Now, it would be great if we could just set the Content-Type to be application/xhtml+xml for XHTML pages. Well, you can, if you want to make your website become useless for the 70% of the internet population that uses IE6 or some other browser that doesn't know that Mime type.

So, you have two choices: either stick with text/html, or move to application/xhtml+xml where possible. There are several ways to do the latter, and the best way is to rely on the 'Accept' request header. This header consists of a list of Mime-Types that a client can handle, and its preference for each of these. A lot of sample code is available [PHP] for adding a switch on your page dependent on this Accept header,

A method on Apache webservers that doesn't require any coding - or, rather, it uses code written into the server software - is to use content-negotiation to serve up application/xhtml+xml for clients that want it, and text/html for clients that don't. I'm using the following entries in my httpd.conf file:

<Directory "[.....]/EndOfTheNet">
    Options Includes Indexes FollowSymLinks MultiViews
    AllowOverride None
    Order allow,deny
    Allow from all
</Directory>

AddType "text/html" .shtml
AddHandler server-parsed .shtml

AddType "application/xhtml+xml" .xhtml
AddHandler server-parsed .xhtml

So, I now have two extensions that both support server-side includes, but they are served up as different mime types. For URL's that have two corresponding 'resources' on the server, content-negotation will cause one of the two resources to be sent back to the client, If a client (browser) has a preference for application/xhtml+xml then the .xhtml file will be returned, and the content type will be set to application/xhtml+xml, otherwise the .shtml version is returned, as text/html. By using server-side includes to have the same content appear in both versions, you don't actually need two completely separate files. Note that this also makes it easy to include an xml-prolog in the .xhtml version, allowing you to set character encodings etc.