Simple Apache configuration file for mobile device detection

From time to time I get questions about caching strategies and mobile devices. Definitely when you want to create different responses in the back end based on the device AND you have strong caching strategies, things get very tricky.

An example scenario in Drupal (but can relate to any CMS) is where you boost performance by storing the generated pages as static files (see Boost).

These solutions rely heavily on Apache in a way that Apache delivers the static files in case these files exist. Otherwise it passes the request to the CMS.

In order to provide a way to server different responses to mobile devices I created following Apache script:

  # Detect if a "device" query parameter is set in the request. 
  # If so, the value for that attribute forces the requesting device type.
  # If the parameter is available, set a cookie to remember for which device pages have to be generated in subsequent requests.
  # Assume three categories:
  #  - mobile-high (high end mobile devices)
  #  - mobile-low (low end mobile devices)
  #  - desktop (desktop browser)
  RewriteCond %{QUERY_STRING} ^.*device=(mobile-high|mobile-low|desktop).*$
  RewriteRule .* - [CO=device:%1:.yourdomain.tld:1000:/,E=device:%1,S=2]
  # now skip 2 RewriteRules till after the user agent detection
  # If there is no "device" attribute, search for a cookie.
  # If the cookie is available add ?device=<device-type> to the request
  RewriteCond %{HTTP_COOKIE} ^.*device=(.*)$
  RewriteRule (.*) $1?device=%1  [E=device:%1,S=2,QSA]
  #now skip till after the user agent detection
  # If no cookie or device attribute are present, check the user agent by simple user agent string matching (android and iphone only here)
  RewriteCond %{HTTP_USER_AGENT} ^.*(iphone|android).*$ [NC]
  RewriteRule (.*) $1?device=mobile-high [E=device:mobile-high,QSA,S=1]
  # Detect the user agent for lower end phones (nokia, older blackberries, ...)
  RewriteCond %{HTTP_USER_AGENT} ^.*(nokia|BlackBerry).*$ [NC]
  RewriteRule (.*) $1?device=mobile-low [E=device:mobile-low,QSA]

This scripts makes sure that every request has an extra "device" query parameter that defines the requesting device (desktop/mobile-high/mobile-low). This information can be used by the back end or any caching mechanisms!

I also added a %{ENV:device} variable so that within the Apache script more logic can be placed based on the device.

Applying to the Boost module

