How To Host Multiple WordPress Sites On Ubuntu 16.04, NGINX, PHP7-FPM – Part 2

This tutorial assumes that you already have one website functioning on the server and that you have followed the initial steps detailed in How to Install WordPress on Ubuntu 16.04 using NGINX & PHP7

Login to the site where you registered your domain name and change the nameservers to point to:

  1. ns1.digitalocean.com
  2. ns2.digitalocean.com
  3. ns3.digitalocean.com

Then, go to your DigitalOcean account and go to Networking > Domains. In the form to “Add a Domain” enter your domain name and then select the droplet.

Now lets create a new database for our new site. From the command line:

$ mysql -u root -p

Create a new database, user and assign privileges to the user (change ‘mywpdb’ to whatever database name you want, ‘mywpdbuser’ to whatever username you want, and replace ‘securepassword with a good password):

MariaDB [(none)]> CREATE DATABASE mywpdb DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
MariaDB [(none)]> GRANT ALL ON mywpdb.* TO 'mywpdbuser'@'localhost' IDENTIFIED BY 'securepassword';
MariaDB [(none)]> FLUSH PRIVILEGES;
MariaDB [(none)]> quit

enter ‘exit’ to exit MariaDB

Note that we could also use phpMyAdmin to do this. Login to phpMyAdmin and create a new database noting the new database name. For now, we will also use our current username and password for setting up the connection between our new database and WordPress. You may want to add a new user with Administrator privileges with a new password which you can do inside phpMyAdmin. If you need the information on how to do this, I will post the steps later.

Next, from the command line, add a new directory to ‘/var/www/’ and change ownership permissions (replace ‘site2.com’ with your own domain name):

$ sudo mkdir /var/www/site2.com

$ sudo mkdir /var/www/site2.com/html

$cd /var/www/site2.com

$ sudo chown -R www-data:www-data .

Then we want to note the most recent version of WordPress.

Then, go to web root directory (replace 'site2.com' with your own domain name):

$ cd /var/www/site2.com/html

$ sudo svn co http://core.svn.wordpress.org/tags/4.6.1 .

note the ‘.’ (period) at the end of the last command.

Then, update the ownership of the files:

$ sudo chown -R www-data:www-data .

again, note the ‘.’ (period) at the end of the command.

Before we can visit our domain in a web browser, we have to configure some nginx settings. If you followed the earlier steps for setting up the first site, by going to the new site you will be redirected to the default site in ‘etc/nginx/sites-available’.

We now create a new file in the NGINX configuration replacing ‘site2.com’ with your own domain name (you can aslo just use WinSCP to this):

$ sudo nano /etc/nginx/sites-available/site2.com

Inside this file add the code below:

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

        root /var/www/site2.com/html;
        index index.php index.html;

        server_name site2.com www.site2.com;
		
        location / {
                try_files $uri $uri/ /index.php?$args;
        }

        location ~ \.php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/run/php/php7.0-fpm.sock;
                fastcgi_cache_bypass $skip_cache;
                fastcgi_no_cache $skip_cache;
                fastcgi_cache WORDPRESS;
                fastcgi_cache_valid 60m;
                include fastcgi_params;
        }
 
        location ~* ^/wp-includes/.*(?<!(js/tinymce/wp-tinymce))\.php$ {
                internal;
        }

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

        location = /wp-config.php {
                deny all;
        }

        location ~* /(?:uploads|files)/.*\.php$ {
                deny all;
        }

        location ~* ^/wp-content/.*\.(txt|md|exe|sh|bak|inc|php|pot|po|mo|log|sql)$ {
                deny all;
        }

		location ~ /.well-known {
                allow all;
        }

                location ~ /\.(ht|svn)? {
                deny all;
		}
}

Ctrl-X, Y then ‘Enter’ to save and exit.

Note that we have not added an SSL certificate (for ‘https’) yet. We will get to that soon.

check to make sure the NGINX syntax is correct:

$ sudo nginx -t

restart NGINX:

$ sudo service nginx restart

Then create symbolic links from this file to the sites-enabled directory, which Nginx reads during startup (replace ‘site2.com’ with your own domain name):

sudo ln -s /etc/nginx/sites-available/site2.com /etc/nginx/sites-enabled/site2.com

When i first did all of the steps above, everytime I tried to load the new site (site2.com) into a browser, I was redirected to the default site1.com. When searching for solutions, I found that this is a very common issue with nginx when trying to add multiple sites to the same server. The solution wasn’t really a solution at all – I just came back and tried to load the site again (about 12-24 hours later) and it worked! My guess is that the DNS wasn’t completely resolving from the nameserver changes that we did above in DigitalOcean and our domain name provider. It just needed to take more time to resolve (your domain name provider will generally state it may take 24-48 hours but in my experience it only takes a few minutes or less) – or it was just the browser cache or the server cache setup in our previous nginx setup :).

Now you can visit your domain in a web browser and complete the installation by following the WordPress prompts. Note the database name, user and password that you created earlier.

Since we are still using ‘http’ and we want our site to default to ‘https’ we will add an SSL certificate for this new site with LetsEncrypt (replace ‘site2.com’ with your own domain name) :

