Drupal + Varnish + Mobile: Your Device Detection Dilemma

Device detection can be tricky, even without caching. The standard approach in Drupal involves Browscap, Mobile Tools, or another method of allowing Drupal to handle device detection and redirection. This works really well in most cases, especially if it's a simple configuration with no special caching layers. Introduce Drupal and Mobile Tools to a caching reverse proxy like Varnish, and everything changes.

Give me the right page!

First, forget about Mobile Tools and device detection with Drupal. Why? Varnish remembers whatever Drupal returns. Suppose a mobile user is the first to visit a page. Drupal detects the mobile device and, if you have a separate mobile site, will redirect the visitor there. Varnish sees that redirect and faithfully serves it to subsequent visitors, regardless of device type. Not good.

Well, why not leave Mobile Tools in place and do device detection with Varnish? This can actually have the same result, albeit rarely. Unless you have identical device matches in Mobile Tools and Varnish, which is redundant and time-consuming, the wrong page will occasionally be delivered to visitors. Even after extensive testing, you may not discover this discrepancy.

So, we should give up?

No. Device detection with Varnish is easy! Cut the Mobile Tools cord, and dust off your regex-fu, or just copy and paste the following example code.

Nota bene: these snippets assume you have Varnish configured and working, that your mobile site is not delivered through the same Varnish system, and that you don't mind periodically updating device matches -- don't worry, it's not that bad.

Varnish device detection examples

We want to handle the redirect first, so put the following at the top of your sub vcl_recv. If you add devices to this match, don't be too aggressive, otherwise you might inadvertently redirect non-mobile visitors to the mobile site, which is worse than the other way around.


sub vcl_recv {
  # Allow browser detection if parameter or cookie not set
  if( !( req.url ~ "\?desktop$" || req.http.Cookie ~ "prefdesktop=1") ) {
    # Check for mobile device
    if( req.http.User-Agent ~ "(?i)android.+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|meego.+mobile|midp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|windows (ce|phone)|xda|xiino" ) {
        error 750 "Moved Temporarily";
    }
  }
}

Let's set the desktop preference cookie, by putting the following at the top of your sub vcl_fetch. Be sure to change "yourdomain.com" to your actual domain.


sub vcl_fetch {
  # Looks for ?desktop parameter at the end of URL
  # If there are other parameters, this regexp may require adjustment
  if( req.url ~ "\?desktop$" ) {
    # Set desktop preference cookie
    set beresp.http.Set-Cookie = "prefdesktop=1; domain=.yourdomain.com; path=/";
  }
}

Lastly, where are we redirecting mobile users who don't prefer the full site? Put something like the following at the top of your sub vcl_error. Again, be sure to change "yourdomain.com" to your actual domain.


sub vcl_error {
  if( obj.status == 750) {
    set obj.http.Location = "http://m.yourdomain.com" + req.url;
    set obj.status = 302;
    return(deliver);
  }
}

That wasn't so bad, was it? Now you should be able to safely disable Mobile Tools or any other Drupal-side device detection. Congrats!

Other thoughts

If you take a responsive design approach, implementing caching and Varnish is considerably simpler, and can side-step caching layer device detection altogether.