Server-Side Device Detection with Browscap

Share this article

Ensuring that websites can adapt to multiple devices – particularly mobile devices – has become both increasingly important and complex. The variety of ways in which people browse the web having mushroomed. Much of this has been driven by the rise in the use of mobile devices to access the web, although it also applies to devices such as tablets, Internet TV, and so on. In recent years, Responsive Web Design has become all the rage as a mechanism for adapting websites and applications to cater to multiple devices by using information client-side to adapt layouts accordingly. In a sense, this is a “one-size-fits-all” approach. However, there’s often a case for providing different versions of the same site or using a slightly different approach. An alternative solution to the problem is to use server-side device detection and then take certain actions based on that information. One possibility is to simply forward requests for a mobile site to a different URL. Another possibility is to adapt the layout – or indeed content – programmatically as it’s generated on the server. Taking a server-side approach is the basis of this article, which looks in detail at the Browser Capabilities Project, or Browscap for short, to provide the information on which to base these decisions.

An Introduction to User Agent Strings

Before we can look at server-side detection, we need to understand the information available to the web application which comes in the form of user agent strings. When you make an HTTP request to retrieve a page from a web browser, it includes a piece of information called the user agent string. This provides information about who’s making the request and includes a variety of elements. Typically the user agent string specifies what browser is used – it’s how analytics services can determine whether you’re using Chrome, Firefox, Safari, IE, or one of a multitude of other browsers. Furthermore, it can also provide information about the version of the requesting application. This is great for identifying the dwindling numbers of IE6 users, for example. Of course a browser is really just a specific type of application. Remember, HTTP requests aren’t just issued when your web browser accesses a web page or other resource. The request may be from a web crawler, a feed reader (ie: RSS), a validator, a library such as cURL, or one of a potentially infinite number of web service clients. The user agent string is also used to provide information about the operating system. Windows, Linux or Mac? iOS or Android? That, too is qualified with version numbers. The user agent string will often also provide information about the physical device being used, whether it is a desktop, laptop, mobile, tablet, etc. Let’s look at some examples of user agent strings: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20100101 Firefox/24.0 – This says that the request has been made by Firefox (version 24, to be specific) from an Intel-based Mac. Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25 – Safari on an iPad (running iOS6) Opera/9.80 (J2ME/MIDP; Opera Mini/9.80 (S60; SymbOS; Opera Mobi/23.348; U; en) Presto/2.5.25 Version/10.54 – Version 9 of Opera Mini on a Java-based mobile phone Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30 – Mobile Safari on an Android phone You can view hundreds of user agent strings online at useragentstring.com/pages/useragentstring.php.

Accessing Browscap Data

Considering the number of variables, the number of possible versions, and of course the ever-expanding range of physical devices being used, you’ll realize that the number of different permutations is quite large and growing every day. The Browser Capabilities Project is an attempt to catalogue these permutations. You can think of it of a database of user agent strings which associates them with some of the information which we can glean from them. In reality it’s not simply a database keyed by user agent string; it’s a series of rules and patterns used to extract relevant pieces of information, cross-referenced against known browsers, devices, and other information. PHP offers a native wrapper to Browscap data in the form of the get_browser() function. (See php.net/get-browser). However, there are two key disadvantages to its use. Because PHP doesn’t ship with a browscap.ini data file, you’ll need to manually download one and set your php.ini to point to the appropriate location. More significantly, because the database is updated regularly you’ll need to manually replace it when it does. Fortunately, there are libraries which take care of both of these issues and don’t require any additional configuration. There are a number of them available, but I recommend github.com/browscap/browscap-php.

Integrating a Browscap Library

I’m going to use the Slim Framework for the following examples. You might prefer a different framework, or indeed none at all, but as this article is specifically about Browscap I don’t want to get bogged down in fundamentals such as autoloading, routing, or middleware. The code for this article is available so you can follow along. The easiest way to get started is to use Composer to download the dependencies. There are two to start with – the Slim framework and the Browscap library. Create a project directory, and in its root a composer.json file to specify these dependencies:
{
    "require": {
        "slim/slim": "2.2.*",
        "browscap/browscap-php": "1.0.*@dev"
    }
}
(Note: At time of writing, the current version of the Slim Framework is 2.3.0. However, I’m deliberately using 2.2.x for compatibility with a layout view library that we’ll be using later.) Then run composer:
php composer.phar install
You should now have a vendor folder in your project root containing both libraries, as well as an autoloader. Create a public folder in the project root, and in that an index.php file. Start by including the autoloader:
<?php
require '../vendor/autoload.php';
Let’s now create the bare bones of a Slim application, which is beautifully simple:
$app = new SlimSlim();

$app->get('/', function () {
  print 'Hello World';
});

$app->run();
At this stage, you’ll probably want to verify that everything is installed properly by browsing to your new index page. Next, we’re going to set up the Browscap library. This takes care of three key tasks:
  • downloading the actual data file (browscap.ini)
  • processing the data to make lookups more efficient
  • keeping the data up-to-date
Since the library needs to download and create some files, we need a cache directory. Let’s keep it simple and create a cache directory in the project root – just don’t forget to make it writeable by the web server. We specify the cache directory when we construct an instance of the Browscap class:
use phpbrowscapBrowscap;

// create a new Browscap object (loads or creates the cache)
$bc = new Browscap('../cache');
Let’s check it out by grabbing the information and dumping it straight to the screen:
// get information about the current browser's user agent
$current_browser = $bc->getBrowser();

echo '<pre>';
print_r($current_browser);
echo '</pre>';
This might take a little while to run first time as the library needs to download the INI file and perform some processing on it – thankfully, subsequent requests will be considerably faster. If you take a look in the cache directory you’ll notice two files; browscap.ini is the original data file, and cache.php is the same data but optimized for quicker lookups. Different libraries process and optimise the INI data in different ways – you might wish to try a few and perform benchmark tests to see which is the most efficient.

Interpreting Browscap Data

Here’s example output from the code in the previous section:
stdClass Object
(
    [browser_name] => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36
    [browser_name_regex] => ^mozilla/5.0 (.*intel mac os x.*) applewebkit/.* (khtml, like gecko).*chrome/27..*safari/.*$
    [browser_name_pattern] => Mozilla/5.0 (*Intel Mac OS X*) AppleWebKit/* (KHTML, like Gecko)*Chrome/27.*Safari/*
    [Parent] => Chrome 27.0
    [Platform] => MacOSX
    [Win32] => 
    [Comment] => Chrome 27.0
    [Browser] => Chrome
    [Version] => 27.0
    [MajorVer] => 27
    [MinorVer] => 0
    [Beta] => 1
    [Frames] => 1
    [IFrames] => 1
    [Tables] => 1
    [Cookies] => 1
    [JavaScript] => 1
    [JavaApplets] => 1
    [CssVersion] => 3
    [Platform_Version] => unknown
    [Alpha] => 
    [Win16] => 
    [Win64] => 
    [BackgroundSounds] => 
    [VBScript] => 
    [ActiveXControls] => 
    [isMobileDevice] => 
    [isSyndicationReader] => 
    [Crawler] => 
    [AolVersion] => 0
)
What you actually see here will of course depend on your machine, the browser you’re using, and what versions. But notice that key pieces of information have been extracted into properties of the returned object, for example:
  • browser_name is the original user agent string. Notice the ambiguity – it mentions both Chrome and Safari; which one is it?
  • Browser tells us I’m using Chrome
  • Version is the version of Chrome I’m using
  • Platform tells us I’m using an Apple Macintosh
Furthermore there are a number of characteristics of my setup that are identified. Tables, Cookies, Frames, IFrames, Javascript, JavaApplets, BackgroundSounds, VBScript, and ActiveXControls provide some information about the capabilities of my browser – perhaps some more useful than others nowadays! We can glean even more information about the browser’s capabilities from CssVersion, which specifies the maximum version of the CSS specification the browser supports. isSyndicationReader and Crawler are useful for identifying specific types of application. For example, were you to write an analytics service, you may wish to use Crawler to exclude those “hits” from your visitor data. Alternatively, an application or website that caches heavily may wish to ensure it’s fresh when a request has been identified by a crawler that’s likely to be a search engine. isMobileDevice is an interesting one – and arguably the most useful. What actually constitutes a mobile device is open to interpretation, but generally speaking it will give us that all important distinction between, say, a desktop computer and a mobile phone. Dealing with the “edge cases” of isMobileDevice is outside the scope of this article, and furthermore often depends on the project. For example, should an iPad or Android tablet get the desktop of mobile version of a site – or should there be a specific tablet version? We’ve called getBrowser() without any additional arguments in the example, which means that the user agent string will be taken from the request. If you want to play around or do some additional testing, you can pass a user agent string as the first parameter, for example:
$current_browser = $bc->getBrowser(
    "Opera/9.80 (J2ME/MIDP; Opera Mini/9.80 (S60; SymbOS; Opera Mobi/23.348; U; en) Presto/2.5.25 Version/10.54"
);
Additionally, passing true as a third argument will cause the function to return the Browscap data as an array, rather than an object, as its default format.

Using Browscap to Redirect to a Mobile Site

Now let’s look at some practical examples of how you might use Browscap data. Suppose you have a separate mobile website, perhaps on a subdomain – e.g. m.example.com – or perhaps using a different TLD, e.g. example.mobi. You could simply intercept all requests to the desktop site, and redirect mobile visitors to the corresponding site by using a hook. In this case we use slim.before, which is invoked before the Slim application is run:
$app->hook('slim.before', function () use ($app) {
    // create a new Browscap object (loads or creates the cache)
    $bc = new Browscap('../cache');

    // get information about the current browser's user agent
    $current_browser = $bc->getBrowser();

    // redirect to the mobile site if this is mobile
    if ($current_browser->isMobileDevice) {		
        $url = 'http://m.example.com';
        $app->response()->redirect($url, 301);
    }
});
Of course this isn’t terribly useful in iteslf; a request for, say, http://example.com/about would be redirected to http://m.example.com, that is to say the mobile site’s homepage. However if you know that the site structure of the mobile site matches that of the desktop site, you can make it a little more useful by analyzing the requested URL:
if ($current_browser->isMobileDevice) {
    $path = $app->request()->getResourceUri();
    $url = 'http://m.example.com' . $path;
    $app->response()->redirect($url, 301);
}

Using Browscap for Layout Switching

Another common use for Browscap data is to perform server-side layout switching for desktop vs. mobile sites. Let’s take our bare-bones Slim-based application, and do exactly that. The first thing to do is update the composer.json file to specify a new dependency, a handy little library we’ll be using to implement layouts: github.com/petebrowne/slim-layout-view.
{
    "require": {
        "slim/slim": "2.2.*",
        "browscap/browscap-php": "1.0.*@dev",
        "petebrowne/slim-layout-view": "0.1.*"
    }
}
Now run:
php composer.phar update
This should install the code for a new View class which will wrap the output of our application in a layout view. Now in your project root, create a templates directory. Within that a layouts directory, and in that two files – desktop.php and mobile.php. Keeping it simple for now, desktop.php would look something like this:
<!DOCTYPE html>
<html>
 <body>
  <h1>This is the Desktop Layout</h1>
  <?php echo $yield ?>
 </body>
</html>
mobile.php would look something like this:
<!DOCTYPE html>
<html>
 <body>
  <h1>This is the Mobile Layout</h1>
  <?php echo $yield ?>
 </body>
</html>
These are extremely simple examples, but they’re enough to test out layout switching. Now change the instantiation of Slim to:
$app = new SlimSlim(array(
  'view' => 'SlimLayoutView',
  'templates.path' => '../templates',
  'layout' => 'layouts/desktop.php'
));
What we’re doing here is telling Slim to use layouts, specifying the templates directory we created a moment ago, and defaulting to our desktop layout. Now create a file in templates called home.php, for example:
<p>Hello World!</p>
And change your route to:
$app->get('/', function () use ($app) {	
  $app->render('home.php');
});
Visit the page and you should see the heading “This is the Desktop Layout” followed by “Hello World!”. Okay, now we need to add the ability to dynamically switch layouts for mobile. To do this we’re going to build some middleware. This middleware will be run during the request life cycle and will be responsible for detecting a mobile visitor and switching the layout accordingly. To create middleware, we simply extend SlimMiddleware and define a call() method. Keep in mind it’s also the responsibility of each item of middleware to call the next one in Slim. Here’s our skeleton middleware:
class LayoutSwitcherMiddleware extends SlimMiddleware
{
    public function call() {
        // get reference to application
        $app = $this->app;

        // call the next middleware
        $this->next->call();
    }
}
Now we need to add this to the application, so right after we instantiate the Slim framework, add this line:
$app->add(new DeviceSwitcherMiddleware());
If you visit the page, the device switcher should run – feel free to echo something out to verify that this is the case. Switching layouts is straightforward; all we need to do is grab the device information from the Browscap library, try and identify a mobile visitor, and if that’s the case then override the default layout. To do this, add these lines before the line $this->next->call():
// create a new Browscap object (loads or creates the cache)
$bc = new Browscap('../cache');

// get information about the current browser's user agent
$current_browser = $bc->getBrowser();

// switch to the corresponding layout if this is mobile
if (!$current_browser->isMobileDevice) {
    $app->config('layout', 'layouts/mobile.php');
}
If you visit the page using a mobile device, you should see the mobile layout being used. In practice, your desktop and mobile layouts can include separate stylesheets, handle navigation differently, include or exclude regions, and so on. The eagle-eyed among you will notice that I’ve made a big but all-to-common oversight. It’s my view – a common one, I hope – that whilst it’s all good-and-well to serve a mobile website by default, it should always be possible for a mobile user to visit the desktop version of a site if they so wish. I won’t go into too much detail, but one approach might be to provide a link with a flag – e.g. ?desktop=true, which sets a cookie that overrides the automatic layout-switching behaviour.

Combining Server-Side Layout Switching with RWD

Although server-side layout switching and responsive web design are two different approaches to the same problem, there’s no reason they can’t be used in unison. For example, there’s no reason why you cannot have different layouts for different groups of devices, but keep these layouts responsive too – after all, even among what you might classify mobile devices, the possible variation in things like screen resolution can be huge.

Another Example – a Software Website

Knowing what operating system someone is using when browsing a website can be useful too. Let’s suppose you’re building a simple website to promote an application which is available for Windows, Mac, and Linux. It’s common on such websites to simplify such a website’s download page to try and guess which version the user is looking for. For example, if you access the site using a Mac, it’d show the corresponding download link most prominently – not forgetting, of course, that people may still wish to download alternate versions. Here’s one way in which you might do this in a template file:
<?php if (!strncmp($current_browser->Platform, 'Win', 3)): ?>

<p><a href="/downloads/windows.zip" class="btn btn-large btn-primary">Download for Windows</a></p>
<p>Alternatively, download for <a href="/downloads/mac.dmg">Mac</a> or <a href="/downloads/linux.tar.gz">Linux</a>.</p>

<?php elseif (!strncmp($current_browser->Platform, 'Mac', 3)): ?>

<p><a href="/downloads/mac.dmg" class="btn btn-large btn-primary">Download for Mac</a></p>
<p>Alternatively, download for <a href="/downloads/windows.zip">Windows</a> or <a href="/downloads/linux.tar.gz">Linux</a>.</p>

<?php elseif ($current_browser->Platform == 'Linux'): ?>

<p><a href="/downloads/linux.tar.gz" class="btn btn-large btn-primary">Download for Linux</a></p>
<p>Alternatively, download for <a href="/downloads/windows.zip">Windows</a> or <a href="/downloads/mac.dmg">Mac</a>.</p>

<?php else: ?>

<p>Select your version:</p>
<ul>
 <li><a href="/downloads/windows.zip">Windows</a></li>
 <li><a href="/downloads/mac.dmg">Mac</a></li>
 <li><a href="/downloads/linux.tar.gz">Linux</a></li>
</ul>

<?php endif; ?>
You’ll find an implementation of this (with styles and icons) in the accompanying code. A Windows machine could be indicated by a variety of strings, e.g. Win2000, WinXP, WinVista, Win7… As such, we attempt to identify Windows by looking at the start of the string. Another way would be to examine the Win16, Win32 and Win64 flags in Browscap’s device data. Of course if you’re targeting different versions of Windows, for example separating Vista and Windows 7 versions, you can check for WinVista and Win7 respectively.

A Final Example

This one’s pretty self-explanatory!
$current_browser = $bc->getBrowser();
if (($current_browser->Browser == 'IE') && ($current_browser->MajorVer == 6)) {
    header('Location: http://www.ie6countdown.com/');
    exit;
}

Summary

In this article I’ve introduced the idea of server-side device detection, looking in detail at the Browser Capabilities Project (Browscap). I’ve shown how it can be used as an alternative to – or even to supplement – client-side techniques for adapting websites for multiple devices, such as Responsive Web Design. While my examples use the Slim framework, the principles remain the same whatever your preferred approach. I’ve also given you a few ideas about how else you might use Browscap data – if you can think of others, be sure to let me know in the comments! Image via Fotolia

Frequently Asked Questions (FAQs) about Server-Side Device Detection with Browscap

What is Browscap and how does it work?

Browscap, short for Browser Capabilities Project, is a community-driven initiative that maintains an up-to-date database of browser capabilities. It works by parsing the user agent string of the browser and matching it against its database to determine the browser’s properties. This includes information such as the browser’s name, version, platform, whether it supports JavaScript, and more. This information can be used to tailor the user experience based on the capabilities of their browser.

How do I install and configure Browscap?

Browscap can be installed and configured in a few simple steps. First, download the latest version of the Browscap file from the official website. Then, update your php.ini file to include the path to the Browscap file. Finally, restart your server for the changes to take effect. You can then use the get_browser() function in PHP to retrieve information about the user’s browser.

How can I use Browscap with Drupal?

The Browscap module for Drupal provides an API for accessing the Browscap database. To use it, first install and enable the module. Then, download the latest Browscap data file and place it in the appropriate directory. The module will automatically use this data to provide browser capabilities information.

How do I set the browscap.ini file?

The browscap.ini file is a configuration file that specifies the path to the Browscap data file. To set it, open your php.ini file and look for the line that starts with “browscap=”. Enter the full path to the Browscap data file after the equals sign. For example, if your Browscap data file is located at /usr/local/php/browscap.ini, the line would look like this: browscap = /usr/local/php/browscap.ini.

What is the purpose of the HTTP::Browscap module in Perl?

The HTTP::Browscap module in Perl provides an interface to the Browscap database. It allows you to query the database for information about a specific browser based on its user agent string. This can be useful for tailoring your web application to the capabilities of the user’s browser.

How can I update the Browscap data file?

The Browscap data file can be updated by downloading the latest version from the official Browscap website. Once downloaded, replace the old data file with the new one. Remember to restart your server for the changes to take effect.

What is the Browscap feature in Windows 10?

The Browscap feature in Windows 10 is not directly related to the Browscap project. It is a system file used by Internet Explorer to determine the capabilities of the browser.

How can I use Browscap to detect mobile devices?

Browscap can be used to detect mobile devices by checking the ‘ismobiledevice’ property of the browser capabilities object returned by the get_browser() function. If this property is true, the user is browsing from a mobile device.

Can Browscap detect bots and crawlers?

Yes, Browscap can detect bots and crawlers. The ‘crawler’ property of the browser capabilities object will be true if the user agent belongs to a bot or crawler.

How reliable is Browscap for device detection?

Browscap is generally reliable for device detection, but it is not foolproof. User agent strings can be spoofed, and new browsers and devices may not be immediately added to the Browscap database. However, it is regularly updated and is one of the most comprehensive sources of browser capabilities information available.

Lukas WhiteLukas White
View Author

Lukas is a freelance web and mobile developer based in Manchester in the North of England. He's been developing in PHP since moving away from those early days in web development of using all manner of tools such as Java Server Pages, classic ASP and XML data islands, along with JavaScript - back when it really was JavaScript and Netscape ruled the roost. When he's not developing websites and mobile applications and complaining that this was all fields, Lukas likes to cook all manner of World foods.

Intermediate
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week