$ sudo letsencrypt certonly -a webroot --webroot-path=/var/www/site2.com/html -d site2.com -d www.site2.com

Now we create a new file for our new SSL certifcate replacing ‘site2.com’ with your own domain name (you can also do this using WinSCP):

$ sudo nano /etc/nginx/snippets/ssl-site2.com.conf

paste this code into the file replacing ‘site2.com’ with your sites domain name:

ssl_certificate /etc/letsencrypt/live/site2.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/site2.com/privkey.pem;

Ctrl-X, ‘Y’ and ‘Enter’ to save and exit.

Then we add some extra code to the site2.com file in ‘sites-availabe’. Add the code below right after ‘server_name site2.com www.site2.com;’ replacing ‘ssl-site2.com.conf’ with your own domain name:

if ($scheme != "https") {
			rewrite ^ https://$host$uri permanent;
		}
		
		listen 443 ssl http2;
        listen [::]:443 ssl http2;
		include snippets/ssl-site2.com.conf;
		include snippets/ssl-params.conf;

		set $skip_cache 0;
        if ($request_method = POST) {
                set $skip_cache 1;
        }
        if ($query_string != "") {
                set $skip_cache 1;
        }
        if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") {
                set $skip_cache 1;
        }
        if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
                set $skip_cache 1;
        }
		
		autoindex off;
        location ~ /purge(/.*) {
                fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";
        }
        location ~* ^.+\.(flv|pdf|avi|mov|mp3|wmv|m4v|webm|aac|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
                expires max;
                log_not_found off;
                access_log off;
        }

The entire file should look like this (replace ‘site2.com’ with your own domain name):

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

        root /var/www/site2.com/html;
        index index.php index.html;

        server_name site2.com www.site2.com;
		
		if ($scheme != "https") {
			rewrite ^ https://$host$uri permanent;
		}
		
		listen 443 ssl http2;
        listen [::]:443 ssl http2;
		include snippets/ssl-site2.com.conf;
		include snippets/ssl-params.conf;

		set $skip_cache 0;
        if ($request_method = POST) {
                set $skip_cache 1;
        }
        if ($query_string != "") {
                set $skip_cache 1;
        }
        if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") {
                set $skip_cache 1;
        }
        if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
                set $skip_cache 1;
        }
		
		autoindex off;
        location ~ /purge(/.*) {
                fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";
        }
        location ~* ^.+\.(flv|pdf|avi|mov|mp3|wmv|m4v|webm|aac|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
                expires max;
                log_not_found off;
                access_log off;
        }
		
        location / {
                try_files $uri $uri/ /index.php?$args;
        }

        location ~ \.php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/run/php/php7.0-fpm.sock;
                fastcgi_cache_bypass $skip_cache;
                fastcgi_no_cache $skip_cache;
                fastcgi_cache WORDPRESS;
                fastcgi_cache_valid 60m;
                include fastcgi_params;
        }
 
        location ~* ^/wp-includes/.*(?<!(js/tinymce/wp-tinymce))\.php$ {
                internal;
        }

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

        location = /wp-config.php {
                deny all;
        }

        location ~* /(?:uploads|files)/.*\.php$ {
                deny all;
        }

        location ~* ^/wp-content/.*\.(txt|md|exe|sh|bak|inc|php|pot|po|mo|log|sql)$ {
                deny all;
        }
		
        

		location ~ /.well-known {
                allow all;
        }

                location ~ /\.(ht|svn)? {
                deny all;
		}
}

We also need to adjust one setting really quickly in the default Nginx configuration file. Open it up by typing:

$ sudo nano /etc/nginx/nginx.conf

We just need to uncomment one line. Find and remove the comment from this:

server_names_hash_bucket_size 64;

Now, we are ready to restart Nginx to enable your changes. You can do that by typing:

sudo service nginx restart

Now when you go to site2.com in your browser, it should automatically redirect you to ‘https://site2.com

References

This tutorial references code from various websites.

https://www.digitalocean.com/community/tutorials/how-to-install-wordpress-with-lemp-on-ubuntu-16-04
and
https://www.digitalocean.com/community/tutorials/how-to-set-up-nginx-server-blocks-virtual-hosts-on-ubuntu-14-04-lts/
and
https://www.digitalocean.com/community/tutorials/understanding-the-nginx-configuration-file-structure-and-configuration-contexts
and
http://www.morphatic.com/2016/05/21/super-fast-secure-wordpress-install-on-digitalocean-with-nginx-php7-and-ubuntu-16-04-lts

Errors & Solutions

1. problems with LetsEncrypt accessing ‘.well-known’ directory.
https://github.com/letsencrypt/acme-spec/issues/221
I just found the code that specifies nginx access to the ‘.wellknown’ directory shown here (in ‘/etc/nginx/sites-available/site2.com):

location ~ /.well-known {
                allow all;
        }

and moved it above the code which denied access to our ‘.svn’ directory shown here which does not allow dot prefix file requests:

location ~ /\.(ht|svn)? {
                deny all;
		}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.