Varnish device detection and redirect to mobile site

29. November 2011 - Cristian Andrei

It's a fact: more and more people start using their smart phones and tablets for their browsing needs. This means that popular websites need to adapt their content for these new devices or face the chance of losing some of their users.

Today, I'll share how you can achieve device detection and redirect for a website that runs Varnish in 4 easy steps.

For simplicity's sake, we will assume that the project at hand has already a mobile site set up at m.mywebsite.adrress. The following extracts are part of Varnish's configuration file (default.vcl found in "/etc/varnish") with only the device detection, redirect and error handling being displayed. Varnish is using VCL ( varnish configuration language ), a small domain-specific language designed to be used to define request handling and document caching policies. All code listed from this point onwards will be written as VCL.

Now, it's time to get dirty.

Step 1: Open the default.vcl and add the following to your vcl_recv :

sub vcl_recv { 
	[…]
	  if ((req.http.host == "mywebsite.address" || req.http.host == "www.mywebsite.address") && req.url !~ "no_redirect") {
	    call device_detection; }
	[…]
	}

There's nothing fancy here: we are checking if the incoming request is trying access your website and doesn't have the "no_redirect" flag set (the purpose of the flag is explained below, please continue reading) in order to call the device_detection subroutine.

Step 2: We shall declare the device_detection subroutine as follows:

sub device_detection { 
	  set req.http.X-Device = "pc";
	  if (req.http.User-Agent ~ "iP(hone|od)" || req.http.User-Agent ~ "Android" || req.http.User-Agent ~ "Symbian" || req.http.User-Agent ~ "^BlackBerry" || req.http.User-Agent ~ "^SonyEricsson" || req.http.User-Agent ~ "^Nokia" || req.http.User-Agent ~ "^SAMSUNG" || req.http.User-Agent ~ "^LG" || req.http.User-Agent ~ "webOS") { set req.http.X-Device = "mobile"; } if (req.http.User-Agent ~ "^PalmSource") {
	    set req.http.X-Device = "mobile";
	  }
	  if (req.http.User-Agent ~ "Build/FROYO" || req.http.User-Agent ~ "XOOM" ) {
	    set req.http.X-Device = "pc";
	  }
	  if (req.http.X-Device == "mobile") {
	    error 750 "m.mywebsite.address";
	  }
	}

The function adds a made-up header called X-Device to the request. This header is used to track what device it detected, but also serves as our way of informing the backend-server of which device was detected and, if it's a mobile device it throws a custom error that we will handle next.

Step 3: In the sub vcl_error we shall add :

sub vcl_error {
  […]
  if (obj.status == 750) {
    set obj.http.Content-Type = "text/html; charset=utf-8"; synthetic {"
   <html>
    <head>
     <script language="javascript">
       <!--
         function confirmation() {
           var answer = confirm("Press \"OK\" to be redirected to the mobile page or \"Cancel\" to continue.");
           if (answer) {
             window.location = "http://m.mywebsite.address";
           }
           else {
             window.location = "http://mywebsite.address?no_redirect";
           }
         } //-->
      </script>
    </head>
    <body onload="confirmation();">
      <p></p>
    </body>
  </html>
    "};
  }
  […]
  set obj.status = 200;
  return(deliver);
}

The "synthetic" keyword is used to produce a synthetic response body in vcl_error and it takes a single string as argument. We catch the custom error that has been thrown earlier and render a javascript popup inside an html page. Remember the "no_redirect" flag ? We set it here as some users might want, for whatever reason, to access the desktop version of the site with their mobile device. It is of crucial importance that you do set a "no_redirect" flag as if you don't, you'll find yourself in a redirect loop with no way out. 

Step 4: Save the file, restart varnish with "/etc/init.d/varnish restart" and you're done.

And the end result looks like this : 
Mearra Varnish mobile detection and redirect

Based on the above implementation, you can try to restrict desktop access to the mobile site as the latter can be aesthetically displeasing on a very large screen. You can achieve that by setting a custom error code in the device_detection subroutine and handling it the vcl_error. Also, you can create custom actions for special target devices that access your site.

Please take note of the fact that the solution I've just presented is not perfect for all scenarios and some may find that bypassing Varnish altogether for mobile devices and handling the device recognition with JS is a better and easier alternative.

Next post

Drupal project management - Life cycle of a website

Read More »

Comments

1. December 2011 - 16:47 - Oscar (not verified)

Does this produce that javascript alert on every request? I've done something similar with Varnish, by first redirecting users to the mobile site. The mobile site provides a link back to the main site. When users click that link, they get a cookie to disable future redirects to mobile.

Cristian Andrei's picture

2. December 2011 - 8:14 - Cristian Andrei

If the user presses "Cancel", it will get an alert on every request but if it presses "Ok" it won't. It does seem a bit aggressive but I guess it's OK as long as one wants to urge mobile users to access mainly the mobile site. The cookie implementation that you mention seems to be a very sensitive solution for the problem that you raised.

Cristian Andrei's picture

2. December 2011 - 8:19 - Cristian Andrei

For a popular site, Drupal's own cache system can be slow and not very friendly to the user (high loading times) compared to Varnish. If one goes down that road , and based on a relatively low amount of mobile traffic , I'd suggest bypassing Varnish altogether and just let Drupal do its thing.

Add new comment