Setup WordPress (LEMP Stack)


How To Set Up Multiple WordPress Installations on a Single Ubuntu Server

Reference :

  • https://www.digitalocean.com/community/tutorials/how-to-set-up-multiple-wordpress-sites-on-a-single-ubuntu-vps

Install LEMP

Install Nginx

sudo apt-get install nginx
sudo apt-get install nginx-extras
curl http://localhost

Install MySQL

sudo apt-get update
sudo apt-get install mysql-server

Install PHP

sudo apt-get update
sudo apt-get install php-fpm php-mysql php-mbstring php-mcrypt php-xml php-gd php-ssh2 php-curl
sudo apt-get install php-cli php-cgi
sudo service php7.0-fpm restart

Setup MySql

Setup MySQL for the first time

sudo /usr/bin/mysql_secure_installation

The prompt will ask you for your current root password.

Enter password for user root:

VALIDATE PASSWORD PLUGIN can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD plugin?

Press y|Y for Yes, any other key for No: y

There are three levels of password validation policy:

LOW Length >= 8
MEDIUM Length >= 8, numeric, mixed case, and special characters
STRONG Length >= 8, numeric, mixed case, special characters and dictionary file

Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 2

Estimated strength of the password: 50
Change the password for root ? ((Press y|Y for Yes, any other key for No) : y

New password:
Re-enter new password:

Estimated strength of the password: 100
Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) : y

By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.

Remove anonymous users? (Press y|Y for Yes, any other key for No) : y
Success.

Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y
Success.

By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.

Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y
- Dropping test database...
Success.

- Removing privileges on test database...
Success.

Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y
Success.

All done!

Setup WordPress

1. Create Site Databases and Users

For the purposes of this guide, we will be using the following information:

domain1.com
site name : Domain 1
db name : domain1_wp
db user : domain1_user
db pass : password

domain2.com
site name : Domain 2
db name : domain2_wp
db user : domain2_user
db pass : password

a. Log into MySQL using the administrator account you configured during the MySQL installation:

mysql -u root -p

b. Execute the mysql commands :

CREATE DATABASE domain1_wp;
CREATE USER [email protected] IDENTIFIED BY 'password';
// SET PASSWORD FOR [email protected]= PASSWORD("password");
GRANT ALL PRIVILEGES ON domain1_wp.* TO [email protected] IDENTIFIED BY 'password';

CREATE DATABASE domain2_wp;
CREATE USER [email protected] IDENTIFIED BY 'password';
// SET PASSWORD FOR [email protected]= PASSWORD("password");
GRANT ALL PRIVILEGES ON domain2_wp.* TO [email protected] IDENTIFIED BY 'password';

FLUSH PRIVILEGES;
exit

# Drop Database
DROP DATABASE domain1_wp;

# Drop user
DROP USER [email protected]'%';
DROP USER [email protected]'localhost';

# Show users
SELECT user from mysql.user;

# Show grants
SHOW GRANTS FOR 'domain1_user'@'localhost';

2. Download WordPress and decompress the archive

sudo wget http://wordpress.org/latest.tar.gz

or

curl -O https://wordpress.org/latest.tar.gz
tar -xzvf latest.tar.gz

3. Go to extracted wordpress archive and create the configuration from the sample configuration file.

sudo cp wordpress/wp-config-sample.php wordpress/wp-config.php

4. Configuring Site Root Directories

a. Create a new root document

sudo mkdir -p /var/www/html

b. Create the directories for your wordpress site

sudo mkdir /var/www/html/domain1.com
sudo mkdir /var/www/html/domain1.com

c. Copy the extracted wordpress files to your site

sudo rsync -avP wordpress/ /var/www/html/domain1.com/
sudo rsync -avP wordpress/ /var/www/html/domain1.com/

d. Give ownership of the directories to the Apache web user and then add your linux username to the web group:

sudo chown www-data:www-data /var/www/html/domain1.com -R
sudo chown www-data:www-data /var/www/html/domain2.com -R
sudo usermod -a -G www-data

5. WordPress Configuration
a. Update the wordpress config file

sudo nano /var/www/html/domain1.com/wp-config.php

Find the section that contains the fields below and substitute the database, username, and password for your site:

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', 'domain1_wp');

/** MySQL database username */
define('DB_USER', 'domain1_user');

/** MySQL database password */
define('DB_PASSWORD', 'password');

// same goes for the 2nd site

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', 'domain2_wp');

/** MySQL database username */
define('DB_USER', 'domain2_user');

/** MySQL database password */
define('DB_PASSWORD', 'password');

/** Updates from WordPress Admin Panel */
define('FS_METHOD','direct');

define('AUTH_KEY', 'YOUR_KEY_HERE');
define('SECURE_AUTH_KEY', 'YOUR_KEY_HERE');
define('LOGGED_IN_KEY', 'YOUR_KEY_HERE');
define('NONCE_KEY', 'YOUR_KEY_HERE');
define('AUTH_SALT', 'YOUR_KEY_HERE');
define('SECURE_AUTH_SALT', 'YOUR_KEY_HERE');
define('LOGGED_IN_SALT', 'YOUR_KEY_HERE');
define('NONCE_SALT', 'YOUR_KEY_HERE');

