Secure Multiple Web Sites with PHP-FPM Pools on CentOS 7 with Apache

Updated on November 2, 2021
Secure Multiple Web Sites with PHP-FPM Pools on CentOS 7 with Apache header image

Introduction

PHP-FPM is a robust FastCGI Process Manager for PHP that has some advanced features useful for heavy-loaded sites. PHP-FPM allows grouping processes into pools, each running under a separate user/group with its own settings. This guide explains how to use PHP-FPM pools to secure multiple websites with Apache on a single CentOS 7 server.

For simplicity, this guide assumes you want to set up two websites. But, you can set up as many websites as you want as long as your server has enough resources.

Prerequisites

1. Install Apache

The version of Apache in the official CentOS repositories is 2.4.6, so it is pretty old. Instead, you will add the IUS repository to get a recent version of Apache with improvements and bug fixes.

  1. Add the IUS repository.

     $ sudo yum -y install https://repo.ius.io/ius-release-el7.rpm

    The above command also adds the Extra Packages for Enterprise Linux (EPEL) repository because the IUS repository depends on it.

  2. Install the main Apache package.

     $ sudo yum -y install httpd24u
  3. Enable the Apache service so that it starts at boot time.

     $ sudo systemctl enable httpd.service
  4. For high performance and memory saving, switch the Multi-Processing Module (MPM) from prefork to event.

    1. Edit the MPM configuration file.

       $ sudo nano /etc/httpd/conf.modules.d/00-mpm.conf
    2. Find the LoadModule mpm_prefork_module modules/mod_mpm_prefork.so line and put the # character at the beginning of it.

    3. Find the LoadModule mpm_event_module modules/mod_mpm_event.so line and remove the # character at the beginning of it.

  5. Save the configuration file and exit.

  6. Start the Apache service.

     $ sudo systemctl start httpd.service

2. Install PHP

The official CentOS 7 repositories only offer PHP 5.4, which has reached its End Of Life in September 2015. Therefore, you will add the Remi repository, a long-time and community-trusted repository that offers PHP from version 5.4 to the latest version.