The above snippet works together with Boost by replacing %{server_name} by %{ENV:device}/%{server_name} in the Boost apache configuration.

 ### BOOST START ###
  AddDefaultCharset utf-8
  <FilesMatch "(\.html|\.html\.gz)$">
    <IfModule mod_headers.c>
      Header set Expires "Sun, 19 Nov 1978 05:00:00 GMT"
      Header set Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0"
  <IfModule mod_mime.c>
    AddCharset utf-8 .html
    AddCharset utf-8 .css
    AddCharset utf-8 .js
    AddEncoding gzip .gz
  <FilesMatch "(\.html|\.html\.gz)$">
    ForceType text/html
  <FilesMatch "(\.js|\.js\.gz)$">
    ForceType text/javascript
  <FilesMatch "(\.css|\.css\.gz)$">
    ForceType text/css
  # Gzip Cookie Test
  RewriteRule boost-gzip-cookie-test\.html  cache/perm/boost-gzip-cookie-test\.html\.gz [L,T=text/html]
  # GZIP - Cached css & js files
  RewriteCond %{HTTP_COOKIE} !(boost-gzip)
  RewriteCond %{HTTP:Accept-encoding} !gzip
  RewriteRule .* - [S=2]
  RewriteCond %{DOCUMENT_ROOT}/cache/perm/%{ENV:device}/%{SERVER_NAME}%{REQUEST_URI}_\.css\.gz -s
  RewriteRule .* cache/perm/%{ENV:device}/%{SERVER_NAME}%{REQUEST_URI}_\.css\.gz [L,QSA,T=text/css]
  RewriteCond %{DOCUMENT_ROOT}/cache/perm/%{ENV:device}/%{SERVER_NAME}%{REQUEST_URI}_\.js\.gz -s
  RewriteRule .* cache/perm/%{ENV:device}/%{SERVER_NAME}%{REQUEST_URI}_\.js\.gz [L,QSA,T=text/javascript]
  # NORMAL - Cached css & js files
  RewriteCond %{DOCUMENT_ROOT}/cache/perm/%{ENV:device}/%{SERVER_NAME}%{REQUEST_URI}_\.css -s
  RewriteRule .* cache/perm/%{ENV:device}/%{SERVER_NAME}%{REQUEST_URI}_\.css [L,QSA,T=text/css]
  RewriteCond %{DOCUMENT_ROOT}/cache/perm/%{ENV:device}/%{SERVER_NAME}%{REQUEST_URI}_\.js -s
  RewriteRule .* cache/perm/%{ENV:device}/%{SERVER_NAME}%{REQUEST_URI}_\.js [L,QSA,T=text/javascript]
  # Caching for anonymous users
  # Skip boost IF not get request OR uri has wrong dir OR cookie is set OR request came from this server OR https request
  RewriteCond %{REQUEST_METHOD} !^(GET|HEAD)$ [OR]
  RewriteCond %{REQUEST_URI} (^/(admin|cache|misc|modules|sites|system|openid|themes|node/add))|(/(comment/reply|edit|user|user/(login|password|register))$) [OR]
  RewriteCond %{HTTP:Pragma} no-cache [OR]
  RewriteCond %{HTTP:Cache-Control} no-cache [OR]
  RewriteCond %{HTTPS} on
  RewriteRule .* - [S=3]
  # GZIP
  RewriteCond %{HTTP_COOKIE} !(boost-gzip)
  RewriteCond %{HTTP:Accept-encoding} !gzip
  RewriteRule .* - [S=1]
  RewriteCond %{DOCUMENT_ROOT}/cache/%{ENV:device}/%{SERVER_NAME}%{REQUEST_URI}_%{QUERY_STRING}\.html\.gz -s
  RewriteRule .* cache/%{ENV:device}/%{SERVER_NAME}%{REQUEST_URI}_%{QUERY_STRING}\.html\.gz [L,T=text/html]
  RewriteCond %{DOCUMENT_ROOT}/cache/normal/%{ENV:device}/%{SERVER_NAME}%{REQUEST_URI}_%{QUERY_STRING}\.html -s
  RewriteRule .* cache/normal/%{ENV:device}/%{SERVER_NAME}%{REQUEST_URI}_%{QUERY_STRING}\.html [L,T=text/html]
  ### BOOST END ###

In your settings.php you should do the following:

 * Theme array for the mobile categories. The key equals the device category
 * supported by the site.
 * Changing this array configures the mobile theme for a certain device group
$mobile_themes = array('mobile-low' => 'bluemarine', 'mobile-high' => 'pushbutton');
 * Check if the device argument is one of the valid devices (this is just an extra check)
if(isset($_GET['device']) && array_key_exists($_GET['device'], $mobile_themes)){
	$boost_device = $_GET['device'];
	$conf['theme_default'] = $mobile_themes[$_GET['device']];  // set the theme
else {
	$boost_device = 'desktop'; 
// We configure the Boost folders to support multiple devices. 
// This corresponds to the %{ENV:device}/%{SERVER_NAME} string in the apache script
$conf['boost_normal_dir'] = 'normal/' . $boost_device ;
$conf['boost_gzip_dir'] = 'normal/' . $boost_device;
$conf['boost_perm_normal_dir'] = 'perm/' . $boost_device;
$conf['boost_perm_gz_dir'] = 'perm/' . $boost_device;
 * End configuration for boost 

Hope this gives some inspiration!