6. WordPress needs an extra PHP module installed in order to function correctly.

sudo apt-get update
sudo apt-get install php5-gd libssh2-php

or

# php-7
$ sudo apt-get install php-gd php-ssh2

Setup Nginx (Modify Nginx Server Blocks)

References :
* https://www.digitalocean.com/community/tutorials/how-to-install-wordpress-with-nginx-on-ubuntu-14-04
* https://www.digitalocean.com/community/tutorials/how-to-set-up-nginx-server-blocks-virtual-hosts-on-ubuntu-14-04-lts
* https://www.digitalocean.com/community/tutorials/how-to-configure-single-and-multiple-wordpress-site-settings-with-nginx

Create the NGINX .conf files

1. Create a directory for global configurations under /etc/nginx/global.

* common.conf: Configurations applicable to all sites.
* wordpress.conf: Configurations applicable to all WordPress sites.
* multisite.conf: Special configurations for WordPress multisite with sub-directories.

sudo mkdir /etc/nginx/global

2. Create the common.conf file

sudo nano /etc/nginx/global/common.conf

## Copy the following configurations

# Global configuration file.
# ESSENTIAL : Configure Nginx Listening Port
listen 80;

# ESSENTIAL : Default file to serve. If the first file isn't found,
index index.php index.html index.htm;

# ESSENTIAL : no favicon logs
location = /favicon.ico {
log_not_found off;
access_log off;
}

# ESSENTIAL : robots.txt
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}

# ESSENTIAL : Configure 404 Pages
error_page 401 403 404 /404.html;

# ESSENTIAL : Configure 50x Pages
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}

# SECURITY : Deny all attempts to access hidden files .abcde
location ~ /\. {
deny all;
}

# PERFORMANCE : Set expires headers for static files and turn off logging.
location ~* ^.+\.(js|css|swf|xml|txt|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
access_log off; log_not_found off; expires 30d;
}

## Save and exit - Ctrl-O, Ctrl-X

3. Create the wordpress.conf file

sudo nano /etc/nginx/global/wordpress.conf

## Copy the following configurations

# WORDPRESS : Rewrite rules, sends everything through index.php and keeps the appended query string intact
location / {
try_files $uri $uri/ /index.php?q=$uri&$args;
}

# SECURITY : Deny all attempts to access PHP Files in the uploads directory
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}

# Prevent XML-RPC Attacks : Deny all attempts to access the xmlrpc.php
location = /xmlrpc.php {
deny all;
}

# REQUIREMENTS : Enable PHP Support
location ~ \.php$ {
# SECURITY : Zero day Exploit Protection

# for php5
try_files $uri =404;
# ENABLE : Enable PHP, listen fpm sock
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;

# for php-7
include snippets/fastcgi-php.conf;

# With php7.0-cgi alone:
# fastcgi_pass 127.0.0.1:9000;
# With php7.0-fpm:
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}

# PLUGINS : Enable Rewrite Rules for Yoast SEO SiteMap
rewrite ^/sitemap_index\.xml$ /index.php?sitemap=1 last;
rewrite ^/([^/]+?)-sitemap([0-9]+)?\.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;

## Save and exit - Ctrl-O, Ctrl-X

4. Create the server block files for your wordpress site

sudo nano /etc/nginx/sites-available/domain1.conf

## Copy the following
## Same goes for your other wordpress site
## root = this is the root folder of your wordpress installation
## server_name = this is the domain name of your site
## Depending on your DNS provider route your wordpress sites accordingly

server {
#listen 80;

root /var/www/html/domain1.com;
index index.php index.html index.htm;

server_name domain1.com;

include global/common.conf;
include global/wordpress.conf;
}

## Save and exit - Ctrl-O, Ctrl-X

5. Update the /etc/nginx/nginx.conf

# Add the following to http (add at the end)
http : {
client_max_body_size 32M;
}

6. Enable the wordpress site

sudo ln -s /etc/nginx/sites-available/domain1.conf /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/domain2.conf /etc/nginx/sites-enabled/

# (Optional) Disable the default nginx server block
sudo rm /etc/nginx/sites-enabled/default

7. To check your Nginx configuration

# You can use this command to troubleshoot your configuration if there is something wrong.

$ sudo nginx -t

8. Restart the web server and PHP processor.

sudo service nginx restart
sudo service php5-fpm restart

# php-7
sudo service nginx php7.0-fpm restart

Secure NGINX

1. Update /etc/nginx/nginx.conf and apply the SSL Ciphers

http {
...

# -----------------------
# Max Body Size
# -----------------------
client_max_body_size 20M;

# -----------------------
# SSL Protocol | Ciphers
# -----------------------
ssl_protocols TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!3DES';

...
}

2. Update your wordpress server block