You should install only supported PHP versions. This guide uses PHP 7.4, the most widely-used version at the time of writing. Make sure to replace the 74 string in the code examples with the string that matches the PHP version of your choice, for example, 73 or 80.

  1. Add the Remi repository.

     $ sudo yum -y install https://rpms.remirepo.net/enterprise/remi-release-7.rpm
  2. List all available PHP 7.4 packages in the Remi repository.

     $ yum --disablerepo="*" --enablerepo="remi" list available | grep '^php74\S\+'

    The result looks like this.

     php74.x86_64                          1.0-3.el7.remi            remi
     php74-build.x86_64                    1.0-3.el7.remi            remi
     php74-php.x86_64                      7.4.24-1.el7.remi         remi
     php74-php-ast.x86_64                  1.0.14-1.el7.remi         remi
     php74-php-bcmath.x86_64               7.4.24-1.el7.remi         remi
     php74-php-brotli.x86_64               0.13.1-1.el7.remi         remi
     php74-php-channel-horde.noarch        1.0-2.el7.remi            remi
     php74-php-cli.x86_64                  7.4.24-1.el7.remi         remi
     ...

    * The first column contains package names. Although the names include the .x86_64 suffix, you can omit it when installing.

    • The second column contains package versions.
    • The last column contains the repository ids.
  3. Install the PHP interpreter and the PHP-FPM extension.

     $ sudo yum -y install php74-php-cli php74-php-fpm
  4. Install other PHP extensions (from the above list) needed to run your two websites. For example:

     $ sudo yum -y install php74-php-gd php74-php-intl php74-php-mbstring php74-php-mysqlnd php74-php-opcache php74-php-pecl-redis5 php74-php-tidy php74-php-xml
  5. Enable the PHP-FPM service so that it starts at boot time.

     $ sudo systemctl enable php74-php-fpm.service
  6. Start the service.

     $ sudo systemctl start php74-php-fpm.service
  7. Set PHP 7.4 as the default PHP version on the system.

     $ sudo ln -sf /opt/remi/php74/root/usr/bin/* /usr/bin/
  8. Confirm the default version is 7.4.

     $ php -v

3. Configure PHP

Customize Common Settings For Both Websites

  1. List all the time zones that your CentOS system supports. Use the Up / Down / Pgup / Pgdn keys to move through the list, and press Q to exit.

     $ timedatectl list-timezones
  2. Copy an appropriate time zone from the list, for example, America/New_York. Then update the operating system with that time zone.

     $ sudo timedatectl set-timezone America/New_York
  3. Edit the main PHP configuration file to tell PHP to use the new time zone.

     $ sudo nano /etc/opt/remi/php74/php.ini
  4. Find the line ;date.timezone =, then remove the ; character and add your time zone. For example:

     date.timezone = America/New_York
  5. Here are the common settings that you can customize if needed:

     max_execution_time
     memory_limit
     post_max_size
     upload_max_filesize
  6. Save the configuration file and exit.

Configure the First Pool for the First Website

For security, create a dedicated user and a dedicated PHP-FPM pool for each website. Each user owns all website source code files and other PHP-FPM-related files, such as log and session files. Therefore, the two websites are completely independent of each other.

  1. Create a dedicated user named site1.

     $ sudo adduser site1
  2. Set a strong password for the user. You may need it when transferring the source code of your website.

     $ sudo passwd site1
  3. Rename the default configuration file to disable it and keep it as a template.

     $ sudo mv /etc/opt/remi/php74/php-fpm.d/www.conf /etc/opt/remi/php74/php-fpm.d/www.conf.default
  4. Create a configuration file for the first pool from the template.

     $ sudo cp /etc/opt/remi/php74/php-fpm.d/www.conf.default /etc/opt/remi/php74/php-fpm.d/site1.conf
  5. Edit the file.

     $ sudo nano /etc/opt/remi/php74/php-fpm.d/site1.conf

    In the file, any line starting with ; is a comment.

  6. Search for the following settings, then:

    • Replace [www] with [site1]
    • Replace user = apache with user = site1
    • Replace group = apache with group = site1
    • Replace slowlog = /var/opt/remi/php74/log/php-fpm/www-slow.log with slowlog = /var/opt/remi/php74/log/php-fpm/site1/slow.log
    • Remove the ; character at the beginning of ;catch_workers_output = yes and ;php_flag[display_errors] = off.
    • Replace php_admin_value[error_log] = /var/opt/remi/php74/log/php-fpm/www-error.log with php_admin_value[error_log] = /var/opt/remi/php74/log/php-fpm/site1/error.log
    • Replace php_value[session.save_path] = /var/opt/remi/php74/lib/php/session with php_value[session.save_path] = /var/opt/remi/php74/lib/php/session/site1
    • Replace php_value[soap.wsdl_cache_dir] = /var/opt/remi/php74/lib/php/wsdlcache with php_value[soap.wsdl_cache_dir] = /var/opt/remi/php74/lib/php/wsdlcache/site1
    • Make sure the listen = 127.0.0.1:9000 setting does not start with ; because Apache will forward FastCGI requests to the first pool using the TCP socket address 127.0.0.1:9000.

    Those settings make PHP-FPM log error messages to the /var/opt/remi/php74/log/php-fpm/site1/error.log file instead of displaying them to website users, store session data in the /var/opt/remi/php74/lib/php/session/site1 directory, and store WSDL cache in the /var/opt/remi/php74/lib/php/wsdlcache/site1 directory.

  7. Save the configuration file and exit.

  8. Create three directories to store PHP logs, session data, and WSDL cache.

     $ sudo mkdir -p /var/opt/remi/php74/log/php-fpm/site1
     $ sudo mkdir -p /var/opt/remi/php74/lib/php/session/site1
     $ sudo mkdir -p /var/opt/remi/php74/lib/php/wsdlcache/site1
  9. Update the ownership and permissions of the directories so that only the first pool's processes can access them.

     $ sudo chown site1:site1 /var/opt/remi/php74/log/php-fpm/site1
     $ sudo chmod 700 /var/opt/remi/php74/log/php-fpm/site1
     $ sudo chown site1:site1 /var/opt/remi/php74/lib/php/session/site1
     $ sudo chmod 700 /var/opt/remi/php74/lib/php/session/site1
     $ sudo chown site1:site1 /var/opt/remi/php74/lib/php/wsdlcache/site1
     $ sudo chmod 700 /var/opt/remi/php74/lib/php/wsdlcache/site1

Configure the Second Pool for the Second Website

The configuration of the second pool is similar to that of the first pool except for some minor changes.

  1. Create a dedicated user named site2.

     $ sudo adduser site2
  2. Set a strong password for the user.

     $ sudo passwd site2
  3. Copy the configuration of the first pool.

     $ sudo cp /etc/opt/remi/php74/php-fpm.d/site1.conf /etc/opt/remi/php74/php-fpm.d/site2.conf
  4. Update the new file by replacing all occurrences of site1 with site2.

     $ sudo  sed -i 's|site1|site2|g' /etc/opt/remi/php74/php-fpm.d/site2.conf
  5. Change the TCP socket address of the second pool to 127.0.0.1:9001.

     $ sudo  sed -i 's|127.0.0.1:9000|127.0.0.1:9001|g' /etc/opt/remi/php74/php-fpm.d/site2.conf
  6. Create three directories to store PHP logs, session data, and WSDL cache.

     $ sudo mkdir -p /var/opt/remi/php74/log/php-fpm/site2
     $ sudo mkdir -p /var/opt/remi/php74/lib/php/session/site2
     $ sudo mkdir -p /var/opt/remi/php74/lib/php/wsdlcache/site2
  7. Update the ownership and permissions of the directories so that only the second pool's processes can access them.

     $ sudo chown site2:site2 /var/opt/remi/php74/log/php-fpm/site2
     $ sudo chmod 700 /var/opt/remi/php74/log/php-fpm/site2
     $ sudo chown site2:site2 /var/opt/remi/php74/lib/php/session/site2
     $ sudo chmod 700 /var/opt/remi/php74/lib/php/session/site2
     $ sudo chown site2:site2 /var/opt/remi/php74/lib/php/wsdlcache/site2
     $ sudo chmod 700 /var/opt/remi/php74/lib/php/wsdlcache/site2
  8. Restart the PHP-FPM service for the changes to take effect.

     $ sudo systemctl restart php74-php-fpm.service

4. Prepare Source Code

The First Website

  1. Create a new document root directory named /var/www/site1 to store the source code.

     $ sudo mkdir /var/www/site1
  2. Make site1 the owner of the directory.

     $ sudo chown site1:site1 /var/www/site1
  3. Use your file transfer tool, such as rsync or FileZilla, to transfer the source code of the first website to the /var/www/site1 directory.

  4. Create a PHP file that displays the PHP information to confirm that the PHP-FPM pool running this website is the first pool.

     $ echo '<?php phpinfo();' | sudo tee /var/www/site1/info.php > /dev/null
  5. Make sure site1 is the owner of all the source code files.

     $ sudo chown -R site1:site1 /var/www/site1

The Second Website

  1. Create a new document root directory named /var/www/site2 to store the source code.

     $ sudo mkdir /var/www/site2
  2. Make site2 the owner of the directory.

     $ sudo chown site2:site2 /var/www/site2
  3. Transfer the source code of the second website to the /var/www/site2 directory.

  4. Create a PHP file to confirm the PHP-FPM pool running this website is the second pool.

     $ echo '<?php phpinfo();' | sudo tee /var/www/site2/info.php > /dev/null
  5. Make sure site2 is the owner of all the source code files.

     $ sudo chown -R site2:site2 /var/www/site2

5. Configure Apache

Configure the First Website

  1. Create a configuration file for the first website.

     $ sudo nano /etc/httpd/conf.d/site1.conf
  2. Paste the following into the file:

     <VirtualHost *:80>
         ServerName site1.example.com
    
         # For Apache 2.4.9 or higher
         # Using SetHandler avoids issues with using ProxyPassMatch in combination
         # with mod_rewrite or mod_autoindex
         <FilesMatch \.php$>
             SetHandler proxy:fcgi://127.0.0.1:9000
    
             # for Unix sockets, Apache 2.4.10 or higher
             # SetHandler proxy:unix:/path/to/fpm.sock|fcgi://dummy
         </FilesMatch>
    
         DocumentRoot /var/www/site1
         DirectoryIndex index.php index.html
    
         <Directory /var/www/site1>
             # enable the .htaccess rewrites
             AllowOverride All
    
             # disable autoindex
             Options -Indexes
    
             Require all granted
         </Directory>
    
         ErrorLog /var/log/httpd/site1_error.log
         CustomLog /var/log/httpd/site1_access.log combined
     </VirtualHost>

    The above configuration contains the most basic directives for running PHP scripts with PHP-FPM. You may add more directives to fit your website requirements.

  3. Save the configuration file and exit.

Configure the Second Website

The configuration of the second website is similar to that of the first website except for some minor changes.

  1. Copy the configuration of the first website.

     $ sudo cp /etc/httpd/conf.d/site1.conf /etc/httpd/conf.d/site2.conf
  2. Update the new file by replacing all occurrences of site1 with site2.

     $ sudo  sed -i 's|site1|site2|g' /etc/httpd/conf.d/site2.conf
  3. Change the TCP socket address of the second pool to 127.0.0.1:9001.

     $ sudo  sed -i 's|127.0.0.1:9000|127.0.0.1:9001|g' /etc/httpd/conf.d/site2.conf
  4. Save the configuration file and exit.

Apply the New Configuration

  1. Add the apache user to the site1 and site2 groups so that Apache processes can access the source code of the two websites.

     $ sudo usermod -aG site1,site2 apache
  2. Check the new configuration. Make sure you see Syntax OK in the output.

     $ sudo apachectl configtest
  3. Restart the Apache service for the changes to take effect.

     $ sudo systemctl restart httpd.service

6. Verify the Setup

  1. Restart the server.

     $ sudo reboot
  2. Wait a moment for the operating system to boot, then open the following URLs in your browser.

     http://site1.example.com/info.php
     http://site2.example.com/info.php
  3. Checking the Environment section on each website, you will see that the USER variable has a value of site1 for the first website and a value of site2 for the second.

  4. For security, log in to the server as a non-root sudo user via SSH again, then delete the info.php files.

     $ sudo rm /var/www/site1/info.php
     $ sudo rm /var/www/site2/info.php

You can set up as many websites as you want as long as your server has enough resources.

More Information