Extension:MobileFrontend/Configuring browser auto-detection

This document explains how a site admin can enable a mobile site for their mediawiki extension such that when a user visits the site from a mobile device they are automatically redirected to the mobile view rather than the desktop view.

Built-in autodetection

edit

As of commit 5a1867e, MobileFrontend supports automatic device detection with minimal configuration. All you need to do is set $wgMFAutodetectMobileView = true; in your LocalSettings.php. While this is the easiest method, it will almost certainly be the least performant. This solution is NOT compatible with front-end caching, as it provides no way for your cache to know the difference between a mobile view and a desktop view.

Apache Mobile Filter (AMF)

edit

As of commit 0fb2c72d, MobileFrontend supports the Apache Mobile Filter (AMF) for device detection. You must be using the Apache webserver. Follow instructions for setup/configuration of AMF, and it should "just work". This functions very similarly to built-in auto detection, and will not be compatible with front-end caching without special configuration.

Webserver/proxy-cache device detection

edit

Some reverse proxies (like Varnish ) can inform MediaWiki whether mobile version is needed or not, thus creating a cache-friendly autodetection.

For Varnish 4.0 or later, replace the keyword remove with unset.

Detection using Varnish: mobile site on another domain

edit

Suppose we use Varnish, and we want wiki.example.com to show desktop version, and m.wiki.example.com to show mobile version. Here is how to do that.

sub vcl_recv {
	remove req.http.x-subdomain; # Requester shouldn't be allowed to supply arbitrary X-Subdomain header
	if (req.http.host == "m.wiki.example.com") { # mobile domain
		set req.http.host = "wiki.example.com"; # desktop domain
		set req.http.x-subdomain = "m";
	}
}
sub vcl_hash {
	# Cache the mobile version of pages separately.
	#
	# NOTE: x-subdomain header should only have one value (if it exists), therefore vcl_recv() should remove user-supplied X-Subdomain header.
	hash_data(req.http.x-subdomain);
}

LocalSettings.php should contain the following:

$wgMFAutodetectMobileView = false;
$wgMobileUrlCallback = fn ( $domain ) => "m.$domain"; // domain for mobile site
$wgCookieDomain = ".wiki.example.com";

Where "%h<#>" maps to a segment of the hostname of $wgServer. So, if $wgServer = 'en.wikipedia.org';, %h0 is "en", %h1 is "wikipedia", %h2 is "org". Given this, the above $wgMobileUrlTemplate will automatically interpolate your mobile URL as "en.m.wikipedia.org". This is particularly useful for the WMF and projects like Wikipedia, which follow a template of <lang code>.wikipedia.org, so the mobile domain will always look like <lang code>.m.wikipedia.org.

In this solution, built-in autodetection should be disabled ($wgMFAutodetectMobileView = false;), so that MediaWiki wouldn't question the decisions of Varnish.

Detection using Varnish: same domain for desktop/mobile site

edit

Suppose we use Varnish, and we want wiki.example.com to show desktop/mobile version on the very same URLs (without creating additional domains like "m.wiki.example.com", etc.). Here is how to do that.

sub vcl_recv {
	remove req.http.x-subdomain; # Requester shouldn't be allowed to supply arbitrary X-Subdomain header
	if(req.http.User-Agent ~ "(?i)^(lg-|sie-|nec-|lge-|sgh-|pg-)|(mobi|240x240|240x320|320x320|alcatel|android|audiovox|bada|benq|blackberry|cdm-|compal-|docomo|ericsson|hiptop|htc[-_]|huawei|ipod|kddi-|kindle|meego|midp|mitsu|mmp\/|mot-|motor|ngm_|nintendo|opera.m|palm|panasonic|philips|phone|playstation|portalmmm|sagem-|samsung|sanyo|sec-|sendo|sharp|softbank|symbian|teleca|up.browser|webos)") {
		set req.http.x-subdomain = "m";
	}

	if(req.http.Cookie ~ "mf_useformat=") {
		# This means user clicked on Toggle link "Mobile view" in the footer.
		# Inform vcl_hash() that this should be cached as mobile page.
		set req.http.x-subdomain = "m";
	}
}
sub vcl_hash {
	# Cache the mobile version of pages separately.
	#
	# NOTE: x-subdomain header should only have one value (if it exists), therefore vcl_recv() should remove user-supplied X-Subdomain header.
	hash_data(req.http.x-subdomain);
}

LocalSettings.php should contain the following:

$wgMFAutodetectMobileView = true;
$wgCookieDomain = ".wiki.example.com";
In this solution, built-in autodetection should be enabled (it won't impact the cache):
$wgMFAutodetectMobileView = true;

Otherwise MobileFrontend misbehaves (if we don't set $wgMobileUrlCallback, it ignores X-Subdomain header, if we do, it doesn't set "useformat" cookies for "toggle Mobile view" links in the footer").

Detection using nginx: mobile site on another domain

edit

Suppose we use nginx, and we want wiki.example.com to show desktop version, and m.wiki.example.com to show mobile version. Here's how to do that. This configuration mimics the behavior of WMF sites.

# [...]

server_tokens off;
set_real_ip_from localhost;
real_ip_header X-Forwarded-For;

map $cookie_stopMobileRedirect $mobile_redirect {
	default ?01;
	"" ${http_sec_ch_ua_mobile}1;
}

ssl_certificate /path/to/ssl-certificate.pem;
ssl_certificate_key /path/to/ssl-certificate-key.pem;
ssl_dhparam /path/to/ssl-dhparams.pem;

server {
	listen 443 ssl;
	listen [::]:443 ssl;

	root /var/www/html/example;

	index index.php;

	server_name localhost;

	location ~ ^/w/(index|load|api|thumb|opensearch_desc|rest|img_auth)\.php$ {
		include fastcgi_params;
		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
		fastcgi_pass unix:/run/php/php8.1-fpm.sock;
	}

	location /w/images {}

	location /w/images/deleted {
		deny all;
	}

	location ~ ^/w/resources/(assets|lib|src) {
		try_files $uri 404;
		add_header Cache-Control "public" always;
		expires 7d;
	}

	location ~ ^/w/(skins|extensions)/.+\.(css|js|gif|jpg|jpeg|png|svg|wasm|ttf|woff|woff2)$ {
		try_files $uri 404;
		add_header Cache-Control "public" always;
		expires 7d;
	}

	location = /w/images/favicon.svg {
		add_header Cache-Control "public" always;
		expires 7d;
	}

	location = /favicon.ico {
		rewrite ^ /w/images/favicon.ico;
	}

	location = /w/images/favicon.ico {
		add_header Cache-Control "public" always;
		expires 7d;
	}

	location /w/sitemap {}

	location = /sitemap.xml {
		rewrite ^ /w/sitemap/sitemap-index-wiki.xml;
	}

	location ~ ^/w/(COPYING|CREDITS)$ {
		default_type text/plain;
	}

	location /w/rest.php/ {
		try_files $uri $uri/ /w/rest.php?$query_string;
	}

	location /wiki/ {
		rewrite ^/wiki/(?<pagename>.*)$ /w/index.php;
	}

	location = / {
		return 301 /wiki/Main_Page;
	}

	location / {
		return 404;
	}

	error_page 404 /w/images/404.html;

	location /w/images/404.html {
		internal;
	}
}

server {
	listen 80 default_server;
	listen [::]:80 default_server;

	server_name wiki.example.com;

	return 301 https://$host$request_uri;

	return 404;
}

server {
	listen 443 default_server ssl;
	listen [::]:443 default_server ssl;

	server_name wiki.example.com;

	location / {
		if ($arg_mobileaction = toggle_view_desktop) {
			add_header Set-Cookie "stopMobileRedirect=true; Domain=.wiki.example.com; Path=/; Max-Age=2592000; HttpOnly; Secure";
			break;
		}

		if ($mobile_redirect != ?01) {
			return 302 https://m.wiki.example.com$request_uri;
		}

		if ($http_host != wiki.example.com) {
			return 301 https://wiki.example.com$request_uri;
		}

		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_pass https://localhost;
	}
}

server {
	listen 443 ssl;
	listen [::]:443 ssl;

	server_name m.wiki.example.com;

	location / {
		if ($arg_mobileaction = toggle_view_mobile) {
			add_header Set-Cookie "stopMobileRedirect=; Domain=.wiki.example.com; Path=/; Max-Age=0; HttpOnly; Secure";
		}

		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Subdomain m;
		proxy_pass https://localhost;
	}
}

LocalSettings.php should contain the following:

$wgMFAutodetectMobileView = false;
$wgMobileUrlCallback = fn ( $domain ) => "m.$domain";
$wgCookieDomain = ".wiki.example.com";