Separate nginx Mobile and Desktop User-Agent Cache

You may want to separate your server-side nginx cache between mobile and desktop users determined by their browser. This guide will show you how to configure your nginx fastcgi or reverse proxy cache to have separate caches for mobile and desktop user agents. You can make this work for Redis cache integrated with nginx too.

The inspiration for this tutorial came from one of my favorite hosts (read why here) which has beautiful analytics data for your WordPress sites as shown in the screenshot below (check out their demo of their dashboard here).

Separate nginx Mobile and Desktop User-Agent Cache

Google helped me find a list of User agent strings which seemed pretty complete so I used this as the basis for separating the mobile, tablet, handheld and desktop.

This is by no means exhaustive so please consider this a proof of concept.

We are going to use the nginx map directive to detect user agents and place them in mobile and desktop categories.

Then we will use this variable and adjust the cache key for either a proxy_cache (see tutorial) or fastcgi_cache using nginx.

Here is the map command that must go in the http { block of your nginx.conf

# map the list of user agents must escape ( with \(
map $http_user_agent $mobile_user {
   default "WPBULLET_DESKTOP";
   # Android
   "~Mozilla/5.0 \(Linux; Android" WPBULLET_MOBILE;
   # Opera
   "~Opera Mini" WPBULLET_MOBILE;
   # iOS
   "~Mozilla/5.0 \(iPhone" WPBULLET_MOBILE;
   # Windows Phone
   "~Mozilla/5.0 \(Windows Phone" WPBULLET_MOBILE;
   }

Now we need to modify the cache key so we can logically separate desktop and mobile users by altering the fastcgi_cache_key directive to include the $mobile_user key.

You can make similar adjustments for a proxy_cache by adjusting the proxy_cache_key directive.

location ~ \.php$ {
	try_files $uri =404;
	# add cache status
	add_header WP-Bullet-Fastcgi-Cache $upstream_cache_status;
	# add the cache skip reason if relevant
	add_header WP-Bullet-Skip $skip_reason;
	add_header X-Exception $exception;
	include fastcgi_params;
	fastcgi_pass unix:/run/php/php7.3-fpm.sock;
	fastcgi_split_path_info ^(.+\.php)(.*)$;
	fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
	fastcgi_cache_bypass $http_secret_header $skip_cache;
	fastcgi_no_cache $skip_cache;
	fastcgi_cache WORDPRESS;
        fastcgi_cache_key "$scheme$request_method$host$mobile_user$request_uri";
	fastcgi_cache_valid 404 1m;
	fastcgi_cache_valid 60m;
}

You can also use this $mobile_user variable in a custom access_log directive with nginx to track the mobile visitors using something like this in your http { block

log_format customuseragent '[$time_local] $remote_addr $upstream_cache_status '
                           '$mobile_user $request_method '
                           '"$request_uri" $upstream_response_time';

Then in your nginx virtual host you can define the custom access log

access_log /var/log/nginx/customuseragent.log customuseragent;

Make sure to reload nginx and then you can check the custom access log file

tail -f /var/log/nginx/customuseragent.log

You should see output similar to this

[04/Apr/2020:13:41:10 +0000] 192.226.152.59 MISS WPBULLET_MOBILE GET "/" 0.272
[04/Apr/2020:13:41:11 +0000] 192.226.152.59 MISS WPBULLET_MOBILE GET "/wp-json/recently/v1/widget/5?lang=" 0.076
[04/Apr/2020:13:41:18 +0000] 192.226.152.59 - WPBULLET_DESKTOP POST "/wp-admin/admin-ajax.php" 0.072
[04/Apr/2020:13:41:19 +0000] 86.101.36.78 HIT WPBULLET_MOBILE GET "/" -
[04/Apr/2020:13:41:20 +0000] 86.101.36.78 HIT WPBULLET_MOBILE GET "/wp-json/recently/v1/widget/5?lang=" -
[04/Apr/2020:13:41:20 +0000] 86.101.36.78 MISS WPBULLET_MOBILE GET "/wp-json/wordpress-popular-posts/v1/popular-posts/widget/3?is_single=" 0.112
[04/Apr/2020:13:41:23 +0000] 2804:1b2:81:b271:85d2:778c:a336:fd28 MISS WPBULLET_DESKTOP GET "/install-apcu-object-cache-for-php7-for-wordpress-ubuntu-16-04/" 0.140
[04/Apr/2020:13:41:27 +0000] 2804:1b2:81:b271:85d2:778c:a336:fd28 HIT WPBULLET_DESKTOP GET "/wp-json/recently/v1/widget/5?lang=" -
[04/Apr/2020:13:41:27 +0000] 2804:1b2:81:b271:85d2:778c:a336:fd28 MISS WPBULLET_DESKTOP GET "/wp-json/wordpress-popular-posts/v1/popular-posts/widget/3?is_single=808" 0.060
[04/Apr/2020:13:41:28 +0000] 1.164.141.46 MISS WPBULLET_DESKTOP GET "/lets-encrypt-wildcard-ssl-nginx-for-wordpress-ubuntu-18-04/" 0.144
[04/Apr/2020:13:41:30 +0000] 1.164.141.46 HIT WPBULLET_DESKTOP GET "/wp-json/recently/v1/widget/5?lang=" -
[04/Apr/2020:13:41:30 +0000] 1.164.141.46 MISS WPBULLET_DESKTOP GET "/wp-json/wordpress-popular-posts/v1/popular-posts/widget/3?is_single=6539" 0.076

Sources

nginx map module
nginx variable index
nginx Caching per User Agent