RHEL 7 provides the Apache HTTP Server version 2.4 and PHP version 5.4.
The most common configuration for Apache httpd and PHP uses, but this has some limitations and drawbacks:
- a single PHP version of mod_php can be used
- mod_php run in the httpd process, without any isolation
- mod_phpis only supported for the prefork MPM
This article will explain how to configure Apache httpd to delegate PHP scripts execution to a backend using the FastCGI protocol, how to use a more recent PHP version, how to run multiple PHP versions, and how to improve Apache httpd performance.
The Apache httpd package available in RHEL provides all features needed to use such configuration.
1. Switching to php-fpm
1.1. Remove mod_php
It is recommended to remove or disable mod_php to reduce the memory footprint of each httpd process.
You can either remove the php package, which only provides this module:
yum remove php
or simply disable it by commenting out the LoadModule directive in /etc/httpd/conf.modules.d/10-php.conf.
# disabled # LoadModule php5_module modules/libphp5.so
1.2. Install php-fpm
You now can install the php-fpm and enable its service.
yum install php-fpm systemctl start php-fpm systemctl enable php-fpm
Notice: php-fpm package is available in the optional channel, which has to be enabled.
To configure PHP scripts execution, edit or create the /etc/httpd/conf.d/php.conf file:
The following lines prevent .user.ini files from being viewed by Web clients.
<Files ".user.ini"> Require all denied </Files>
Allow php to handle Multiviews:
AddType text/html .php
Add index.php to the list of files that will be served as directory indexes:
Following line, enable the http authorization headers:
SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1
Redirect the PHP scripts execution to the FPM backend.
<FilesMatch \.php$> SetHandler "proxy:fcgi://127.0.0.1:9000" </FilesMatch>
If you have some php_value directive in this file, you need to remove them, they are only for mod_php.
Now you can (re)start the web server and a simple PHP test page with;
it will show you are now running PHP through the FatCGI backend.
PHP Version 5.4.16 Server API= FPM/FastCGI
1.3. PHP tuning
The main FPM configuration file is /etc/php-fpm.conf, which have a lot of comments explaining each option.
FPM can run various pools, each one running PHP scripts with possible different options, the default pool (www) configuration file is /etc/php-fpm.d/www.conf, which also have lot of comments.
1.3.1. php_value, php-flag
PHP Options can be set using the php_value, php_admin_value, php_flag and php_admin_flag directives:
- with mod_php, in the Apache httpd configuration files.
- with FPM, in the pool configuration files.
Additional options can be set in a specific directory:
- with mod_php, using a .htaccess file.
- with FPM, using a .user.ini file (php_* keywords are not needed).
1.3.3. Process tuning
FPM run as a daemon and launch various processes to be able to handle various requests simultaneously, and offers various modes:
- pm = ondemand, child is only started when a connection is open and stopped when idle, suitable for development environment
- pm = dynamic, a set of idle processes is always running, more processes can be started if needed, suitable for production
- pm = static, a fixed set of processes is always running, suitable for production, can be better for performance
1.4. Apache HTTP Server tuning
1.4.1. Threaded MPM
By default, the Apache HTTP Server uses a set of processes to manage incoming requests (prefork MPM).
As we now don't use mod_php we can switch to a threaded MPM (worker of an event) so a set of threads will manage the requests, reducing the number of running processes and the memory footprint, and improving performance, especially when a lot of static files are served.
Switch the used MPM in the /etc/httpd/conf.modules.d/00-mpm.conf configuration files.
# disabled # LoadModule mpm_prefork_module modules/mod_mpm_prefork.so # disabled # LoadModule mpm_worker_module modules/mod_mpm_worker.so LoadModule mpm_event_module modules/mod_mpm_event.so
1.4.2. Unix Domain Socket
By default, FPM listens for incoming requests on a network socket but can use a Unix Domain Socket, which can slightly improve performance.
In FPM pool configuration:
listen = /run/php-fpm/www.sock listen.owner = apache listen.mode = 0660
In Apache httpd configuration:
1.4.2. Separate frontend and backend servers
By default, FPM listens for incoming requests on a local network socket. Of course, it can run on a separate server, another virtual machine or a container (docker instance)
In FPM pool configuration:
listen = 10.0.0.2:9000 listen.allowed_clients = 10.0.0.1
In Apache httpd configuration:
1.4.3 Multiple php backends
To be able to handle more simultaneous requests, we may want to balance the load between various PHP backends, which is easy.
Apache httpd configuration sample, with 3 backends:
# Load balancer creation <Proxy balancer://phpfpmlb> BalancerMember fcgi://10.0.0.2:9000 BalancerMember fcgi://10.0.0.3:9000 BalancerMember fcgi://10.0.0.4:9000 </Proxy> # Redirect PHP execution to the balancer <FilesMatch \.php$> SetHandler "proxy:balancer://phpfpmlb" </FilesMatch>
2. Running a recent PHP version
RHEL provides PHP version 5.4 which was the current version when RHEL-7 was released, but which can be too old for some recent projects.
In the example below, we'll use version 7.0, but it can be used for the others available versions.
Install the Software Collection, after having enabled the RHSCL channel:
yum install rh-php70
Install the FPM service for this version:
yum install rh-php70-php-fpm
Install any needed additional extensions:
yum install rh-php70-php-mbstring rh-php70-php-pgsql rh-php70-php-opcache
Tips: compare the list of available extensions to ensure everything needed is available.
php --modules | tee /tmp/54 scl enable rh-php70 'php --modules' | tee /tmp/70 diff /tmp/54 /tmp/70
Tips: never rely on the package name, but prefer extension name (e.g. php-mysqli or rh-php70-php-simplexml), as the package layout may change across versions.
2.2. Switch to newer PHP versions
When running FPM, this is as simple as stopping the old version service and starting the new one:
systemctl stop php-fpm systemctl start rh-php70-php-fpm
2.3. Additional packages
The Software Collections provide the same set of PHP extensions than standard packages in RHEL.
As users are used to finding some additional extensions, in the EPEL repository, an additional extension can be found in the community centos-sclo-sclo repository, for more information search sclo-php on https://www.softwarecollections.org/.
3. Running multiple versions of PHP
As PHP execution is redirected to the FastCGI service using the SetHandler directive, this can be set per vhost, project or directory.
In the example below, we will run both PHP version 5.4 from base system (for some legacy applications, already configured above) and PHP version 7.1 simultaneously.
Install the Software Collection, after having enabled the RHSCL beta channel:
yum install rh-php71 rh-php71-php-fpm rh-php71-php-mbstring rh-php71-php-opcache ...
Configure FPM to listen to a different port than the one used by the default php-fpm service, in /etc/opt/rh/rh-php71/php-fpm.d/www.conf.
listen = 127.0.0.1:9071
Ensure this port is not blocked by SELinux:
semanage port -a -t http_port_t -p tcp 9071
Start the service:
systemctl start rh-php71-php-fpm
Now, it is possible to choose PHP version for each directory, from Apache httpd configuration file.
# Use PHP 7.1 by default <FilesMatch \.php$> SetHandler "proxy:fcgi://127.0.0.1:9071" </FilesMatch> # Some legacy application use PHP 5.4 <Directory /var/www/html/old> <FilesMatch \.php$> SetHandler "proxy:fcgi://127.0.0.1:9000" </FilesMatch> </Directory>
I hope this small article has shown the various benefits of switching to FPM for your PHP scripts:
- process isolation between frontend (httpd) and backend (fpm)
- performance improvement
- running modern PHP version
- running multiple PHP versions
Last updated: October 18, 2018