Use PHP-FPM Pools to Secure Multiple Web Sites
Introduction
As server hardware gets faster and more efficient, it's no longer uncommon to host multiple websites on Nginx web server. For better performance, process isolation, and security, consider using separate PHP FastCGI Process Manager (PHP-FPM) pools for each website. This article describes the basics of using separate PHP-FPM pools for multiple websites on Nginx with PHP 7.4, using a Vultr Ubuntu 20.04 LTS cloud server instance. This allows you to host multiple virtual domains using Nginx server blocks.
Prerequisites
- Add a non-root sudo user.
- Follow the best practices to update Ubuntu.
- Install a LEMP stack on Ubuntu 20.04 LTS.
- Create two DNS "A" records pointing to the same IP address of your LEMP server, with different names. This example uses
site1.example.com
andsite2.example.com
, which point to the same IP address for the server.
1. Remove the Default Site
Nginx installs a default site which is redundant for this article. To remove the default site, SSH to the server as a non-root sudo user and run:
$ sudo rm /etc/nginx/sites-enabled/default
2. Create the Site Users
Each site needs to run as a different user for security purposes and isolation. To do that, create two user accounts and assign the www-data user to their respective groups. This allows the web server to interact with the users and vice-versa. Do not give the site1
and site2
users login privileges or associate any other information to the accounts. To create the users, run:
$ sudo useradd site1
$ sudo useradd site2
$ usermod -a -G site1 www-data
$ usermod -a -G site2 www-data
3. Assign Directory Permissions
Create two directories and lock down the permissions to prepare the server for the two separate sites.
$ sudo mkdir /var/www/site1
$ sudo chown -R site1:site1 /var/www/site1
$ sudo mkdir /var/www/site2
$ sudo chown -R site2:site2 /var/www/site2
$ sudo chmod 770 /var/www/site2
The Unix permissions are 770. Each user and the user's associated group has full permissions (7) on the directory, but the world has no (0) permission. This setting restricts the site1 user from seeing data for site2 and vice-versa.
4. Create New PHP-FPM Pools
Copy the default PHP-FPM pool as a template for the two new user pools:
$ sudo cp /etc/php/7.4/fpm/pool.d/www.conf /etc/php/7.4/fpm/pool.d/fpm-site1.conf
$ sudo cp /etc/php/7.4/fpm/pool.d/www.conf /etc/php/7.4/fpm/pool.d/fpm-site2.conf
Delete the unneeded default pool.
$ sudo rm /etc/php/7.4/fpm/pool.d/www.conf
5. Configure the First Pool
Each pool has an associated user and Unix socket. Edit the first configuration file:
$ sudo nano /etc/php/7.4/fpm/pool.d/fpm-site1.conf
Change four lines:
- Change the top line inside the brackets that sets the pool name from
[www]
to[site1]
. - Change the line
user = www-data
touser = site1
. - Change the line
group = www-data
togroup = site1
. - Change the line
listen = /var/run/php/php7.4-fpm.sock
tolisten = /var/run/php/php7.4-site1-fpm.sock
.
Save the file and exit.
6. Configure the Second Pool
Edit the second configuration file:
$ sudo nano /etc/php/7.4/fpm/pool.d/fpm-site2.conf
Change four lines:
- Change the top line inside the brackets that sets the pool name from
[www]
to[site2]
. - Change the line
user = www-data
touser = site2
. - Change the line
group = www-data
togroup = site2
. - Change the line
listen = /var/run/php/php7.4-fpm.sock
tolisten = /var/run/php/php7.4-site2-fpm.sock
.
Save the file and exit.
7. Restart PHP-FPM
Restart the PHP-FPM daemon.
$ sudo service php7.4-fpm restart
The daemon should restart without errors. Verify two separate PHP-FPM pools are running.
$ sudo service php7.4-fpm status
If the FPM service pools are correct they are visibly forked in the process list:
CGroup: /system.slice/php7.4-fpm.service
├─70796 php-fpm: master process (/etc/php/7.4/fpm/php-fpm.conf)
├─70807 php-fpm: pool site1
├─70808 php-fpm: pool site1
├─70809 php-fpm: pool site2
└─70810 php-fpm: pool site2
The actual process IDs may differ from the ones listed above.
8. Create the Nginx Sites
The server needs two new sites to use the two PHP-FPM pools, one associated with each respective pool.
Create a configuration file for site1:
$ sudo nano /etc/nginx/sites-available/site1
Paste the following into the file:
server { server_name site1.example.com; access_log /var/log/nginx/site1.access.log; error_log /var/log/nginx/site1.error.log; root /var/www/site1; index index.php; try_files $uri $uri/ /index.php?$query_string; location ~ \.php$ { fastcgi_pass unix:/var/run/php/php7.4-fpm-site1.sock; include snippets/fastcgi-php.conf; } }
Save and exit the file. The important changes above are:
server_name
- the fully qualified DNS name of the hostaccess_log
- the location and name of the access logerror_log
- the location and name of the error logroot
- the location on the files on disk that the web server usesfastcgi_pass
- the location of the PHP-FPM sock created by the pool
Make a similar file for site2.
$ sudo nano /etc/nginx/sites-available/site2
The contents of this file matches site1, except it has the information for site2:
server { server_name site2.example.com; access_log /var/log/nginx/site2.access.log; error_log /var/log/nginx/site2.error.log; root /var/www/site2; index index.php; try_files $uri $uri/ /index.php?$query_string; location ~ \.php$ { fastcgi_pass unix:/var/run/php/php7.4-fpm-site2.sock; include snippets/fastcgi-php.conf; } }
Save and exit the file.
Link the files to the respective Nginx directories.
$ sudo ln -s /etc/nginx/sites-available/site1 /etc/nginx/sites-enabled/site1 $ sudo ln -s /etc/nginx/sites-available/site2 /etc/nginx/sites-enabled/site2
Restart Nginx.
$ sudo service nginx restart
This should start without error.
9. Test the Configuration
Add a test file in the root of each site to test the configuration and verify which user serves each web site.
Create an index file for site1.
$ nano /var/www/site1/index.php
Paste the following into the file:
<?php phpinfo();
Save and exit the file.
Create an index file for site2.
$ nano /var/www/site2/index.php
Paste the following into the file for site2:
<?php phpinfo();
Save and exit the file.
Open a browser and visit http://site1.example.com and http://site2.example.com.
Examine the PHP Information page for each site. In the PHP Variables section, the variable $_SERVER['USER']
should be site1 for site1.example.com, and site2 for site2.example.com.
Summary
Creating separate PHP-FPM pools for each website served on a single server gives the administrator stronger security, more defined boundaries, and makes it easier to troubleshoot problems associated with individual sites. It's also possible to individually tune site settings for performance. These settings are in the /etc/php/7.4/fpm/pool.d
configuration files. The main setting that is often changed is the pm setting, which controls process creation, with settings like dynamic, static or ondemand. See the FastCGI Process Manager Configuration for more information.