In this article by Tim Butler, author of the book Nginx 1.9 Cookbook, we'll go through examples of the more common PHP scenarios and how to implement them with Nginx. PHP is a thoroughly tested product to use with Nginx because it is the most popular web-based programming language. It powers sites, such as Facebook, Wikipedia, and every WordPress-based site, and its popularity hasn't faded as other languages have grown.
(For more resources related to this topic, see here.)
As WordPress is the most popular of the PHP systems, I've put some additional information to help with troubleshooting. Even if you're not using WordPress, some of this information may be helpful if you run into issues with other PHP frameworks.
Most of the recipes expect that you have a working understanding of the PHP systems, so not all of the setup steps for the systems will be covered.
In order to keep the configurations as simple as possible, I haven't included details such as cache headers or SSL configurations in these recipes.
Covering nearly 30 percent of all websites, WordPress is certainly the Content Management System (CMS) of choice by many. Although it came from a blogging background, WordPress is a very powerful CMS for all content types and powers some of the world's busiest websites.
By combing it with Nginx, you can deploy a highly scalable web platform.
You can view the official WordPress documentation on Nginx at https://codex.wordpress.org/Nginx.
We'll also cover some of the more complex WordPress scenarios, including multisite configurations with subdomains and directories.
Let's get started.
To compile PHP code and run it via Nginx, the preferred method is via PHP-FPM, a high speed FastCGI Process Manager. We'll also need to install PHP itself and for the sake of simplicity, we'll stick with the OS supplied version. Those seeking the highest possible performance should ensure they're running PHP 7 (released December 3, 2015), which can offer a 2-3x speed improvement for WordPress.
To install PHP-FPM, you should run the following on a Debian/Ubuntu system:
sudo apt-get install php5-fpm
For those running CentOS/RHEL, you should run the following:
sudo yum install php-fpm
As PHP itself is a prerequisite for the php-fpm packages, it will also be installed.
Note: Other packages such as MySQL will be required if you're intending on running this on a single VPS instance. Consult the WordPress documentation for a full list of requirements.
At this instance, we're simply using a standalone WordPress site, which would be deployed in many personal and business scenarios. This is the typical deployment for WordPress.
For ease of management, I've created a dedicated config file just for the WordPress site (/etc/nginx.conf.d/generic-wordpress.conf):
server {
listen 80;
server_name wordpressdemo.nginxcookbook.com;
access_log /var/log/nginx/access.log combined;
location / {
root /var/www/html;
try_files $uri $uri/ /index.php?$args;
}
location ~ .php$ {
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME
$document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Restart Nginx to pickup the new configuration file and then check your log files if there are any errors. If you're installing WordPress from scratch, you should see the following:
You can complete the WordPress installation if you haven't already.
For the root URL call, we have a new try_files directive, which will attempt to load the files in the order specified, but will fallback to the last parameter if they all fail.
For this WordPress example, it means that any static files will be served if they exist on the system, then fallback to /index.php?args if this fails. This can also be very handy for automatic maintenance pages too.
The args rewrite allows the permalinks of the site to be in a much more human form. For example, if you have a working WordPress installation, you can see links such as the one shown in the following image:
Lastly, we process all PHP files via the FastCGI interface to PHP-FPM. In the preceding example, we're referencing the Ubuntu/Debian standard; if you're running CentOS/RHEL, then the path will be /var/run/php-fpm.sock.
Nginx is simply proxying the connection to the PHP-FPM instance, rather than being part of Nginx itself. This separation allows for greater resource control, especially since the number of incoming requests to the webserver don't necessarily match the number of PHP requests for a typical website.
Take care when copying and pasting any configuration files. It's very easy to miss something and have one thing slightly different in your environment, which will cause issues with the website working as expected. Here's a quick lookup table of various other issues which you may come across:
Error |
What to check |
502 Bad Gateway |
File ownership permissions for the PHP-FPM socket file |
404 File Not Found |
Check for the missing index index.php directive |
403 Forbidden |
Check for the correct path in the root directive |
Your error log (defaults to /var/log/nginx/error.log) will generally contain a lot more detail in regard to the issue you're seeing compared with what's displayed in the browser. Make sure you check the error log if you receive any errors.
Hint: Nginx does not support .htaccess files. If you see examples on the web referencing a .htaccess files, these are Apache specific. Make sure any configurations you're looking at are for Nginx.
WordPress multisites (also referred to as network sites) allow you to run multiple websites from the one codebase. This can reduce the management burden of having separate WordPress installs when you have similar sites. For example, if you have a sporting site with separate news and staff for different regions, you can use a Multisite install to accomplish this.
To convert a WordPress site into a multisite, you need to add the configuration variable into your config file:
define( 'WP_ALLOW_MULTISITE', true );
Under the Tools menu, you'll now see an extra menu called Network Setup. This will present you with two main options, Sub-domains and Sub-directories. This is the two different ways the multisite installation will work. The Sub-domains option have the sites separated by domain names, for example, site1.nginxcookbook.com and site2.nginxcookbook.com. The Sub-directories option mean that the sites are separated by directories, for example, www.nginxcookbook.com/site1 and www.nginxcookbook.com/site2.
There's no functional difference between the two, it's simply an aesthetic choice. However, once you've made your choice, you cannot return to the previous state.
Once you've made the choice, it will then provide the additional code to add to your wp-config.php file.
Here's the code for my example instance, which is subdirectory based:
define('MULTISITE', true);
define('SUBDOMAIN_INSTALL', false);
define('DOMAIN_CURRENT_SITE', 'wordpress.nginxcookbook.com');
define('PATH_CURRENT_SITE', '/');
define('SITE_ID_CURRENT_SITE', 1);
define('BLOG_ID_CURRENT_SITE', 1);
Because Nginx doesn't support .htaccess files, the second part of the WordPress instructions will not work. Instead, we need to modify the Nginx configuration to provide the rewrite rules ourselves.
In the existing /etc/nginx/conf.d/wordpress.conf file, you'll need to add the following just after the location / directive:
if (!-e $request_filename) {
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
rewrite ^(/[^/]+)?(/wp-.*) $2 last;
rewrite ^(/[^/]+)?(/.*.php) $2 last;
}
Although the if statements are normally avoided if possible, at this instance, it will ensure the subdirectory multisite configuration works as expected. If you're expecting a few thousand concurrent users on your site, then it may be worthwhile investigating the static mapping of each site. There are plugins to assist with the map generations for this, but they are still more complex compared to the if statement.
If you've selected subdomains, your code to put in wp-config.php will look like this:
define('MULTISITE', true);
define('SUBDOMAIN_INSTALL', true);
define('DOMAIN_CURRENT_SITE', 'wordpressdemo.nginxcookbook.com');
define('PATH_CURRENT_SITE', '/');
define('SITE_ID_CURRENT_SITE', 1);
define('BLOG_ID_CURRENT_SITE', 1);
You'll also need to modify the Nginx config as well to add the wildcard in for the server name:
server_name *.wordpressdemo.nginxcookbook.com wordpressdemo.nginxcookbook.com;
You can now add in the additional sites such as site1.wordpressdemo.nginxcookbook.com and there won't be any changes required for Nginx.
With version 8 recently released and a community of over 1 million supporters, Drupal remains a popular choice when it comes to a highly flexible and functional CMS platform. Version 8 has over 200 new features compared to version 7, aimed at improving both the usability and manageability of the system. This cookbook will be using version 8.0.5.
This example assumes you already have a working instance of Drupal or are familiar with the installation process. You can also follow the installation guide available at https://www.drupal.org/documentation/install.
This recipe is for a basic Drupal configuration, with the Drupal files located in /var/www/vhosts/drupal.
Here's the configuration to use:
server {
listen 80;
server_name drupal.nginxcookbook.com;
access_log /var/log/nginx/drupal.access.log combined;
index index.php;
root /var/www/vhosts/drupal/;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ (^|/). {
return 403;
}
location ~ /vendor/.*.php$ {
deny all;
return 404;
}
location ~ .php$|^/update.php {
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_split_path_info ^(.+?.php)(|/.*)$;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME
$document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Based on a simple PHP-FPM structure, we make a few key changes specific for the Drupal environment. The first change is as follows:
location ~ (^|/). {
return 403;
}
We put a block in for any files beginning with a dot, which are normally hidden and/or system files. This is to prevent accidental information leakage:
location ~ /vendor/.*.php$ {
deny all;
return 404;
}
Any PHP file within the vendor directory is also blocked, as they shouldn't be called directly. Blocking the PHP files limits any potential exploit opportunity which could be discovered in third-party code.
Lastly, Drupal 8 changed the way the PHP functions are called for updates, which causes any old configuration to break. The location directive for the PHP files looks like this:
location ~ .php$|^/update.php {
This is to allow the distinct pattern that Drupal uses, where the PHP filename could be midway through the URI.
We also modify how the FastCGI process splits the string, so that we ensure we always get the correct answer:
fastcgi_split_path_info ^(.+?.php)(|/.*)$;
Nginx Recipe: https://www.nginx.com/resources/wiki/start/topics/recipes/drupal/
MediaWiki, most recognized by its use with Wikipedia, is the most popular open source wiki platform available. With features heavily focused on the ease of editing and sharing content, MediaWiki makes a great system to store information you want to continually edit:
This example assumes you already have a working instance of MediaWiki or are familiar with the installation process. For those unfamiliar with the process, it's available online at https://www.mediawiki.org/wiki/Manual:Installation_guide.
The basic Nginx configuration for MediaWiki is very similar to many other PHP platforms. It has a flat directory structure which easily runs with basic system resources.
Here's the configuration:
server {
listen 80;
server_name mediawiki.nginxcookbook.com;
access_log /var/log/nginx/mediawiki.access.log combined;
index index.php;
root /var/www/vhosts/mediawiki/;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ .php$ {
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME
$document_root$fastcgi_script_name;
include fastcgi_params;
}
}
The default installation doesn't use any rewrite rules, which means you'll get URLs such as index.php?title=Main_Page instead of the neater (and more readable) /wiki/Main_Page. To enable this, we need to edit the LocalSettings.php file and add the following lines:
$wgArticlePath = "/wiki/$1";
$wgUsePathInfo = TRUE;
This allows the URLs to be rewritten in a much neater format.
NGINX Recipe: https://www.nginx.com/resources/wiki/start/topics/recipes/mediawiki/
In this article we learned common PHP scenarios and how to configure them with Nginx. The first recipe talks about how to configure Nginx for WordPress. Then we learned how to set up a WordPress multisite. In third recipe we discussed how to configure and run Drupal using Nginx. In the last recipe we learned how to configure Nginx for MediaWiki.
Further resources on this subject: