Nginx configuration redirect for mobile site with subdomain: mobile.example.com

Hello,
First of all, my Operating system = CentOS Linux 7.7.1908 & Virtualmin version = 6.08.

Please, what I want to achieve is, have all mobile users redirected from www.example.com to subdomain: mobile.example.com.

I have setup the main domain which is www.example.com with the root as /home/user/public_html and also installed letsencrypt ssl and everything is working ok.

I created a subdomain: mobile.example.com in virtualmin, created an A record for it with Cloudflare’s proxy on and also changed its home directory from /home/user/domains/mobile.example.com to the main domain document root: /home/user/public_html.

And here is the nginx configuration for mobile.example.com:
server {
listen 80;
server_name mobile.example.com;

return 301 http://mobile.example.com$request_uri;

}

server {
listen 443 ssl http2;
server_name mobile.example.com;

root /home/user/public_html;
index index.html index.htm index.php

ssl_certificate /etc/letsencrypt/live/example.com/cert.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

ssl_stapling on; # managed by Certbot
ssl_stapling_verify on; # managed by Certbot
	
# log files
access_log /var/log/virtualmin/mobile.example.com_access_log;
error_log /var/log/virtualmin/mobile.example.com_error_log;

location / {
    try_files $uri $uri/ /index.php?$args;
}
	
# REQUIREMENTS : Enable PHP Support
location ~ \.php$ {
	fastcgi_param GATEWAY_INTERFACE CGI/1.1;
	fastcgi_param SERVER_SOFTWARE nginx;
	fastcgi_param QUERY_STRING $query_string;
        fastcgi_param REQUEST_METHOD $request_method;
	fastcgi_param CONTENT_TYPE $content_type;
	fastcgi_param CONTENT_LENGTH $content_length;
	fastcgi_param SCRIPT_FILENAME 
            /home/user/public_html$fastcgi_script_name;
	fastcgi_param SCRIPT_NAME $fastcgi_script_name;
	fastcgi_param REQUEST_URI $request_uri;
	fastcgi_param DOCUMENT_URI $document_uri;
	fastcgi_param DOCUMENT_ROOT /home/user/public_html;
	fastcgi_param SERVER_PROTOCOL $server_protocol;
	fastcgi_param REMOTE_ADDR $remote_addr;
	fastcgi_param REMOTE_PORT $remote_port;
	fastcgi_param SERVER_ADDR $server_addr;
	fastcgi_param SERVER_PORT $server_port;
	fastcgi_param SERVER_NAME $server_name;
	fastcgi_param HTTPS $https;
	location ~ \.php$ {
		try_files $uri =404;
		fastcgi_pass unix:/run/php-fpm/www.sock;
    }
}	

}

And then the nginx configuration for the main domain www.example.com is:
server {
listen 80;
server_name example.com www.example.com;

include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
return 301 https://www.example.com$request_uri;

}

server {
listen 443 ssl http2;
server_name example.com;

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
return 301 https://www.example.com$request_uri;

}

server {
listen 443 ssl http2;
server_name www.example.com;

root /home/user/public_html;
index index.php;

# SSL parameters
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

ssl_client_certificate /etc/ssl/certs/origin-pull-ca.pem; # Cloudflare origin pull cert
ssl_verify_client on; # Cloudflare origin pull cert

ssl_stapling on; # managed by Certbot
ssl_stapling_verify on; # managed by Certbot	

add_header Content-Security-Policy upgrade-insecure-requests;
add_header Referrer-Policy 'strict-origin';
add_header Feature-Policy "vibrate none; geolocation none; unsized-media 
    https://www.example.com;";
add_header "Access-Control-Allow-Origin" $http_origin;

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; 
    preload" always;

# log files
access_log /var/log/virtualmin/example.com_access_log;
error_log /var/log/virtualmin/example.com_error_log;

include /home/user/public_html/nginx.conf;

location = ^/favicon.ico {
    log_not_found off;
    access_log off;
}

location = ^/robots.txt {
    allow all;
    log_not_found off;
    access_log off;
}

# Deny access to uploads that aren’t images, videos, music, etc.
location ~* ^/wp-content/uploads/.*.(html|htm|shtml|php|js|swf)$ {
    access_log off;
	log_not_found off;
	deny all;
}

# Deny public access to wp-config.php
location ~* wp-config.php {
    deny all;
}

location / {
    try_files $uri $uri/ /index.php?$args;
}

# REQUIREMENTS : Enable PHP Support
location ~ \.php$ {
    fastcgi_param GATEWAY_INTERFACE CGI/1.1;
	fastcgi_param SERVER_SOFTWARE nginx;
	fastcgi_param QUERY_STRING $query_string;
	fastcgi_param REQUEST_METHOD $request_method;
	fastcgi_param CONTENT_TYPE $content_type;
	fastcgi_param CONTENT_LENGTH $content_length;
	fastcgi_param SCRIPT_FILENAME 
            /home/user/public_html$fastcgi_script_name;
	fastcgi_param SCRIPT_NAME $fastcgi_script_name;
	fastcgi_param REQUEST_URI $request_uri;
	fastcgi_param DOCUMENT_URI $document_uri;
	fastcgi_param DOCUMENT_ROOT /home/user/public_html;
	fastcgi_param SERVER_PROTOCOL $server_protocol;
	fastcgi_param REMOTE_ADDR $remote_addr;
	fastcgi_param REMOTE_PORT $remote_port;
	fastcgi_param SERVER_ADDR $server_addr;
	fastcgi_param SERVER_PORT $server_port;
	fastcgi_param SERVER_NAME $server_name;
	fastcgi_param HTTPS $https;
	location ~ \.php$ {
		try_files $uri =404;
		fastcgi_pass unix:/run/php-fpm/www.sock;
    }
**#----- redirect to mobile check (starts) -----#**

** set $mobile_rewrite do_not_perform;**


** if ($http_user_agent ~* “(android|bb\d+|meego).+mobile|avantgo|bada/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)/|plucker|pocket|psp|series(4|6)0|symbian|treo|up.(browser|link)|vodafone|wap|windows ce|xda|xiino”) {**
** set $mobile_rewrite perform;**
** }**


** if ($http_user_agent ~* “^(1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|)|g1 u|g560|gene|gf-5|g-mo|go(.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| ||a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-)”) {**
** set $mobile_rewrite perform;**
** }**


** if ($mobile_rewrite = perform) {**
** rewrite ^ https://mobile.example.com$request_uri? redirect;**
** break;**
** }**
** #----- redirect to mobile check (ends) -----#**
}

Please, after all these setup, when I visit mobile.example.com on mobile phone, it shows nothing on the page and only says mobile.example.com has too many redirects.

Please, what am I doing wrong? I will forever be grateful and indebted to any person who will help me out with a solution. Many, many thanks in advance.

I’m fairly certain your ‘return’ line in your mobile.example.com nginx config is causing the issue. You need to to tell it what requests should be dealt with that way. Something like the below works for me.

server{
    listen 80;
    server_name example.com www.example.com;
    root /home/example/public_html;

    location  /.well-known/acme-challenge/ {
            allow all;
    }
    location / {
            return 301 https://$server_name$request_uri;
   }
}

Hello,
I’m very glad for your quick response. I appreciate that a lot!

I deleted my mobile.example.com nginx config and replaced it with yours like this:

server{
listen 80;
server_name mobile.example.com www.mobile.example.com;
root /home/example/public_html;

access_log /var/log/virtualmin/example.com_access_log;
error_log /var/log/virtualmin/example.com_error_log;

location /.well-known/acme-challenge/ {
allow all;
}
location / {
return 301 https://$server_name$request_uri;
}
}

And the result I got is that, It directs only to the www.example.com main domain when on mobile phone and not the mobile.example.com subdomain.
Please, what am I missing?

And please, is the entire nginx config for the main domain (www.example.com) including the code (that begins with set $mobile_rewrite do_not_perform;) in my first post ok?

And if you don’t mind, how do I contact you privately and replicate same here when done?
Many thanks once more @noisemarine

You still need to use the ssl part of the config. What the ‘return’ does is take any requests to the ‘http’ site and redirects them to the ‘https’ site. So each of your website configs has two server{} parts - the first for http (port 80) and the second for https (port 443).

I also just realised you have two port 443 sections in your example.com config. Combine them into one by putting both names on the server_name line. You also need to make the http section look more like the one I posted due to the ‘return’ statement (it should be enclosed in a location{} section).

Hello @noisemarine,
Many thanks for your response.

I also just realised you have two port 443 sections in your example.com config.

Yes. One of the port 443 is to redirect all NON WWW to → WWW and the other (www.example.com) is where the main content is delivered to the visitor.

By the way, and as you suggested, I tried combing the two port 443 sections into one by putting both names (example.com www.example.com) on the server_name line but it wasn’t giving me the desired output. Maybe, the fault is coming from me.

You still need to use the ssl part of the config…

I get test failed when I try using this:
location / {
return 301 https://$server_name$request_uri;
}

Then I tried this config for mobile.example.com but it didn’t work as expected:

server {
listen 80;
server_name mobile.example.com www.mobile.example.com;

return 301 https://mobile.example.com$request_uri;
}

server {
listen 443 ssl http2;
server_name mobile.example.com;

root /home/user/public_html;
index index.html index.htm index.php

ssl_certificate /etc/letsencrypt/live/example.com/cert.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

ssl_stapling on; # managed by Certbot
ssl_stapling_verify on; # managed by Certbot
	
# log files
access_log /var/log/virtualmin/mobile.example.com_access_log;
error_log /var/log/virtualmin/mobile.example.com_error_log;

location / {
    try_files $uri $uri/ /index.php?$args;
}
	
# REQUIREMENTS : Enable PHP Support
location ~ \.php$ {
	fastcgi_param GATEWAY_INTERFACE CGI/1.1;
	fastcgi_param SERVER_SOFTWARE nginx;
	fastcgi_param QUERY_STRING $query_string;
        fastcgi_param REQUEST_METHOD $request_method;
	fastcgi_param CONTENT_TYPE $content_type;
	fastcgi_param CONTENT_LENGTH $content_length;
	fastcgi_param SCRIPT_FILENAME 
            /home/user/public_html$fastcgi_script_name;
	fastcgi_param SCRIPT_NAME $fastcgi_script_name;
	fastcgi_param REQUEST_URI $request_uri;
	fastcgi_param DOCUMENT_URI $document_uri;
	fastcgi_param DOCUMENT_ROOT /home/user/public_html;
	fastcgi_param SERVER_PROTOCOL $server_protocol;
	fastcgi_param REMOTE_ADDR $remote_addr;
	fastcgi_param REMOTE_PORT $remote_port;
	fastcgi_param SERVER_ADDR $server_addr;
	fastcgi_param SERVER_PORT $server_port;
	fastcgi_param SERVER_NAME $server_name;
	fastcgi_param HTTPS $https;
	location ~ \.php$ {
		try_files $uri =404;
		fastcgi_pass unix:/run/php-fpm/www.sock;
    }
}
location  /.well-known/acme-challenge/ {
        allow all;
}

}

I’m not sure what to do next as I have tried several configs all to no avail.

Maybe, if only you could help me with the complete configuration for the mobile.example.com, I will be more than glad @noisemarine. Many thanks!