[an error occurred while processing this directive] [an error occurred while processing this directive] Tech Stuff - Server Side Browser Sniffing [an error occurred while processing this directive] [an error occurred while processing this directive] [an error occurred while processing this directive]
[an error occurred while processing this directive]

Tech Stuff - Server Side Browser Sniffing

We decided a long time ago to generate browser specific web pages to reduce page overheads, to simplify Javascript development and page layout. This page describes what we do and how we do it. We have written an opinion piece describing our reasoning and approach to building this web site.

This is an extract from a more detailed page on configuring Apache for SSI and browser sniffing.

In essence our technique uses server wide variables that can be interrogated by any hosted web site to generate unique - browser specific - page content or format. We now use a trivial variation of this technique to generate Printer Friendly pages using SSI generated CSS definitions.

Configure Apache Browser Detection

Apache really is very powerful software with more features than you can shake a stick at. Apache can set some variables that you can then use to generate content using SSI conditional directives.

To perform this magic you use the Apache directives 'BrowserMatchNoCase' or 'BrowserMatch' which apply a regular expression check to the Apache environmental variable HTTP_USER_AGENT which is supplied by every browser. For a survival guide to regular expresions read the grep man page under unix or our page on this exotic subject. Full list of Apache environmental variables.

By judicious checking you can find out the browser, its version, its platform even and anything else that may take your fancy. We have never seen a comprehensive list of Browser ID (or user-agent) strings so we started one.

In our case we added browser detection checks to the General Section to provide a single consistent set of variables to all the web sites on our server (and hopefully reduce server load by doing it once and in one place). You could of course, by placing these lines inside a <vitualhost> section, limit them to that host only.

We modified the 'General section' of httpd.conf as follows:

 
BrowserMatchNoCase Mozilla/[4-6] isJS
BrowserMatchNoCase MSIE isIE
BrowserMatchNoCase Gecko isW3C
BrowserMatchNoCase MSIE.((5\.[5-9])|([6-9])) isW3C
BrowserMatchNoCase (W3C_|validator) isW3C
BrowserMatchNoCase (iphone|[mM]obile) isMob

Complete explanation of these tests and an overview of regular expressions.

Now you can restart your Apache server 'cos you are ready for some action.

Server Side Browser Specific Page Generation

To generate code conditionally you can now use the above variables in SSI's, PHP, Ruby, PERL, Python or any other scripting language or service.

The rest of this page illustrates this technique using Server Side Includes (SSI).

You can you place SSI statements in any file with a .shtml, .htm or .html file (we use Apache's 'XHackBit On' directive for this purpose). SSI statements look like HTML comments and take the general format (full SSI list)

<!--#element attribute=value attribute=value ... -->

There MUST NOT be a space between the '#element' and the second opening '-' and there MUST BE a space between the last character of the last 'attribute=value' pair and the first closing '-'.

We make extensive use of only two elements, include and conditional expressions but have included a complete SSI statement description.

The 'include' element

The following shows a sample of one of our standard template .htm files, we just load it and 'save as' the target file name and then add the content:

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<meta http-equiv="Content-Language" content="en-us">
<meta http-equiv="Content-Type" content="text/html">
<meta name="GENERATOR" content="company name">
<!--#include virtual="/templates/meta.html" -->
<title>Level 1 template</title>
<!-- conditionally generated style sheet -->
<!--#include virtual="/templates/styles.shtml" -->
<!-- conditionally generated javascript code -->
<!--#include virtual="/scripts/js.shtml" -->
</head>
<body>
<!-- banner/page headings -->
<!--#include virtual="/templates/level_1.shtml" -->

<div class="page-content">
  
<!-- unique page contents go here -->
   
</div>
<!--#config timefmt="%B %d %Y" -->
<!--#set var=\"real_date\" value=\"$LAST_MODIFIED\" -->
<!--#include virtual="/templates/footer.shtml" -->
</body>
</html>

We always include the css style sheet, the javascript code, the page header and the page footer from standard files. You can use the <LINK> tag for the style sheet and the 'src=file.js' in the <SCRIPT> section but we chose to minimise the number of web server accesses at the cost of exposing all our amazing code!!

The #config and #set lines before the #include for the "footer.shtml" file is our way ensuring that the LAST_MODIFIED date is for this file not the footer file. We format the date (month day, year), save the current LAST_MODIFIED to the variable 'real_date' and then use "#echo var="real_date" in the footer file (more info on SSI statements including this technique).

Nesting SSI Expressions

The more observant among you may have noticed that some include files have an .shtml suffix and others not. If you want to use SSI expressions in an #included file then that file MUST have a .shtml suffix (even if you are using the XBitHack). We did not find this out until very late and had to modify #include lines in over 400 html files (well actually we found a fantasic 10 line PHP script to do it for us which we then enhanced).

SSI conditional expressions

The following code snippet shows how we generate browser conditional css style data from a styles.shtml file (remember we are nesting SSI directives so must use a .shtml file suffix):


<!--#if expr="${isW3C} && !${isIE}" -->
/*  W3C compliant browsers - currently just Gecko */
.n-l-s {font:bold 9pt Verdana,sans-serif;
        text-decoration:none}
.popup {font:bold small Verdana,sans-serif;
       background:cyan;
       color:black;text-indent:4;
       text-decoration:none;}
<!--#elif expr="${isIE}" -->
/*  Explorer and other DHTML browsers */
.n-l-s {font-family:Verdana,sans-serif;
        font-size:7pt;font-weight:bold;text-decoration:none}
.popup {font-family:Verdana,sans-serif;font-size:small;
       font-weight:bold;background:cyan;
       color:black;text-indent:4;
       text-decoration:none;}
<!--#else -->
/* NS4.x CSS and display options here */
.n-l-s {font-family:Verdana,sans-serif;
       font-size:8pt;font-weight:bold; text-decoration:none}
.popup {position:relative;font-family:Verdana,sans-serif;
       font-size:small;font-weight:bold;
       background:blue;color:white;
       text-decoration:none;
       text-indent:4;}
#lnav0 {position:relative;top:0;left:0;}
<!--#endif -->

The above code is trivial but can get a tad more complex if you add additional capability. We currently support the W3C'ish DOM (Gecko + Opera 7.54+), the MS DOM (IE 5.5+) and are rapidly removing our support for NS4.x. In the above simple example we test the variables 'isW3C' and 'isIE' (which Apache has kindly set for us above) and directly branch to the #else or #endif statements.

Using variations of this technique can allow you do do almost any conditional page generation you wish. Does life get any better than that?

[an error occurred while processing this directive]
[an error occurred while processing this directive]
[an error occurred while processing this directive] [an error occurred while processing this directive]
[an error occurred while processing this directive] [an error occurred while processing this directive]