WordPress is one of the most popular open source content management systems today, used by more than 30% of all websites to host online web applications. WordPress is written in PHP, and both PHP and WordPress can run on NGINX Unit, the new dynamic application server from NGINX, Inc.
Formerly, deploying WordPress with NGINX required the use of a separate application server, such as Apache or PHP‑FPM. NGINX Unit is more flexible, supporting Go, Perl, Python, and Ruby along with PHP.
With NGINX Unit, you set up and make changes to your application server dynamically – without service disruption or configuration reloads – using the RESTful JSON API. This makes hands‑on management easier, and also makes it much easier to automate some or all of your management tasks.
In this blog post, we describe how to set up WordPress on a “LEMU” stack (Linux, NGINX Open Source, MySQL or MariaDB, and NGINX Unit) on a host running Ubuntu 16.04.
Editor – We also offer a bash
script that automates WordPress installation on Ubuntu with TLS certificates, NGINX Open Source for web serving, and NGINX Unit for application serving. For details, see Automating Installation of WordPress with NGINX Unit on Ubuntu.
The instructions are written from the bottom up. First, we describe the architecture. Then, we show how to install the database, application language, application server, and finally, the web server and load balancer. This way, you can validate every step of your installation, simplifying troubleshooting in case of errors.
Prerequisites
- A host running one of the distributions that NGINX Unit supports (we’re using Ubuntu 16.04 in this blog)
root
privilege, or equivalent access viasudo
Architecture Overview
WordPress is a fairly standard three‑tier web application. It includes PHP scripts that have to be executed by a PHP processor and static files that have to be delivered by a web server.
However, WordPress has two different URL schemes:
- Direct URLs. For users who request a PHP file directly (for example, with
GET
/wp-admin/admin.php
), an application server needs to open the required PHP file and process it. - User‑friendly URLs. Most WordPress admins prefer to have meaningful URLs such as /blog, /products/software, or /store instead of /index.php?p=123, /index.php?p=234, or /index.php?p=4567.
WordPress does not create user‑friendly files and folders in the filesystem. Instead, it expects a web server or an application server to send all requests for unknown files to /index.php.
With NGINX Open Source and NGINX Unit, the two URL schemes are configured as two separate applications, running at separate locations.
Installing MySQL
One of the key required components of a fresh WordPress installation is a database to store user accounts and site data. In this blog post, we’re using MySQL.
Note: For a detailed description of each action, see the MySQL documentation about secure installation.
-
Install and configure MySQL:
$ sudo apt-get install mysql-server
-
Enter a new MySQL root password when prompted:
-
Run the MySQL configuration tool and respond to the prompts:
$ sudo mysql_secure_installation 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: Using existing password for root. Change the password for root ? ((Press y|Y for Yes, any other key for No) : 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) : 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) : 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) : 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) : All done!
Creating a MySQL Database and a WordPress User
Now that MySQL is installed, we create a database to store the WordPress content and a user account that has permission to manage the database.
The values in orange
are examples we’re using in this blog; substitute the values appropriate to your deployment.
-
Log in to the MySQL root account:
$ sudo mysql -u root -p
-
Create a database for WordPress to use:
mysql> CREATE DATABASE wordpress;
-
Create a WordPress user and password:
mysql> CREATE USER user@localhost IDENTIFIED BY 'secure_password';
-
Grant privileges to the newly created user:
mysql> GRANT ALL PRIVILEGES ON wordpress.* TO user@localhost;
-
Make MySQL recognize the changes:
mysql> FLUSH PRIVILEGES;
-
Exit MySQL:
mysql> Exit Bye
Installing WordPress
In this blog, we’re preparing the WordPress installation in a temporary directory, and later moving the files to the document root.
-
Change directory to /var/www:
$ cd /var/www/
-
Download the latest version of WordPress and unpack the files:
$ sudo wget http://wordpress.org/latest.tar.gz $ sudo tar xzvf latest.tar.gz
Configuring and Securing WordPress
As a quick and simple way to configure WordPress, we start with a copy of the sample configuration file provided by WordPress and make a few modifications.
-
Create a copy of the sample configuration file, naming it wp-config.php:
$ cd /var/www/wordpress $ sudo cp wp-config-sample.php wp-config.php
-
To strengthen security, use the WordPress
salt
function to randomly generate new secret keys. Changing out the keys allows the administrator to force all users to log in again.The output is similar to the following; in Step 4 you’ll copy values from the output into wp-config.php.
$ curl -s https://api.wordpress.org/secret-key/1.1/salt/ define('AUTH_KEY', '3LJ|w/!Fit|/mo]>XLxWU+dG+7N+)64Y.KVVNoQ#X}1.s[os'); define('SECURE_AUTH_KEY', ')%C_q0{RNEe1+A{>C=#|y3c2} e J)AsxXq}z0H;x#$0J{o{'); define('LOGGED_IN_KEY', '@hD(g;-os^||uVI%6&`U1WI3YIz)F:7&Y%[jW]@DawoP{]A['); define('NONCE_KEY', 'fVv:A3(XNG`fXNi6Pmg#4,UnX)K|t8+jO{iv7g2Fay&kDJzV'); define('AUTH_SALT', ']s2/a`+2z~5+c6g)f-^h~D,@7C(eNr63x}Zz[)H]:!>=Q5(f'); define('SECURE_AUTH_SALT', 'Xw8x`IO.,@Y6l5)NK9)#!8V?s=&nzwXLRIwiBc,k];k3_%Fo'); define('LOGGED_IN_SALT', '3<.D-+I#Lr6+~We@1+~%LO=s9FHkgxV+w-l-S8g%BVC:dMD<'); define('NONCE_SALT', 'UVc/gj1Mjh*Tcl|9aq)YR|1!045=-2VpxNXrDNl>})9-Q>[x');
-
Using your preferred text editor, open wp-config.php. Here we’re using
nano
:$ sudo nano wp-config.php
-
Find the following lines in wp-config.php and replace the text in
orange
with the database name and password you defined in Steps 2 and 3 of Creating a MySQL Database and a WordPress User. For the username used in that section, substitutewpuser
or a similar value:// ** MySQL settings - You can get this info from your web host ** // /** The name of the database for WordPress */ define('DB_NAME', 'wordpress'); /** MySQL database username */ define('DB_USER', 'wpuser'); /** MySQL database password */ define('DB_PASSWORD', 'secure_password');
-
Find the following lines in wp-config.php and in each one copy in the value generated by
salt
in Step 2:define('AUTH_KEY', 'value generated by salt'); define('SECURE_AUTH_KEY', 'value generated by salt'); define('LOGGED_IN_KEY', 'value generated by salt'); define('NONCE_KEY', 'value generated by salt'); define('AUTH_SALT', 'value generated by salt'); define('SECURE_AUTH_SALT', 'value generated by salt'); define('LOGGED_IN_SALT', 'value generated by salt'); define('NONCE_SALT', 'value generated by salt');
-
Save and exit the file.
-
Set the following read‑write permissions for your user (substituting the appropriate name for
wpuser
):$ sudo chown -R wpuser:www-data /var/www/wordpress $ sudo find /var/www/wordpress -type d -exec chmod g+s {} \; $ sudo chmod g+w /var/www/wordpress/wp-content $ sudo chmod -R g+w /var/www/wordpress/wp-content/themes $ sudo chmod -R g+w /var/www/wordpress/wp-content/plugins
Installing PHP
We recommend that you install PHP and relevant extensions prior to installing NGINX Unit. For WordPress, you need several extensions that are not in NGINX Unit’s list of dependencies.
When you install a prebuilt NGINX Unit package as instructed in the next section, the package manager downloads the correct dependencies. For the OS version we’re using, Ubuntu 16.04, the dependencies are based on PHP 7.0, but substitute the correct values for your OS version. For example, for CentOS 7.0 the dependencies are for PHP 5.4; for Ubuntu 18.04, they’re for PHP 7.3.
With Ubuntu 16.04, install the PHP 7.0 extensions most commonly used with WordPress (adjust as necessary for your OS version):
$ sudo apt-get install -y php7.0 php7.0-common php7.0-mbstring php7.0-gd php7.0-intl php7.0-xml php7.0-mysql php7.0-mcrypt
Installing NGINX Unit
-
Install the precompiled NGINX Unit package for your operating system, following the instructions in the NGINX Unit documentation.
-
Install the additional NGINX Unit module for PHP:
$ sudo apt-get install unit-php
-
Run these commands to verify that NGINX Unit and PHP are functioning as expected:
$ sudo service unit restart $ sudo curl -X PUT --data-binary @/usr/share/doc/unit-php/examples/unit.config --unix-socket /run/control.unit.sock http://localhost/config $ curl http://localhost:8300/
If the phpinfo page appears, NGINX Unit was installed correctly. If not, see the NGINX Unit Troubleshooting Guide.
Configuring NGINX Unit
The following instructions create a file of JSON‑formatted configuration for WordPress and use the NGINX Unit API to load it into NGINX Unit. This instantaneously updates the initial NGINX Unit test configuration we loaded in Step 3 of the previous section.
-
Change directory to the location where you want store the file of WordPress configuration (we’re using /var/www/wordpress):
$ cd /var/www/wordpress
-
Using your preferred text editor, create a new file called wordpress.config. As before, we’re using
nano
:$ sudo nano wordpress.config
-
Copy in the following contents and save the file:
{ "listeners": { "127.0.0.1:8090": { "application": "script_index_php" }, "127.0.0.1:8091": { "application": "direct_php" } }, "applications": { "script_index_php": { "type": "php", "processes": { "max": 20, "spare": 5 }, "user": "www-data", "group": "www-data", "root": "/var/www/wordpress", "script": "index.php" }, "direct_php": { "type": "php", "processes": { "max": 5, "spare": 0 }, "user": "www-data", "group": "www-data", "root": "/var/www/wordpress", "index": "index.php" } } }
This configuration creates two NGINX Unit applications, one for each URL scheme – the web application and the administration panel.
Using the NGINX Unit
script
parameter instead of theindex
parameter means that requests for pages that are not found use the main index.php script in WordPress. For more information, see the NGINX Unit documentation for PHP application objects.To scale your application, change the IP addresses and ports accordingly.
-
Run this
curl
command to load the configuration:$ curl -X PUT --data-binary @/var/www/wordpress/wordpress.config --unix-socket /run/control.unit.sock http://localhost/config
Installing NGINX Open Source
We recommend installing NGINX Open Source as a prebuilt package from the mainline branch in our official repository. The packages available from other sources (operating system vendors, for example) are often several releases behind. You can also build NGINX Open Source from source.
For large‑scale and production WordPress deployments, NGINX Plus includes enhanced capabilities that improve site performance and make management easier. You can try it for free for 30 days. See Enhanced Capabilities in NGINX Plus for more information.
-
Start NGINX:
$ sudo service nginx start
-
In a browser, navigate to the IP address or hostname of the NGINX host. The appearance of this page confirms that NGINX is running.
You can also run the following command on the NGINX host and confirm that the raw HTML code for the page appears in the terminal.
$ curl localhost
If the
curl
command works, but browser access does not, check your routing configuration, firewalls, and any network settings that affect traffic between the NGINX host and your client machine.
Configuring NGINX Open Source
Now we configure NGINX Open Source to support both of the URL schemes described in Architecture Overview, by defining location
blocks that forward traffic to our two NGINX Unit application servers as appropriate.
-
Create a backup of the NGINX default configuration file:
$ cd /etc/nginx/conf.d/ $ sudo mv default.conf default.conf.bak
-
Using your preferred text editor, create a new default configuration file (again, we’re using
nano
):$ sudo nano default.conf
-
Copy the following contents into the file and save it:
upstream index_php_upstream { server 127.0.0.1:8090; # NGINX Unit backend address for index.php with # 'script' parameter } upstream direct_php_upstream { server 127.0.0.1:8091; # NGINX Unit backend address for generic PHP file handling } server { listen 80; server_name localhost; root /var/www/wordpress/; location / { try_files $uri @index_php; } location @index_php { proxy_pass http://index_php_upstream; proxy_set_header Host $host; } location /wp-admin { index index.php; } location ~* .php$ { try_files $uri =404; proxy_pass http://direct_php_upstream; proxy_set_header Host $host; } }
The first
location
block handles requests for the root URL (/). Thetry_files
directive searches for the exact URI. If it doesn’t exist, the request is sent to the second, named location @index_php, which proxies it to the index_php_upstream upstream group.The third
location
block handles requests for /wp-admin and serves the index.php file directly. This location does not need to handle URLs that aren’t found.The last
location
block handles requests for the .php extension, which match the regular expression. It proxies the requests to the index_php_upstream upstream group, where the generic NGINX Unit application handles all PHP requests directly. Thetry_files
directive handles404
errors by displaying a page generated directly by NGINX. Another option is to let WordPress handle404
errors, by replacing=404
with@index_php
.For security purposes, or for segregating traffic further, you can include additional
location
blocks and different parameters to thetry_files
directive. -
Verify that the main /etc/nginx/nginx.conf configuration file has an
include
directive that reads in files from the /etc/nginx/conf.d directory:include /etc/nginx/conf.d/*.conf;
-
Run this command to verify that the configuration is syntactically valid:
$ sudo nginx -t
-
Reload the configuration:
$ sudo nginx -s reload
-
In a web browser, navigate to the IP address or hostname of your WordPress site and complete the installation:
You’re all set! You now have WordPress up and running in a “LEMU” stack with NGINX and NGINX Unit.
Enhanced Capabilities in NGINX Plus
To get the most out of your WordPress application, we recommend NGINX Plus for its enhanced features, such as application health checks, sophisticated caching of both static and dynamic content, and live activity monitoring. These features are especially beneficial if you have multiple WordPress servers and require load balancing. And they work well with NGINX Unit; the image below shows the live activity monitoring dashboard on an NGINX Plus instance that is load balancing three NGINX Unit servers.
For details about all the great features in NGINX Plus, see the product page. To try it in your WordPress environment, start your free 30-day trial today or contact us.