sudo nano /etc/nginx/sites-available/domain2.conf

server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;

server_name _;

root /var/www/domain2.com;
index index.php index.html index.htm;

# ----------------
# SSL
# ----------------
listen 443 ssl;
ssl_certificate /etc/ssl/ssl_certificate;
ssl_certificate_key /etc/ssl/private_key;

# ----------------
# Redirect to HTTPS
# ----------------
if ($scheme = 'http') {
return 301 https://$host$request_uri;
}

# ----------------
# HSTS
# ----------------
add_header Strict-Transport-Security 'max-age=31536000';

# ----------------
# Enable SiteMap
# ----------------
#Yoast SEO Sitemaps
location ~ ([^/]*)sitemap(.*).x(m|s)l$ {
rewrite ^/sitemap.xml$ /sitemap_index.xml permanent;
rewrite ^/([a-z]+)?-?sitemap.xsl$ /index.php?xsl=$1 last;
rewrite ^/sitemap_index.xml$ /index.php?sitemap=1 last;
rewrite ^/([^/]+?)-sitemap([0-9]+)?.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;
rewrite ^/news-sitemap.xml$ /index.php?sitemap=wpseo_news last;
rewrite ^/locations.kml$ /index.php?sitemap=wpseo_local_kml last;
rewrite ^/geo-sitemap.xml$ /index.php?sitemap=wpseo_local last;
rewrite ^/video-sitemap.xsl$ /index.php?xsl=video last;
}

include conf.prod/common.conf;
include conf.prod/wordpress.conf;
}

Update WordPress – PHP Config

To increase the file upload limit for WordPress.

* Update the php.ini file
sudo nano /etc/php5/fpm/php.ini

# Php 7
sudo nano /etc/php/7.0/fpm/php.ini

Set the following configurations :

upload_max_filesize = 32M
post_max_size = 16M
memory_limit = 128M
file_uploads = On
max_execution_time = 60

Backup WordPress

references : https://codex.wordpress.org/Backing_Up_Your_WordPress_Files

Backup MySql Databases
1. Execute the command

sudo mysqldump -u root -p --all-databases > all_databases.sql

# Enter your mysql root password

Backup WordPress files
* By default, you can backup the whole wordpress installation.
If you want to backup only your own user-generated content, such as edited themes, new plugins, and uploaded files
Backup the /wp-content and the wp-config.php

$ sudo cp -R /var/www/html/domain1.com/wp-content /home/ubuntu/wordpress/backup/backup_20160630
$ sudo cp /var/www/html/domain1.com/wp-config.php /home/ubuntu/wordpress/backup/backup_20160630

Restoring WordPress

Restore the MySql Databases
1. Execute the command

sudo mysql -u root -p < all_databases.sql

# Single Database
sudo mysql -u root -p wp-fourd < db_backup.dump

2. Include again the privileges for the Users of every database

a. Log into MySQL using the administrator account you configured during the MySQL installation:

$ mysql -u root -p

b. Execute the mysql commands :

FLUSH PRIVILEGES;
exit

3. Copy the wordpress backup files.
* If you backup the whole wordpress site. Just setup your nginx server blocks.
* If you backup just the /wp-content and the wp-config.php, Download again the latest WordPress (see Setup WordPress at the top) and copy the wp-content to the wordpress

WP Content
domain1.com/wp-content
domain2.com/wp-content

sudo rsync -avP domain1.com/ /var/www/domain1.com/
sudo rsync -avP domain2.com/ /var/www/domain2.com/

Other Needed Libraries

sudo apt-get install php5-curl

# See installed packages
dpkg --list

# See running services
service --status-all

------------
PHP-5.6
------------
sudo add-apt-repository ppa:ondrej/php
sudo apt-get update
sudo apt-get install php5.6-fpm php5.6-mbstring php5.6-mcrypt php5.6-mysql php5.6-xml php5.6-gd php5.6-ssh2 php5.6-curl
sudo service nginx php5.6-fpm restart

sudo nano /etc/nginx/conf.prod/wordpress.conf
# REQUIREMENTS : Enable PHP Support
location ~ \.php$ {
# SECURITY : Zero day Exploit Protection

# for php5
try_files $uri =404;
# ENABLE : Enable PHP, listen fpm sock
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php5.6-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}

------------
PHP-7.1
------------
sudo add-apt-repository ppa:nginx/stable

sudo add-apt-repository ppa:ondrej/php
sudo apt-get update
sudo apt-get install php-fpm php-mbstring php-mcrypt php-mysql php-xml php-gd php-ssh2 php-curl
sudo apt-get install php-cli php-cgi
sudo service nginx php7.1-fpm restart

sudo nano /etc/nginx/conf.prod/wordpress.conf
# REQUIREMENTS : Enable PHP Support
location ~ \.php$ {
# SECURITY : Zero day Exploit Protection

try_files $uri =404;
# ENABLE : Enable PHP, listen fpm sock
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php7.1-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
Previous Configure SSL in Nginx
This is the most recent story.