Using Apache's mod_rewrite for Maintaining Advanced and Classic Mobile Websites

These days it's not enough to just have a single mobile website. With fragmentation of handheld devices, operating systems, and browsers, your site's visitors have a wider range of a viewing capabilities than ever. My HTC Incredible for example, running Android 2.2, delivers a much richer browsing experience than my old BlackBerry Curve (you know the one - everyone used to have it in the pre-iPhone days - it's the one everyone was playing BrickBreaker on while riding the subway). To be truly considerate of your site's visitors, you should have a feature-rich "advanced" mobile site for advanced handhelds (iPhone, iPad, Android, etc.), and a simpler, more text-centric "classic" site for older handhelds. The challenge, then, is how to route users to the appropriate site without annoying them. This is where Apache's .htaccess and mod_rewrite come in handy.

Put very simply, .htaccess is a file you can put in any directory under web root for controlling file and directory configuration. (If you want a better understanding, see the official Apache documentation and then come back here.) One way to maintain advanced and classic websites is to create separate html files for each version, and use .htaccess to display the appropriate version based on the user's requests. Specifically, create a mod_rewrite script in .htaccess that will rewrite the user's request to the correct version of the file.

One easy way to keep the files straight is to give the advanced files a "_advanced" suffix, and the classic files a "_classic" suffix. The following example should hopefully make things clear.

Let's say these are the pages that can be requested by a user:

  • index.php
  • about.php
  • contact.php

Then these are the advanced pages that can be displayed:

  • index_advanced.php
  • about_advanced.php
  • contact_advanced.php

And these are the classic pages that can be displayed:

  • index_classic.php
  • about_classic.php
  • contact_classic.php

The challenge is to get mod_rewrite to take the user's request for http://example.com/index.php and display either http://example.com/index_advanced.php (if the advanced version is desired) or http://example.com/index_classic.php (if the classic version is desired). Similarly, mod_rewrite should take the user's request for http://example.com/about.php and display either http://example.com/about_advanced.php or http://example.com/about_classic.php.

The first step is to determine which version to display to a new visitor. In pseudocode, this is:

If user agent is iPhone, iPod, or Android,
Display the advanced version

Else display the classic version

Making use of the HTTP_USER_AGENT server variable, you can route traffic accordingly with this mod_rewrite code:

RewriteCond %{HTTP_USER_AGENT} ^.*(iP(hone|od)|Android).*$ [NC]
RewriteRule ^([^_]+)\.php$ $1_advanced\.php [NC,L]

RewriteRule ^([^_]+)\.php$ $1_classic\.php [NC,L]

Taking this one step further, you can cookie the user so that you don't have to detect the user agent every time. Here is pseudocode:

If the user is cookied to the classic site,
Display the classic version

Else if the user is cookied to the advanced site,
Display the advanced version

Else if the user doesn't have a cookie and the user agent is iPhone, iPod, or Android,
Display the advanced version and cookie the user to the advanced version

Else display the classic version and cookie the user to the classic version

If you give the cookie the name "view" and give its values the options of "advanced" and "classic," then in mod_rewrite code, this is:

RewriteCond %{HTTP_COOKIE} view=classic [NC]
RewriteRule ^([^_]+)\.php$ $1_classic\.php [NC,L]

RewriteCond %{HTTP_COOKIE} view=advanced [NC]
RewriteRule ^([^_]+)\.php$ $1_advanced\.php [NC,L]

RewriteCond %{HTTP_USER_AGENT} ^.*(iP(hone|od)|Android).*$ [NC]
RewriteRule ^([^_]+)\.php$ $1_advanced\.php [CO=view:advanced:example.com,NC,L]

RewriteRule ^([^_]+)\.php$ $1_classic\.php [CO=view:classic:example.com,NC,L]

Now if you're truly kind, you'll want to allow the user to access the advanced site, even if you're pretty darn sure they're phone can't handle it properly. And correspondingly, you should probably also let the advanced user view the classic site. One way to do that is to use query parameters. The classic site should have a link to the advanced site with the parameter "view=advanced" and the advanced site should have a link to the classic site with the parameter "view=classic". Building on what we have come up with previously, the full pseudocode would then be:

If there is a "view=classic" query parameter,
Display the advanced version and cookie the user to the advanced version

Else if there is a "view=advanced" query parameter,
Display the classic version and cookie the user to the classic version

Else if the user is cookied to the classic site,
Display the classic version

Else if the user is cookied to the advanced site,
Display the advanced version

Else if the user doesn\'t have a cookie and the user agent is iPhone, iPod, or Android,
Display the advanced version and cookie the user to the advanced version

Else display the classic version and cookie the user to the classic version

Then the full, final mod_rewrite code looks like this:

RewriteCond %{QUERY_STRING} (^|&)view=classic(&|$) [NC]
RewriteRule ^([^_]+)\.php$ $1_classic\.php [CO=view:classic:example.com,NC,L]

RewriteCond %{QUERY_STRING} (^|&)view=advanced(&|$) [NC]
RewriteRule ^([^_]+)\.php$ $1_advanced\.php [CO=view:advanced:example.com,NC,L]

RewriteCond %{HTTP_COOKIE} view=classic [NC]
RewriteRule ^([^_]+)\.php$ $1_classic\.php [NC,L]

RewriteCond %{HTTP_COOKIE} view=advanced [NC]
RewriteRule ^([^_]+)\.php$ $1_advanced\.php [NC,L]

RewriteCond %{HTTP_USER_AGENT} ^.*(Firefox|Chrome).*$ [NC]
RewriteRule ^([^_]+)\.php$ $1_advanced\.php [CO=view:advanced:example.com,NC,L]

RewriteRule ^([^_]+)\.php$ $1_classic\.php [CO=view:classic:example.com,NC,L]

And there you have it.

Important 4/18 Update! Mobile browsers have a tendency to ignore cache-control html directives, so you should also make sure the Apache mod_headers module is active, and add this line to your .htaccess:

Header set Cache-Control no-cache

Otherwise you're going to run into caching issues on iPhones and Androids and you'll give yourself a headache trying to figure out why your rewrite script isn't working!

Notes:

  • Once your mod_rewrite script is fully tested and working the way you want, you should consider moving it into a <Directory> block in Apache's config (httpd.conf) file, if you are able to. It's much more efficient that way - see the Apache .htaccess documentation for more info.
  • When developing on localhost, writing cookies with mod_rewrite can be tricky. Though Apache may send a cookie to your browser, if it's not formatted correctly, your browser may reject it. Doing some research, I found that you can put a blank space in the domain field in the cookie flag, and enclose the entire flag directive in double quotes, and it should work (well, it works on Firefox and Chrome but not on IE6, but are you really surprised by that?). So instead of [CO=view:classic:example.com,NC,L] you can use "[CO=view:classic: ,NC,L]"

Finally, here are some links I found useful during my development and testing:

Category:

Comments

Cookies on localhost

"I found that you can put a blank space in the domain field in the cookie flag, and enclose the entire flag directive in double quotes" - fantastic. I don't know *how* you discovered that, but I'm very glad you did, and you decided to write it up. Saved me a lot of time and headache - thanks!

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.